Написать данную статью меня побудили следующие обстоятельства:

  • Обновление ОС на своём роутере до OpenWrt 23.05, сломавшее мой предыдущий setup, где я делал роутинг по GeoIP.

  • Многочисленные вопросы знакомых и в дискуссиях в постах на Хабре

  • Статья на Хабре, по которой я стал делать и понял, что так делать не надо.

Постановка задачи

И так, у вас есть настроенный на роутере VPN. Это может быть VPN во внешний мир из РФ, а может быть и VPN из внешнего мира в РФ для использования российских сервисов, отгородившихся от внешнего мира (например, ГИС ЖКХ). И вот у вас теперь задача: на одни сайты заходить через VPN, а на другие через маршрутизацию на шлюз вашего провайдера, так как в эпоху сегментации Интернета парадигма "всё в VPN" не работает. При этом вы можете захотеть не только делить трафик на российский и зарубежный, но и добавлять исключения в эти правила. Например, сайт drive2.ru требует привязывать номер телефона для российских пользователей, пользующихся перепиской в личных сообщениях, что он определяет по IP: добавляя такие исключения, можно избавиться от лишнего сбора данных о себе.

Какие могут быть пути решения? В своё время автор настроил маршрутизацию по GeoIP, однако это решение имело массу недостатков: сложность обновления баз, сложность управления исключениями из правил и в целом достаточно сложный setup, который нельзя было целиком провернуть из графического интерфейса OpenWrt (luci). Можно это делать, наверное, ещё как-то, но не цель статьи сделать подробный обзор таких методов.

Сначала в этой статье мы рассмотрим то, как это настроить, во второй половине разберёмся с тем, как это работает под капотом.

Требования

Итак, речь в данной статье применима для маршрутизаторов, на которых стоит OpenWrt 23.05. Это довольно сильное требование, так механизмы, через которое всё это осуществляется, сильно эволюционировали в последние два релиза (21-я версия была ещё на iptables, 22-я получилась каким-то переходным этапом между iptables и nftables, а 23-я наконец мигрировала на nftables полностью). Поэтому описанное совершенно точно не будет работать на более старых версиях.

От читателя также требуются базовые умения работать с OpenWrt (установка пакетов, настройка ssh-доступа и т.п.). Кроме того требуется, чтобы читатель уже разобрался с настройкой VPN на своём роутере и настроил его. Выбору и настройке VPN на маршрутизаторе вообще и на OpenWrt в частности посвящено множество статей, в том числе и на Хабре.

Возможно, что в каком-то виде этот гайд подойдёт для юзеров Keenetic'ов, потому что KeeneticOS это в сущности OpenWrt, но так как у меня нет под рукой гаджета, то не могу проверить. Кстати, если у вас получится, отметьтесь в комментариях, пожалуйста.

Настройка

Для начала нам потребуется установить пакеты dnsmasq-full и luci-app-pbr. Установка dnsmasq-full сопряжено с тем, что перед этим требуется удалить dnsmasq (иначе будет конфликт). Если удалять dnsmasq через web-интерфейс, то есть риск оставить роутер без работающего DNS, что сделает невозможным установку dnsmasq-full. Поэтому такую замену пакета рекомендуется делать через ssh в соответствии с данным рецептом. У автора прокатило сделать всё через web-интерфейс, но так делать можно только на свой страх и риск. После установки dnsmasq-full и luci-app-pbr роутер стоит перезагрузить, и зайти по новой в web-интерфейс. Появится новое меню Services, а если оно у вас уже было, то появится новый пункт меню Policy Routing:

Собственно, выбираем Policy Routing и попадаем в раздел настройки маршрутизации по политикам. Первое, что необходимо сделать - это на вкладке Basic Configuration настроить опцию "Use resolver set support for domains" в значение "Dnsmasq nft set":

После этого настраиваем собственно сами политики, прописывая в remote addresses непосредственно доменные имена или их суффиксы:

В политиках нельзя указывать регулярные выражения или wildcard'ы. Но соответствие находится по суффиксу. То есть первая политика на домен "ru" будет подходить для всех доменов, оканчивающихся на ".ru" (точку в правила не нужно включать!!!). При этом будет применяться там политика, у которой самое длинное совпадение по доменному имени. Поэтому в указанном примере, домены "*.drive2.ru" будут переопределять первое правило для всех доменов "ru". Обратите внимание на интерфейсы - автор настраивает роутинг для маршрутизатора, находящегося за границей РФ. Поэтому российский трафик заворачивается в VPN (интерфейс wg0 - это WireGuard туннель в РФ), а исключения идут через wan-интерфейс.

