Тема довольно изъезжена, знаю. К примеру, есть отличная статья, но там рассматривается только IP-часть блоклиста. Мы же добавим еще и домены. В связи с тем, что суды и РКН блокируют всё направо и налево, а провайдеры усиленно пытаются не попасть под штрафы, выписанные "Ревизорро" — сопутствующие потери от блокировок довольно велики. Да и среди "правомерно" заблокированных сайтов много полезных (привет, rutracker) Я живу вне юрисдикции РКН, но на родине остались родители, родственники и друзья. Так что было решено придумать легкий для далеких от ИТ личностей способ обхода блокировок, желательно вовсе без их участия. В этой заметке я не буду расписывать базовые сетевые вещи по шагам, а опишу общие принципы как можно реализовать эту схему. Так что знания как работает сеть вообще и в Linux в частности — must have. Для начала освежим в памяти что же блокируется. В выгружаемом XML от РКН несколько типов блокировок: Мы их сведем для простоты к двум: IP и домен, а из блокировок по URL будем просто вытаскивать домен (точнее за нас это уже сделали). Хорошие люди из Роскомсвободы реализовали прекрасный API, через который можно получать то, что нам нужно: Для этого нам нужен какой-нибудь маленький зарубежный VPS, желательно с безлимитным траффиком — таких много по 3-5 баксов. Брать нужно в ближнем зарубежье чтобы пинг был не сильно большой, но опять-таки учитывать, что интернет и география не всегда совпадают. А так как никакого SLA за 5 баксов нет — лучше взять 2+ штуки у разных провайдеров для отказоустойчивости. Далее нам необходимо настроить зашифрованный туннель от клиентского роутера до VPS. Я использую Wireguard как самый быстрый и простой в настройке т.к. клиентские роутеры у меня тоже на базе Linux (APU2 или что-то на OpenWRT). В случае каких-нибудь Mikrotik/Cisco можно использовать доступные на них протоколы вроде OpenVPN и GRE-over-IPSEC. Можно, конечно, завернуть вообще весь интернет-траффик через зарубеж. Но, скорее всего, от этого сильно пострадает скорость работы с локальным контентом. Плюс требования к полосе пропускания на VPS будут сильно выше. Поэтому нам нужно будет каким-то образом выделять траффик к заблокированным сайтам и выборочно его направлять в туннель. Даже если туда попадёт какая-то часть "лишнего" траффика, это всё равно гораздо лучше, чем гонять всё через тоннель. Для управления траффиком мы будем использовать протокол BGP и анонсировать маршруты до необходимых сетей с нашего VPS на клиентов. В качестве BGP-демона возьмём BIRD, как один из наиболее функциональных и удобных. С блокировками по IP всё понятно: просто анонсируем все заблокированные IP с VPS. Проблема в том, что подсетей в списке, который отдает API, около 600 тысяч, и подавляющее большинство из них — это хосты /32. Такое количество маршрутов может смутить слабые клиентские роутеры. Поэтому было решено при обработке списка суммировать до сети /24 если в ней 2 и более хоста. Таким образом количество маршрутов сократилось до ~100 тысяч. Скрипт для этого будет дальше. Тут сложнее и способов есть несколько. Например, можно поставить прозрачный Squid на каждом клиентском роутере и делать там перехват HTTP и подглядывание в TLS-хендшейк с целью получения запрашиваемого URL в первом случае и домена из SNI во втором. Но из-за всяких новомодных TLS1.3+eSNI анализ HTTPS с каждым днем становится всё менее реальным. Да и инфраструктура со стороны клиента усложняется — придется использовать как минимум OpenWRT. Поэтому я решил пойти по пути перехвата ответов на DNS-запросы. Тут тоже над головой начинает витать всякий DNS-over-TLS/HTTPS, но эту часть мы можем (пока что) контролировать на клиенте — либо отключить, либо использовать свой сервер для DoT/DoH. Тут тоже может быть несколько подходов.  Это клиент-серверный протокол, основанный на Protocol Buffers и Frame Streams для передачи с DNS-сервера на некий коллектор структурированных DNS-запросов и ответов. По сути DNS-сервер передает метаданные запросов и ответов (тип сообщения,IP клиента/сервера и так далее) плюс полные DNS-сообщения в том (бинарном) виде в котором он работает с ними по сети. Важно понимать, что в парадигме DNSTap DNS-сервер выступает в роли клиента, а коллектор — в роли сервера. То есть DNS-сервер подключается к коллектору, а не наоборот. На сегодняшний день DNSTap поддерживается во всех популярных DNS-серверах. Но, например, BIND во многих дистрибутивах (вроде Ubuntu LTS) часто собран почему-то без его поддержки. Так что не будем заморачиваться пересборкой, а возьмём более легкий и быстрый рекурсор — Unbound. Есть некоторое количество CLI-утилит для работы с потоком DNSTap-событий, но для решения нашей задачи они подходят плохо. Поэтому я решил изобрести свой велосипед, который будет делать всё что необходимо: dnstap-bgp Алгоритм работы: Дополнительный функционал: Ограничения: Я собрал RPM и DEB пакеты для удобной установки. Должны работать на всех относительно свежих OS с systemd, т.к. зависимостей у них никаких нет. Итак, приступим к сборке всех компонентов воедино. В результате у нас должна получиться примерно такая сетевая топология:
 Логика работы, думаю, понятна из диаграммы: На сервере для маршрутов к заблокированным сайтам у меня внутри BIRD используется отдельная таблица и с ОС она никак не пересекается. В этой схеме есть недостаток: первый SYN пакет от клиента, скорее всего, успеет уйти через отечественного провайдера т.к. маршрут анонсируется не мгновенно. И тут возможны варианты в зависимости от того как провайдер делает блокировку. Если он просто дропает траффик, то проблем нет. А если он редиректит его на какой-то DPI, то (теоретически) возможны спецэффекты. Также возможны чудеса с несоблюдением клиентами DNS TTL, что может привести к тому что клиент будет юзать какие-то устаревшие записи из своего протухшего кеша вместо того чтобы спросить Unbound. На практике у меня ни первое ни второе не вызывало проблем, but your mileage may vary. Для удобства раскатывания я написал роль для Ansible. Она может настраивать как сервера, так и клиенты на базе Linux (рассчитано на deb-based дистрибутивы). Все настройки достаточно очевидны и задаются в inventory.yml. Эта роль вырезана из моего большого плейбука, поэтому может содержать ошибки — pull requests welcome :) Пройдёмся по основным компонентам. При запуске двух BGP-демонов на одном хосте возникает фундаментальная проблема: BIRD никак не хочет поднимать BGP-пиринг с локалхостом (или с любым локальным интерфейсом). От слова совсем. Гугление и чтение mailing-lists не помогло, там утверждают что это by design. Возможно есть какой-то способ, но я его не нашёл. Можно попробовать другой BGP-демон, но мне нравится BIRD и он используется везде у меня, не хочется плодить сущности. Поэтому я спрятал dnstap-bgp внутрь network namespace, которое связано с корневым через veth интерфейс: это как труба, концы которой торчат в разных namespace. На каждый из этих концов мы вешаем приватные p2p IP-адреса, которые за пределы хоста не выходят, поэтому могут быть любыми. Это тот же механизм который используется для доступа к процессам внутри любимого всеми Docker и других контейнеров. Для этого был написан скрипт и в dnstap-bgp был добавлен уже описанный выше функционал перетаскивания себя за волосы в другой namespace. Из-за этого его необходимо запускать под root либо выдать бинарнику CAP_SYS_ADMIN через команду setcap. По умолчанию в Ubuntu бинарник Unbound зажат AppArmor-профилем, который запрещает ему коннектиться ко всяким там DNSTap-сокетам. Можно либо удалить нафиг этот профиль, либо отключить его: Это, наверное, надо добавить в плейбук. Идеально, конечно, поправить профиль и выдать нужные права, но мне было лень. Скрипт для скачивания и обработки списка IP-адресов
Он скачивает список, суммаризует до префикса pfx. В dont_add и dont_summarize можно сказать IP и сети, которые нужно пропустить или не суммаризовать. Мне это было нужно т.к. подсеть моего VPS оказалась в блоклисте :) Самое смешное что API РосКомСвободы блокирует запросы с дефолтным юзер-агентом Питона. Видать скрипт-кидди достали. Поэтому меняем его на Огнелиса. Пока что он работает только с IPv4 т.к. доля IPv6 невелика, но это будет легко исправить. Разве что придется использовать еще и bird6. Скрипт для обновления
Он у меня запускается по крону раз в сутки, может стоит дергать раз в 4 часа т.к. это, по-моему, период обновления который РКН требует от провайдеров. Плюс, есть какие-то еще суперсрочные блокировки у них, которые может и быстрее прилетают. Делает следующее: Они были написаны не особо задумываясь, поэтому если видите что можно улучшить — дерзайте. Тут я приведу примеры для Linux-роутеров, но в случае Mikrotik/Cisco это должно быть еще проще. Для начала настраиваем BIRD: Таким образом мы будем синхронизировать маршруты, полученные из BGP, с таблицей маршрутизации ядра за номером 222. После этого достаточно попросить ядро глядеть в эту табличку перед тем как заглядывать в дефолтную: Всё, осталось настроить DHCP на роутере на раздачу туннельного IP-адреса сервера в качестве DNS и схема готова. При текущем алгоритме формирования и обработки списка доменов в него попадает, в том числе, youtube.com и его CDNы. А это приводит к тому что все видео будут ехать через VPN, что может забить весь канал. Возможно стоит составить некий список популярных доменов-исключений, которые блокировать у РКН пока что кишка тонка. И пропускать их при парсинге. Описанный способ позволяет обходить практические любые блокировки, которые реализуют провайдеры на данный момент. В принципе, dnstap-bgp можно использовать для любых других целей где необходим некий уровень управления траффиком на основе доменного имени. Только нужно учитывать что в наше время на одном и том же IP-адресе может висеть тысяча сайтов (за каким-нибудь Cloudflare, например), так что этот способ имеет довольно низкую точность. Но для нужд обхода блокировок этого вполне достаточно. Дополнения, правки, пуллреквесты — приветствуются! Инженер, разработчик