ВАЖНО: если ваш туннель настроен так, что адрес VPN-сервера указан в виде доменного имени, а не IP-адреса, и это имя попадает в правило, заворачивающее трафик в туннель, то нужно добавить доменное имя вашего VPN-сервера в исключения. Иначе есть риск, что ядро будет пытаться завернуть трафик туннеля в туннель и всё умрёт.

После настройки всего этого нужно нажать кнопочку Save & Apply, а после этого собственно включить "политическую" маршрутизацию в верху страницы (на скриншоте она уже уже включена):

Вот собственно и вся настройка! Открыв ya.ru и введя запрос "мой ip", я могу убедиться, что Яндекс считает, что я зашел через РФ. Как видно, кроме трюка с установкой пакета dnsmasq-full всё остальное делается исключительно через web-интерфейс и доступно практически домохозяйке.

Гораздо интереснее пытливому читателю будет узнать то, как это работает под капотом и что стоит за этой кажущейся простотой, на самостоятельную настройку которой можно было бы угробить несколько часов, а то и дней (если изучать вопрос с нуля). Этому будет посвящен остаток статьи.

Заглядываем под капот

Как это работает под капотом? Сначала поймём проблематику. Маршрутизация выполняется на роутере ядром ОС (коим является Linux) по правилам, которые содержат только IP-адреса и адреса подсетей. Резолвинг же DNS-имён в IP-адреса происходит в пространстве пользователя и является довольно сложным, многостадийным процессом, переносить который в ядро не стоит по целому ряду причин. Кроме того, мы не только хотим маршрутизировать по доменному имени, но и создавать агрегирующие правила, которые включают в себя, например, всю зону RU, то есть нужен ещё механизм матчинга - определения того, попадает ли домен в то или иное агрегирующее правило. Таким образом, нужен некоторый клей, который будет склеивать матчинг и резолвинг DNS-имён с маршрутизацией по IP-адресам. И вот тут наступает проблема как это сделать.

Я не буду делать в данной статье обзор различных методов решения этой проблемы, а опишу именно то, как это реализовано в OpenWrt 23.05. И так, начнём с основного. OpenWrt 23.05 перешел полностью на использование nftables вместо старого и привычного механизма iptables, который используется для различных манипуляций с трафиком (NAT, Firewall, policy based routing и так далее).

Nftables позволяет завести множества (set), в которых хранятся различные однотипные данные, использующиеся в работе правил (rules). Для осуществления нашей задачи нам необходимо создать одно или несколько множеств IP-адресов, куда будут попадать IP-адреса прорезолвленных доменов, которые мы хотим маршрутизировать особенным образом. Кто будет наполнять эти множества и как? Очень просто: собственно dnsmasq (в версии, которая собрана со всеми включенными опциями dnsmasq-full) умеет принимать в качестве конфигурации правила матчинга доменов и имена nft set'ов, в которые нужно добавлять IP-адреса прорезолвленных доменов, которые попадают в те или иные правила.

# ps | grep dnsmasq
11519 root      2912 S    {dnsmasq} /sbin/ujail -t 5 -n dnsmasq -u -l -r /bin/ubus -r /etc/TZ -r /etc/dnsmasq.conf -r /etc/ethers -r /etc/group -r /etc/hosts -r /etc/passwd -w /tmp/dhcp.leases -r /tmp/dnsmasq.d -r /tmp/hosts -r /tmp/r
11521 dnsmasq   5128 S    /usr/sbin/dnsmasq -C /var/etc/dnsmasq.conf.cfg01411c -k -x /var/run/dnsmasq/dnsmasq.cfg01411c.pid
18988 root      1376 S    grep dnsmasq

Видно, что процесс dnsmasq запущен с указанием того, что конфигурацию нужно взять в файле /var/etc/dnsmasq.conf.cfg01411c. Заглянем в него:

# auto-generated config file from /etc/config/dhcp
conf-file=/etc/dnsmasq.conf
dhcp-authoritative
domain-needed
localise-queries
read-ethers
enable-ubus=dnsmasq
expand-hosts
bind-dynamic
local-service
edns-packet-max=1232
domain=home.arpa
local=/home.arpa/
except-interface=pppoe-wan
except-interface=wg0
addn-hosts=/tmp/hosts
dhcp-leasefile=/tmp/dhcp.leases
resolv-file=/tmp/resolv.conf.d/resolv.conf.auto
stop-dns-rebind
rebind-localhost-ok
dhcp-broadcast=tag:needs-broadcast
conf-dir=/tmp/dnsmasq.d
user=dnsmasq
group=dnsmasq


dhcp-ignore-names=tag:dhcp_bogus_hostname
conf-file=/usr/share/dnsmasq/dhcpbogushostname.conf

bogus-priv
conf-file=/usr/share/dnsmasq/rfc6761.conf
dhcp-range=set:lan,192.168.1.100,192.168.1.249,255.255.255.0,12h
no-dhcp-interface=pppoe-wan

Вроде бы нет никакой информации о том, что мы рассматриваем в данной статье, но в 22-й строчке видим conf-dir=/tmp/dnsmasq.d - то есть директиву включить в конфиг все файлы найденные в директории /tmp/dnsmasq.d. А что у нас в той директории? А там только один файл с именем pbr - собственно этот файл и создаётся пакетом pbr (policy based routing), который устанавливается по зависимостям при установке пакета luci-app-pbr. Заглянем в него:

nftset=/ru/4#inet#fw4#pbr_wg0_4_dst_ip_cfg046ff5 # russia
nftset=/drive2.ru/4#inet#fw4#pbr_wan_4_dst_ip_cfg056ff5 # russia_except

Как видно тут содержатся все наши политики, настроенные в статье в количестве двух. Из этого следует, что адреса доменов, попадающих в зону ".ru" после резолвинга добавляются в множество pbr_wg0_4_dst_ip_cfg046ff5, а наш домен-исключение drive2.ru будет добавлен в множество pbr_wan_4_dst_ip_cfg056ff5. Проверим, что там есть. Зайдем по ssh на роутер и дадим команду nft list sets.

table inet fw4 {
        set pbr_wg0_4_dst_ip_cfg046ff5 {
                type ipv4_addr
                flags interval
                counter
                auto-merge
                comment "russia"
                elements = { 5.61.23.66 counter packets 0 bytes 0, 5.101.37.37 counter packets 150 bytes 12811,
                             223.121.15.28 counter packets 0 bytes 0 }
        }
        set pbr_wan_4_dst_ip_cfg056ff5 {
                type ipv4_addr
                flags interval
                counter
                auto-merge
                comment "russia_except"
                elements = { 91.215.43.178 counter packets 52 bytes 3120 }
        }
}

Отлично, резолвер уже что-то напихал в эти множества. Но что теперь делать с этой красотой? Нужные какие-то правила, которые будут эти множества использовать. Давайте посмотрим, дав команду nft list ruleset. Я отрезал нерелевантные части правил и получил:

table inet fw4 {
        chain mangle_prerouting {
                type filter hook prerouting priority mangle; policy accept;
                jump pbr_prerouting comment "Jump into pbr prerouting chain"
        }

        chain pbr_prerouting {
                ip daddr @pbr_wg0_4_dst_ip_cfg046ff5 goto pbr_mark_0x020000 comment "russia"
                ip daddr @pbr_wan_4_dst_ip_cfg056ff5 goto pbr_mark_0x010000 comment "russia_except"
        }

        chain pbr_mark_0x010000 {
                counter packets 52 bytes 3120 meta mark set meta mark & 0xff01ffff | 0x00010000
                return
        }

        chain pbr_mark_0x020000 {
                counter packets 99038 bytes 18734081 meta mark set meta mark & 0xff02ffff | 0x00020000
                return
        }
}

В соответствии с цепочкой правил pbr_prerouting ядро заглядывает в наши множества и, если ip-адрес попадает в одно из них, происходит переход к цепочке правил pbr_mark_0x010000 или pbr_mark_0x020000, а в этих цепочках правил пакет маркируется меткой 0x00010000 или 0x00020000 соответственно. Метка - это число, которое присваивается пакету и сопровождает его внутри ядра Linux до тех пор, пока он не отправится в сеть. На основе этой метки ядро может принимать дальнейшие решения о том, что с этим пакетом делать. Одно из решений, которое может принять ядро - это таблица маршрутизации, по которой данный пакет надо маршрутизировать. Мы можем посмотреть набор таких правил, по которым принимаются решения командой ip rule:

0:      from all lookup local
30000:  from all fwmark 0x10000/0xff0000 lookup pbr_wan
30001:  from all fwmark 0x20000/0xff0000 lookup pbr_wg0
32766:  from all lookup main
32767:  from all lookup default

А вот и наши метки на строках 2 и 3. Если у пакета есть метка 0x10000 с маской 0xff0000, то для его маршрутизации используется таблица маршрутизации pbr_wan, а если 0x20000 с маской 0xff0000, то таблица маршрутизации pbr_wg0. В остальных случаях используется таблица маршрутизации по умолчанию. Тут нужно дать, наверное, пояснение, ибо многие не в курсе того, что кроме дефолтной таблицы маршрутизации могут существовать альтернативные. Они используются, если в правилах политики есть директива lookup с указанием альтернативной таблицы маршрутизации. Таким образом, у нас может быть несколько default route (0.0.0.0/0) - один в основной таблице и в альтернативных таблицах. Давайте же посмотрим, на содержание этих таблиц pbr_wan и pbr_wg0, командой ip route list table <имя таблицы>:

# ip route list table pbr_wan
default via 172.16.0.1 dev pppoe-wan 
192.168.1.0/24 dev br-lan proto kernel scope link src 192.168.1.1 
# ip route list table pbr_wg0
default via 192.168.7.2 dev wg0 
192.168.1.0/24 dev br-lan proto kernel scope link src 192.168.1.1 

Тут всё просто. В одном случае весь трафик улетает через шлюз провайдера (для доменов-исключений), во втором - через VPN туннель. В обоих случаях исключение добавляется для локальной сети, которая не маршрутизируется никуда.

Ну вот собственно и всё. Подытожим то, как осуществляется эта кухня:

  1. Резолвер (dnsmasq) по сконфигурированным правилам добавляет прорезолвленные IP-адреса в множества nftables.

  2. Правила nftables проверяют попадает ли адрес назначения того или иного IP-пакета в одно из множеств и, если попадает, присваивает пакету определенную метку.

  3. Политика маршрутизации трафика в зависимости от наличия у пакета той или иной метки выбирает таблицу маршрутизации, которая будет использоваться для маршрутизации пакета.

Возможные проблемы, с которыми вы столкнетесь

Тут нужно также рассказать о том, какие косяки могут вас поджидать.

Косяк номер один. Теоретически можно себе представить, что на одном и том же IP-адресе будут жить два web-сервера. На одном из них будет сайт, который требует, чтобы вы входили на него через VPN, а на другом будет жить сайт, который требует, чтобы вы входили без VPN. Это невозможно разрулить данной схемой, так как маршрутизация выполняется только по IP-адреса сайта.

Косяк номер два. Несмотря на то, что nftables поддерживают множества, в которых элементы добавляются с определенным временем жизни, которое можно сбрасывать, если какое-то правило нашло этот элемент в множестве, Policy routing в OpenWrt этого не использует. Попав в множество единожды, адрес остается там до перезагрузки. Это может привести, во-первых, к раздуванию размера таблицы со временем. Во-вторых, может случится так, что сайт переедет на другой IP-адрес, а старый IP-адрес останется в множестве. В принципе это не очень страшно, кроме того случая, если на этом IP-адресе не появится другой сайт, на который вам нужно ходить без VPN'а. Это, конечно, крайне маловероятно, но нужно это иметь ввиду.

Косяк номер три. Как правило, любой сайт ссылается на другие сайты (скрипты, изображения, статический контент и прочее). Может получиться так, что вы завернете весь трафик на этот сайт, например, в VPN, а внутри HTML-страницы будут ходить на другие домены, которые не будут заворачиваться в VPN. Это может приводить к спецэффектам, которые потребуется расследовать. Тут на помощь может прийти какой-нибудь режим разработчика в браузере. Хуже, если этим будем заниматься какой-нибудь апп на смартфоне. Тогда придётся включать логирование DNS-запросов и вылавливать проблему.

Update:

Косяк номер четыре описан в первом каменте к посту.

Косяк номер пять: не будет работать, если на вашем гаджете настроен кастомный резолвер (а получаемый от роутера по DHCP игнорируется). Зачем такое бывает нужно? Ну вот, например, на гаджетах у детей у меня настроен DNS от adguard с family protection.

Комментарии (26)


  1. aborouhin
    08.01.2024 16:37
    +3

    Косяк номер четыре (лично наступил на эти грабли при составлении списков для маршрутизации путём ресолвинга доменных имён - хотя у меня там другой способ, но проблема будет та же; впрочем, для этого вообще достаточно ничего не ресолвить, а просто иметь разную геолокацию DNS-сервера и конечного сервера, через который мы маршрутизируем часть трафика...)

    DNS некоторых сервисов (да хотя бы фейсбука) выдаёт разные IP в зависимости от геолокации клиента. И может внезапно выясниться, что полученный нашим маршрутизатором IP вообще недоступен с того зарубежного сервера, через который мы его маршрутизируем... и вот что с этим делать, решительно непонятно :(


    1. dmitrmax Автор
      08.01.2024 16:37

      У dnsmasq в OpenWrt есть такая настройка. Вы можете поднять dnsmasq на своём VPN-сервере и форвардить резолвинг для фейсбука (и других доменов, которые пропишете) на этот резолвер.


      1. aborouhin
        08.01.2024 16:37

        В Вашем упрощённом примере, когда маршрутизация идёт только по доменным именам, это сработает, да. Но обычно доменные имена - это только один из критериев. У меня, скажем, в правиле маршрутизации может быть либо IP, либо подсеть, либо номер AS, либо код страны для GeoIP, либо доменное имя. После чего всё это складывается/вычитается, суммаризуется, чтобы избежать повторов и пересечений, и формируются BGP community на каждую "точку выхода" (у меня их три - Нидерланды, Россия и Турция). Даже если на стадии ресолвинга доменных имён использовать для каждой точки свой DNS-сервер, это не помогает, когда тот самый "геоспецифичный" IP попал в маршруты по другому критерию.


        1. dmitrmax Автор
          08.01.2024 16:37

          Генацвале, статья называется "Маршрутизация по DNS в OpenWrt", а не "Решаем все проблемы фрагментации Интернета". Тем не менее, вы в своём каменте сформулировали потенциальную проблему, вам было дано решение сформулированной проблемы. Далее вы пытаетесь мне сказать, что это не универсальное решение - ну дак я и не претендую на его универсальность.

          У меня, скажем, в правиле маршрутизации может быть либо IP, либо подсеть, либо номер AS, либо код страны для GeoIP, либо доменное имя.

          У вас, скажем, это на какой железяке и ОС такие фокусы можно вытворять?

          В целом, опять же не понятна ни постановка задачи, ни методы решения, которые вы пытаетесь использовать. Видно только то, что вы достигли определенного мастерства в жонглировании маршрутами.

          Даже если на стадии ресолвинга доменных имён использовать для каждой точки свой DNS-сервер, это не помогает, когда тот самый "геоспецифичный" IP попал в маршруты по другому критерию.

          Ничего не понял. Если вы скажем для домена facebook.com делаете настойку, что его нужно резолвить через, скажем, Нидерланды, то логично и в policy routing прописать, что этот домен нужно роутить через Нидерланды. Тогда все карты вроде бы сходятся. Это ведь не так происходит, что facebook.com будет резолвится другим резолвером и поэтому не попадет в нужный nft set. Это именно, что его локальный dnsmasq прорезолвит и добавит его в nft set, просто он сделает это не через дефолтовый DNS-сервер, а через указанный.


          1. aborouhin
            08.01.2024 16:37

            Для Вашего примера из поста проблема будет, поэтому я про неё написал.
            Вы ответили, как её для этого примера решить - ОК, это достаточно очевидно. Но поскольку вряд ли, как я предположил, весь используемый Вами механизм выборочной маршрутизации сводится к этому примеру (в конце концов, для того же FB Вы вряд ли будете вылавливать все его домены, а просто завернёте в тоннель всю AS32934) - то я написал ещё одно уточнение. В ответ Вы зачем-то возбудились и перешли на грузинский :)

            Генацвале, статья называется "Маршрутизация по DNS в OpenWrt", а не "Решаем все проблемы фрагментации Интернета".

            На Хабре обычно комментарии к статьям в 10 раз полезнее самих статей, в т.ч. и тем, что расширяют её предмет на смежные вопросы :)

            У вас, скажем, это на какой железяке и ОС такие фокусы можно вытворять?

            Linux, bird, unbound, много самописного кода. Но не суть, речь про принципы, реализация вопрос десятый. Тот же dnsmasq так, как Вы написали, умеет не только под OpenWRT, но и везде, куда его можно поставить (и вот это, кстати, неочевидная его фича и самое ценное открытие из Вашего поста - спасибо, буду думать, как его в своём случае применить).

            Ничего не понял

            Ещё раз - если Вы только по доменным именам настраиваете маршруты, то да, есть решение. Но Вы вряд ли так делаете, поскольку это долго и непродуктивно, доменные имена хороши для отдельных исключений, когда какой-то сайт ведёт себя странно, - а за базу мы всё-таки берём GeoIP, номера автономных систем или готовые списки заблокированных сайтов. И вот в этом случае, если facebook.com, как указано выше, попал в маршруты не в результате ресолвинга, а в составе AS32934 или списка от zapret-info/antifilter, - проблема будет.

            Да, это не актуально для того конкретного примера, который Вы привели в посте. Но наверняка будет актуально для тех, кто этот рецепт захочет интегрировать в свою систему борьбы с огораживанием в интернете.


            1. dmitrmax Автор
              08.01.2024 16:37

              К счастью или к сожалению, но я не живу в РФ и то, какие списки вы там у себя создали для борьбы с запретами я не слежу. Хотя мысленно шлю вам лучи поддержки в этом нелегком деле. Соответственно специфика распространения этих списков, интегрирования их в свою систему - действительно не учитывается в данной статье от слова совсем.

              Поэтому что касается фейсбука, то я вообще не буду добавлять его в какие-либо исключения, ибо у меня он не забанен.

              Но! Давайте я пофантазирую, на тему того, как бы это сделал я, если бы у меня не было никаких списков, и стояла бы задача настроить это дело в РФ.

              Но Вы вряд ли так делаете, поскольку это долго и непродуктивно, доменные имена хороши для отдельных исключений, когда какой-то сайт ведёт себя странно

              Да! Поэтому я бы настроил в дефолтовой таблице маршрутизации default route через VPN. Создал бы правило, заворачивающее всю зону .ru и .su в wan по указанному в статье методу. И дальше в процессе использования я бы понял, что с фейсбуком что-то не то, и добавил бы его в список особенного резолвинга в настройках dnsmasq. Всё как вы говорите - общий сетап с точечной настройкой исключений.


              1. aborouhin
                08.01.2024 16:37

                Я в обе стороны задачи решаю, т.к. и я сам, и другие пользователи моего маленького домашне-корпоративного VPN могут находиться по разные стороны границы:

                • для пользователей в РФ - заблокированные в РФ (готовый список + дополнения в виде отдельных AS и доменов) или самостоятельно огородившиеся от российских IP (тут сборная солянка, добавляю по мере выявления) через Голландию, турецкие (тоже те ещё любители отгородиться от иностранных IP) - через Турцию, остальное напрямую;

                • для пользователей в Турции - российские через РФ, заблокированные в Турции через Голландию (последнее пока не реализовано ввиду малой востребованности, но есть наброски - там чуть сложнее, т.к. нет готового списка), остальное напрямую;

                • для пользователей вне РФ и Турции - российские через РФ, турецкие через Турцию, остальное напрямую.

                И всё это в разных вариантах (VPN для мобильных пользователей, микротики для дома/офиса), по три протокола туннелей (по убыванию приоритета WG -> Amnezia-WG -> WG over Cloak) между всеми точками и OSPF для автоматического выбора хоть какого-то живого маршрута на случай блокировок VPN... В общем, хватает головной боли :)

                И DNS реально добавляет проблем. Ладно геоспецифичные IP, их мало. Есть ещё CDN. А есть тупо ситуация, что у пользователя скэшировался IP от предыдущего подключения (например, мобильного интернета), а он приходит в дом/офис, где DNS-сервер уже другой... Короче, вот прям беспроблемный опыт, чтобы был доступ ко всему и вся подкапотная работа по маршрутизации была для пользователя вообще не видна, не получается никак...


                1. dmitrmax Автор
                  08.01.2024 16:37

                  Интересный сетап. Если запилите статью, то я с удовольствием почитаю её.

                  WG, конечно, бяка в том плане, что всегда сигнализирует, что всё ОК и линк якобы есть. Тут OSPF, конечно, кстати.


                  1. aborouhin
                    08.01.2024 16:37

                    Не, статью я точно не осилю, там такой говнокод, прости Господи, что выкладывать стыдно (как обычно, всё начиналось с маленького скрипта для конкретной задачи, который потом оброс функциями и костылями и надо бы переписать с нуля нормально, но некогда, да вроде и так работает :) А без кода это будет как у той совы из анекдота, которая стратег, а как реализовать - придумайте как-нибудь сами :)


              1. aborouhin
                08.01.2024 16:37

                Создал бы правило, заворачивающее всю зону .ru и .su в wan по указанному в статье методу

                Из России наружу - да. Но теоретически российские сайты с доменными именами в других зонах тоже могут огораживаться от зарубежных IP (хотя навскидку примеров не вспомню).

                А вот из-за границы в Россию заворачивать всю зону ru/su через российский сервер - плохая идея хотя бы потому, что в этих зонах полно заблокированных Роскомнадзором сайтов, которые через Россию как раз и не откроются.

                В общем, GeoIP в любом направлении получается гораздо более надёжным способом. А там уже можно исключения точечно добавлять.


        1. dmitrmax Автор
          08.01.2024 16:37

          В Вашем упрощённом примере, когда маршрутизация идёт только по доменным именам

          И да, в моём примере маршрутизация идёт не только по доменным именам. Вы вольны добавить роуты по IP или по подсетям во все таблицы маршрутизации - как в дефолтовую, так и в альтернативные. По доменным именам (если вы дочитали статью до конца) происходит только маркировка трафика и выбор таблицы маршрутизации. Сама маршрутизация выполняется по классической схеме: нахождение наиболее длинного префикса IP-адреса в таблице.


  1. dorne
    08.01.2024 16:37
    +1


    Спасибо за статью. Очень обстоятельно и понятно написано.

    Жаль, что не было такой раньше, так что, пришлось разбираться самому. За пару дней осилил, и пришел к конфигурации почти идентичной вашей.

    Однако, получилось несколько отличий.

    В качестве  домашнего маршрутизатора я использую виртуальную машину на x86 сервере. Обычный libvirt/qemu. Туда прекрасно встал образ OpenWRT для ПК.

    На VPS хостинг где должен стоять VPN сервер тоже залит x86 образ OpenWRT. Исключительно из-за компактности, простоты настройки и нетребовательности по ресурсам.

    Вместо WG, я использую ZeroTier в режиме  Ethernet моста без автоконфигурации IP. В этом случае VPNы выглядят как обычные Ethernet интерфейсы, и, соответственно настраиваются. Это удобно.


    1. dmitrmax Автор
      08.01.2024 16:37

      На сколько я понимаю, ZeroTier работает через userspace приложение, которое и осуществляет шифрование. То есть пакет при отправке проделывает следующий путь:

      1) Ядро понимает, что пакет подлежит маршрутизации в VPN-интерфейс, и ставит его в очередь этого интерфейса

      2) Юзерспейс апп вычитывает этот пакет и шифрует его, после чего отправляет его как обычный UDP пакет, пакет снова уходит в ядро

      3) UDP пакет с шифрованным payload'ом отправляется через реальный интерфейс.

      Таким образом, получается что на каждый акт маршрутизации пакета в VPN возникает необходимость сходить в юзерспейс и обратно. А WireGuard делает всё внутри ядра. Для железяки x86 это, полагаю, не суперважно, но на дохлых домашних роутерах, можно запросто уперется в CPU при большом потоке через VPN-интерфейс. Это надо иметь ввиду.


      1. dorne
        08.01.2024 16:37

        Да. Все верно. WG тут исключение из правил, он быстрее, и идеально подходит для создания постоянных L3 тоннелей на слабых роутерах.  И, если бы он умел L2 тоннели, было бы совсем хорошо. Правда, все не на столько плохо у остальных VPN-ов, т.к. в ядро можно сходить сразу за большой пачкой пакетов за раз, а не за каждым отдельно. Но, накладные расходы, конечно, все равно выше.

        Но, на x86 железе ZT узким местом не становится. Так что, для моего юзкейза не принципиально. И, предпочтение было отдано функциональности и простоте настройки.

        ZT исповедует принцип полного отсутствия конфигурации (zero config). Его не надо настраивать на роутере совсем. Только установить. Что удобно.


        1. aborouhin
          08.01.2024 16:37

          У ZT в российских реалиях есть один фатальный недостаток - он по умолчанию завязан на вполне конкретные корневые серверы, которые, если протокол станет хоть сколько-то распространён и обратит на себя внимание, будут элементарно заблокированы тупо по IP. А поднятие собственных корневых серверов (moons) - это уже совсем не zero config, да и клиентскими устройствами не всегда поддерживается (здравствуй, Микротик...)


          1. dorne
            08.01.2024 16:37

            Блокировать корневые сервера относительно бесполезно, т.к. сделать зеркало на VPS можно. Это вроде даже DNAT-ом/SNAT-ом сделать можно. Но, конечно, кастомный адрес для пира прописывается в конфиге. Так что, да, не zero conf, конечно, будет уже.

            Так что, недостаток не настолько фатальный. Но, я сомневаюсь что до этого дойдет. В конце концов, в РФ вроде не VPN запрещен, а обход блокировок с помощью VPN. Мы же тут, вообще, решаем обратную задачу. Роутим российский трафик через российский хостинг чтобы нормально пользоваться российскими сайтами из-за бугра.


            1. aborouhin
              08.01.2024 16:37

              В конце концов, в РФ вроде не VPN запрещен, а обход блокировок с помощью VPN. Мы же тут, вообще, решаем обратную задачу.

              Что никому не помешало блокировать тупо протоколами. И тут совершенно всё равно, для чего он используется. Блокировались в т.ч. туннели, у которых обе стороны вообще внутри России и они даже теоретически для обхода блокировок использованы быть не могут. Сейчас, вроде, отпустило после осенних "учений" (отчёты о блокировках продолжают люди писать, меня лично давно не задевало), но что-то мне подсказывает, что чем ближе к марту - тем всё будет хуже.

              Так что готовимся. Поэтому у меня по умолчанию обычный WG, который уже легко блокировали на практике, если он отвалится - трафик переключается на туннель Amnezia-WG, которая почти не уступает в скорости, но тоже может быть заблокирована, если начнут оставлять белые списки протоколов (пока такое на практике делали только на Юге после заварушки в махачкалинском аэропорту - но было, так что если захотят, повторят в национальном масштабе), ну и если отвалится и он - то вступает в дело Cloak, который раза в 3 тормознее WG, но и заблокировать его уже куда сложнее. В принципе, если бы не микротики, к которым для работы Amnezia-WG и Cloak на приемлемой скорости пришлось прикрутить по Orange Pi в комплект, я бы обычный WG вообще заменил на Amnezia-WG сразу, и оставил только два протокола.

              Но, конечно, кастомный адрес для пира прописывается в конфиге.

              И вот те самые Микротики, будь они трижды неладны, такой опции не имеют :( Откуда я всё это про ZT и узнал, рассматривал его как альтернативу тоже, но этот факт остановил.


              1. dorne
                08.01.2024 16:37

                Не совсем понятно к чему вы готовитесь с такими сложностями, но для описанного выше юзкейза все это просто не нужно. Даже если заблокируют на время, или даже на постоянку, без российских сайтов, которые блокируют заграничных пользователей можно вполне и обойтись.

                Вся эта конструкция, исключительно для удобства. А не для обхода блокировок органов же.


                1. aborouhin
                  08.01.2024 16:37

                  Когда бизнес в России, а сам за границей (да и когда сам в России тоже - ибо любые минимально ценные данные всё равно лучше держать за границей) работающий VPN между своими российскими пользователями/серверами и своими заграничными пользователями/серверами совершенно критичен. Да и вообще в любом сценарии, где хотя бы один удалённый сотрудник отделён российской границей от корпоративного сервера (неважно, кто из них с какой стороны). Или ладно, пусть не сотрудник и бизнеса вообще никакого нет, а Ваш родственник из России, которому Вы тоже в Ваше личное частное облако на европейском сервере бекап настроили. Да мало ли вариантов...


  1. Dixnos
    08.01.2024 16:37

    Не пойму, что я делаю не так? Указал всё тоже самое, но сервис выдает ошибки, если указан просто суфикс (ru, net, com). При указании явного домена - всё прожевывает.


    1. dmitrmax Автор
      08.01.2024 16:37

      "Use resolver set support for domains" установлено в Dnsmasq nft?


      1. Dixnos
        08.01.2024 16:37
        +1

        Да, проблема была в этом. А можно поподробнее по настройке самого VPN интерфейса - какую метрику ему ставить? Ставил выше WAN, тогда после настройки pbr то работает, то нет. Ставил ниже - весь трафик идёт на VPN, на WAN ничего идти не хочет.


        1. dmitrmax Автор
          08.01.2024 16:37

          Судя по вашему скрину, если вы зону ru форвардите в wg0, то смею предположить, что вы из-за бугра хотите в рунет через VPN ходить. Если всё так, то в настройке peer'а на интерфейсе WireGuard, нужно убрать эту галочку:

          В этом случае вообще не будет создан default route с маршрутом через VPN в главной таблице маршрутов, поэтому не нужны никакие метрики, так как не будет никаких конфликтов.


        1. dmitrmax Автор
          08.01.2024 16:37

          Без установки "Use resolver set support for domains" в "Dnsmasq nft set" роутер работает в режиме preresolve. То есть указанные домены он резолвит на старте и добавляет их в нужный set. Поэтому в этом режиме можно указывать только конкретные домены, которые можно во что-то прорезолвить. При этом не будут работать субдомены указанных доментов. В режиме же "Dnsmasq nft set" всё выполняется на лету и резолвер по ходу дела понимает какие домены надо заносить в nft set и поэтому возможна более гибкая настройка, когда указывается только верхний поддомент.


  1. andreid1303
    08.01.2024 16:37
    +1

    Можно подробней про косяк номер пять: "не будет работать, если на вашем гаджете настроен кастомный резолвер (а получаемый от роутера по DHCP игнорируется)"?

    Похоже действительно не работает. У меня установлен Nextdns, также пробовал Dnscrypt-proxy, Stubby. Домены из nft sets не резолвятся (из Pbr или прописанные вручную). Если в Pbr Use resolver set support for domains = Disabled, то розолвятся без dnsmasq, но это занимает кучу времени если записей много.

    Что делать для шифрования DNS? Поднимать сервер на VPS?


    1. dmitrmax Автор
      08.01.2024 16:37

      Если у вас на клиенте роутера используется кастомный DNS, то тут ничего не попишешь (

      Если вы готовы рассмотреть возможность сделать так, чтобы роутер форвардил DNS запросы на nextdns, а клиент переключить на использование резолвера роутера, то это делается путем установки пакета https-dns-proxy. Настройка либо из консоли, либо через Web UI после установки пакета luci-app-https-dns-proxy. По дефолту он, кажется, на гугловые серверы слать будет.