Описание работы программы для автоматического обхода блокировок в интернете, код программы лежит на репозитории Antiblock.

Приблизительно в мае 2022 года был заблокирован один из доменов YouTube (yt3.ggpht.com), через который происходит выгрузка превью и логотипов каналов. Если до этого блокировки меня особо не утруждали, то YouTube без картинок, для активного пользователя, это тяжело. Далее были предприняты несколько разных подходов к оптимальному обходу блокировки этого домена.

 Пример отсутствия превью
Пример отсутствия превью

Попытка номер один - Свой собственный VPN

Весной 2022 года стало актуально создавать собственные VPN на основе мощностей арендных VPS. Я тоже создал свой VPN WireGuard. Почитать как это сделать самому можно например в статье.

Пропускание всего трафика с телефона или планшета через VPN приводило к быстрой разрядке батареи. И на некоторые сайты не заходило через VPN, например Госуслуги или Avito. Поэтому было решено придумать более гибкий и адаптивный метод, а именно маршрутизацию сразу на роутере. Но для маршрутизации на роутере необходимо иметь роутер с поддержкой WireGuard, а значит необходим роутер, на который можно установить OpenWrt. Мой предыдущий роутер (RT-AC57U V3) не имел такой возможности, а так как я хотел достаточно мощнее устройство для экспериментов и настройке сетевого диска, то выбор пал на Beelink U59 Pro. К нему была куплена пара мощных антенн и другой Wi-Fi модуль (Mediatek MT7921K). Подробнее о сборке и настройке я расскажу в следующих статьях. Таким образом у меня появился x86 роутер с возможностью компилировать и запускать на нем код. И была предпринята следующая попытка.

Beelink U59 Pro с антеннами
Beelink U59 Pro с антеннами

Попытка номер два - Таблица маршрутизации

Второй попыткой настроить обход блокировок было внести в таблицу маршрутизации все заблокированные IP-адреса, выложенные на сайте antifilter.download. В списке allyouneed.lst находится около десяти тысяч IP-адресов и подсетей, что достаточно легко помещается в таблицу маршрутизации роутера. Для автоматического внесения адресов был написал Bash скрипт.

ip r | grep VPN | cut -d ' ' -f1 | xargs -I{} ip route del {}
curl https://antifilter.download/list/allyouneed.lst > ip.txt
cat ip.txt | xargs -I{} ip route add {} dev VPN

Если добавить этот скрипт в автозапуск Cron, то таблица маршрутизации будет иметь актуальный список IP-адресов и подсетей.

Скрипт работал, но быстро обнаружились его изъяны. IP-адреса доменов входящие в CDN очень часто меняются, поэтому их нет в списке allyouneed.lst. А домен, ради которого всё затевалось (yt3.ggpht.com), как раз тоже входит в CDN Google. Поэтому от этого подхода пришлось отказаться, но он может кому-то пригодится из-за простой настройки и отсутствию необходимости компилировать код под своё устройство.

Попытка номер три - BPF DPI

"Чтобы бороться с DPI надо думать, как DPI".

Почитав ранее статьи про BPF, я подумал реализовать простой DPI на основе BPF, которая будет маршрутизировать трафик в зависимости от SNI TLS Handshake.  Это возможно было сделать как напрямую маршрутизируя пакеты через BPF, так и выставляя флаг nf_conntrack, а далее маршрутизировать через nftables. Но тут снова сыграл свою роль самый важный для меня домен (yt3.ggpht.com), общение с ним идет не через TLS 1.3, а через QUIC, а значит и nf_conntrack не будет толком работать, да и пакеты зашифрованные, хоть и известным ключом, и для анализа придется их детектировать и расшифровывать. Для обычно не продуктивных роутеров это будет очень тяжелая нагрузка. От этого подхода тоже было решено отказаться.

Попытка номер четыре - Автоматическая антиблокировка

Осознав плюсы и минусы предыдущих попыток, было решено маршрутизировать в зависимости от DNS пакетов. Была написана программа прокси DNS запросов, которая автоматически добавляет IP-адреса заблокированных доменов в таблицу маршрутизации. И удаляет при истечении времени жизни IP-адреса. Программа выложена на репозитории.

Кратко опишу работу программы:

  • Для быстрой проверки входит ли домен в список заблокированных доменов, необходимо добавить заблокированные домены в хеш-таблицу. Перепробовав разные хеш-таблицы для Си, ни одна из них не имела нужные характеристики. Была написана библиотека для хеш-таблицы, подробнее о ней будет рассказано в следующей статье. Для экономии памяти заблокированные домены хранятся не как массив указателей на нуль-терминированные строки, а как длинный массив лежащих подряд нуль-терминированных строк. Экономия памяти, потому что malloc на каждую строку занимал бы служебную информацию. А в хеш-таблице можно хранить смещение начала строки от начала массива, тем самым хеш-таблица состоит из четырехбайтных int, а не восьмибайтных pointer. Список заблокированных доменов автоматически обновляется каждые 12 часов в отдельном потоке. Домены скачиваются с сайта. Код описан в файле urls_read.c.

  • При поступлении DNS запроса от клиента, id запроса заменяется на внутренний, чтобы не было совпадения id номеров с разных клиентов. Старый id, IP-адрес, порт, время прихода пакета и хэш домена запоминаются в массив в поле с номером нового id. При увеличении нагрузки до предельной, если ответы на запросы не успевают приходить, то мы сможем начать отбрасывать пакеты от клиента на этапе поиска нового внутреннего id.

  • При поступлении ответа от DNS сервера, по пришедшему id смотрим поле с этим номером в массиве из предыдущего абзаца. Проверяем не истекло ли время возврата пакета, проверяем совпадение хэш домена с сохранённым, если всё хорошо, то помещаем пришедший пакет в кольцевой буфер для обработки другим потоком. Кольцевой буфер необходим для постоянства в использовании памяти. Код обработки запросов описан в файле net_data.c.

  • Бывает много разных типов DNS ответов, но нас интересуют два варианта типа “A” и “CNAME”. Тип “A” делает прямое соответствие между доменом и IP-адресом. Тип “CNAME” делает соответствие между данным доменом и новым доменом. Если встречаем заблокированный домен с ответом типа “A”, то добавляем IP-адрес в таблицу маршрутизации, и добавляем IP-адрес в хэш таблицу с ключом IP-адрес, а значением временем истечения жизни IP-адреса. Если встречаем заблокированный домен с ответом типа “CNAME”, то добавляем домен в список временно заблокированных, а так же добавляем домен с хэш таблицу с ключом домен, а значением временем истечения жизни домена. Код описан в файле dns_ans.c.

  • Отдельный поток раз в минуту проверяет истекшие IP-адреса и домены и удаляет их. Код описан в файле ttl_check.c.

Тестирование

Тестирование проводилось на отдельно написанной программе, которая эмулирует большое количество DNS запросов. Тестировалось на миллионе самых популярных доменов по версии CloudFlare. Моя программа выдерживала 100 000 запросов в минуту. А так же тестирование проводилось с использованием AddressSanitizer и MemorySanitizer, никаких проблем выявлено не было. При работе программа потребляет очень мало оперативной памяти, примерно 12 MB, причем 8 MB это объем заблокированных доменов, тем самым подходит даже самым простым роутерам.

Разбор на конкретном примере

Например я хочу зайти на RuTracker.

Программа детектировала DNS запрос с заблокированным доменом "RuTracker" и добавила два IP адреса "172.67.187.38" и "104.21.72.173" в таблицу маршрутизации и запомнила что в 15:44:53 необходимо удалить их, так как их актуальность истечет.

Моя программа пишет лог действий в формате CSV:

15:40:30,add ip,rutracker.org,172.67.187.38,15:44:53
15:40:30,add ip,rutracker.org,104.21.72.173,15:44:53

Таблица маршрутизации:

root@OpenWrt:~# ip r | grep VPN
104.21.72.173 via 192.168.6.37 dev VPN
172.67.187.38 via 192.168.6.37 dev VPN

Мы видим внесенные IP адреса, но уже 15:45:19 IP адреса удаляются.

Лог программы:

15:45:19,del ip,,172.67.187.38,15:44:53
15:45:19,del ip,,104.21.72.173,15:44:53

Новая таблица маршрутизации:

root@OpenWrt:~# ip r | grep VPN

Таким образом мы динамически узнаем нынешний IP адрес домена и добавляем именно актуальный IP адрес, тем самым имеем возможность использовать домены лежащие на CDN.

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


  1. ValdikSS
    29.06.2023 19:08
    +50

    Ваш подход похож на опцию --ipset в DNS-резолвере dnsmasq, которая добавляет IP-адреса резолва в ip set, а не в таблицу маршрутизации. IP set'ы хорошо оптимизированы, адреса из set'а могут удаляться по таймауту автоматически, ядром, но требуют маркировки пакетов средствами netfilter, чтобы направить их в нужную таблицу маршрутизации.

    Несколько недоработок вашей реализации:

    • Отсутствует поддержка AAAA-записей. На провайдерах с IPv6 (в России это порядка 7% интернет-трафика) обход не заработает — нужно хотя бы удалять AAAA-ответ из резолва, если нет желания обрабатывать полноценно.

    • Отсутствует поддержка записи SVCB/HTTPS (type 65), которая используется на устройствах Apple. Она имеет приоритет выше, чем A/AAAA, из-за чего iPhone может отправлять запросы в обход ваших правил.

    • На некоторых устройствах и некоторых браузерах по умолчанию включены DNS-over-HTTPS/TLS, из-за чего ваш резолвер не будет получать запросы. У Apple шифрование запросов отключается блокировкой доменов 'mask.icloud.com', 'mask-h2.icloud.com', 'mask.apple-dns.net', а в Firefox — 'use-application-dns.net'.


    1. vagonovozhaty
      29.06.2023 19:08

      Я крайне не рекомендую блокировать какие-либо домены Apple. Это может привести к непредсказуемым последствиям. Какие-то функции перестанут работать или будут работать медленно, через раз, и вы замучаетесь искать причину.

      Лучше в dnsmasq что-то типа:

      address=/use-application-dns.net/
      address=/_dns.resolver.arpa/

      если такой вариант для вас актуален.


      1. sergeymakinen
        29.06.2023 19:08
        +1

        Однако, блокировка mask.icloud.com и mask-h2.icloud.com отдельно документирована для отключения iCloud Private Relay, что может быть полезно для исключения DNS-запросов мимо своего резолвера.


        1. karen07 Автор
          29.06.2023 19:08
          +1

          Как раз на OpenWRT в пакете https-dns-proxy по умолчанию блочатся эти домены)


          1. werter_l
            29.06.2023 19:08

            На ваш, кхм, роутер я бы накатил сперва proxmox ve + pfsense в виде вирт. машины.

            Еще и запас мощности остается для экспериментов.

            Вот это был бы РОУТЕР :)

            P.s. Если заинтересуетесь, цикл статей по proxmox, pfsense etc https://forum.netgate.com/topic/163435/proxmox-ceph-zfs-pfsense-и-все-все-все-часть-2/


            1. karen07 Автор
              29.06.2023 19:08

              Я один раз предпринимал попытку поставить Arch и всё настроить для роутера, но оказалось очень не легко и время ограничено было)


              1. werter_l
                29.06.2023 19:08

                Пробуйте


    1. karen07 Автор
      29.06.2023 19:08

      Спасибо за развернутую рецензию, со временем я добавлю поддержку остальных режимов)


  1. Actaeon
    29.06.2023 19:08

    Хотел написать, что в принципе, на функциональном уровне, это решается много проще , посредством того же privoxy, причем, прописывать там надо скорее прокси в рунет, оставляя заграничный по умолчанию, но если вам так сильно нужен udp ...


    1. karen07 Автор
      29.06.2023 19:08
      +5

      Одно из преимуществ моей программы это очень маленькое потребление памяти, примерно 12 мегабайт, из них 8 мегабайт занимают все заблокированные домены. Я постараюсь дополнить пост примерами реального лога и потреблением памяти.


    1. microArt
      29.06.2023 19:08

      Пожалуйста, распишите подробно, как можно настроить Privoxy?


      1. Actaeon
        29.06.2023 19:08

        Минимальная настройка - открываете config (config.txt в win) дописываете
        forward / outerworld_proxy:port
        forward .ru interrusia_proxy:port
        forward .su interrusia_proxy:port
        (если соотв прокси не нужен, а нужно - прямое соединение ставите просто точку) . После чего на 127.0.0.1:8118 privoxy поднимает вам прокси перенаправляющий вас туда куда вам нужно.
        Где брать внешний прокси ?? Ну тут каждый исхитряется как может. Бомж-вариант - берете смартфон , ставите на него N штук vpn и Android Proxy Server. Главное преимущество - никаких openwrt, никаких BPF - ничерта не нужно, матушка в свои 71 настраивает в режиме телефонной консультации ...


  1. AlexGluck
    29.06.2023 19:08
    +3

    Возможно против блокировок поможет "метод Пригожина", но я ни к чему не призываю. Хотя если уважаемый Максут Шадаев захочет о чем нибудь поговорить с Липовыми, может попросить проработать откровенные недостатки базы заблокированных сервисов. Особенно внесудебные блокировки. Стабильно рабочая сеть важна в стране. Как они ещё госуслуги умудрились не заблокировать я удивлен.


    1. domix32
      29.06.2023 19:08
      +5

      Стать уголовником, сесть на распил бюджета, собрать больше уголовников, а потом устроить бучу? Умно, умно...


      1. AlexGluck
        29.06.2023 19:08
        +3

        А кто-то не оценил постироничный юмор.


  1. Alexsey
    29.06.2023 19:08
    +1

    Помнится когда я начал заворачивать трафик до yt3.ggpht.com через VPN у меня начались какие-то нехорошие приколы, но вот в упор не могу вспомнить какие... Но достаточно серьезные чтобы сидеть без аватарок и картинок в community постах (превью как не удивительно почему-то грузятся нормально)


    1. S-trace
      29.06.2023 19:08

      Кстати да, у меня превьюшки тоже грузились и без yt3.ggpht.com, но спасибо автору статьи что ткнул носом - добавил в zapret-hosts-user.txt, обновил списки и теперь аватарки отображаются снова


    1. Zipdots
      29.06.2023 19:08

      У меня из таких приколов - долгий "запуск" видео, который начинается спустя N дней нормальной работы завернутого трафика в VPN. Видео просто начинает 5-15 секунд крутить колесом загрузки. Пока не решил.


      1. karen07 Автор
        29.06.2023 19:08

        Если есть возможность, можете попробовать моё решение, у меня проблем с зависанием нет)


        1. Zipdots
          29.06.2023 19:08

          Увы, у меня Mikrotik, не OpenWrt.


          1. nApoBo3
            29.06.2023 19:08

            Там есть докер :)


            1. Rim13
              29.06.2023 19:08

              Но он только для ARM


  1. TheDarkKRONOS
    29.06.2023 19:08
    +9

    Вообще на Android в WireGuard клиенте можно и не весь трафик пропускать, а только определённые приложения. Там надо зайти в добавленный конфиг, нажать на редактирование и будет пункт с исключёнными приложениями. Так можно исключать банк. приложения, поставить 2 браузера, один из которых идёт по впн, а второй по обычному каналу.


    1. bit314
      29.06.2023 19:08

      О прикольно, не замечал такой фишки. Удобно однако.


      1. vikarti
        29.06.2023 19:08
        +1

        Она у большинства "нормальных" VPN-клиентов под андроид есть.
        К сожалению у некоторых (насколько помню — Mullvad когда он еще не был сам заблокирован и Proton VPN) — добавление в список исключений — очень долго если приложений много.
        Ну и VPN на андроид = прощай блокировка рекламы (ну или надо ее делать на сервере, если этого достаточно) если она тоже через VPN а сочетать и то и другое умеет штатно только парочка Adguard VPN + Adguard, но вот только они платные.


        1. TheDarkKRONOS
          29.06.2023 19:08

          Kiwi Browser + uBlock origin + Tampermonkey со скриптами под Wireguard и вот резалка рекламы работает


          1. vikarti
            29.06.2023 19:08

            Да? И во всех приложениях который используют WebView или Custom Tab будет работать?


            С браузером то проблема решается просто (Firefox и расширения). С Custom Tab там — теоретически проблема решается тем же Firefox'ом а на практике — есть приложения где тупит все из-за этого.


            1. TheDarkKRONOS
              29.06.2023 19:08

              Если нужно в приложениях под впн, то срабатывает Wireguard + Adguard DNS (на андроиде его как кастомный прописать надо). Реклама в играх режется под vpn на удивление. Тут зависит от того, какой список программ на андроид в клиенте прописать. Там есть список исключений (то есть программы, которые не надо через впн проводить), тогда система также через впн ходит (и WebView и прочие, если не исключать) или же есть белый список, то есть только определённые программы ходят через впн, тогда система ходит напрямую.


              1. vikarti
                29.06.2023 19:08
                +1

                Меня вариант с Adguard + Adguard VPN вполне устраивает. Удобно. Весьма.
                При этом Wireguard через свою VPS(где сервер matrix живет) тоже настроен, но в запас.


  1. Dolios
    29.06.2023 19:08
    +5

    для маршрутизации на роутере необходимо иметь роутер с поддержкой WireGuard, а значит необходим роутер, на который можно установить OpenWrt.

    Кинетики на родной прошивке умеют в WireGuard.


    1. PlatinumThinker
      29.06.2023 19:08
      +1

      и микротики


      1. vikarti
        29.06.2023 19:08

        В RouterOS 7
        Для которой, судя по сайту, до сих пор нет long-term сборки (я в курсе что из Winbox'а такой видно 7.5 но на сайте то нет)


      1. savagebk
        29.06.2023 19:08

        И Asus


    1. TheChief5055
      29.06.2023 19:08

      А policy-based routing они умеют?


    1. yupych
      29.06.2023 19:08

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


      1. dartraiden
        29.06.2023 19:08

        WireGuard и не требует аппаратного ускорения шифрования. Какие вообще процессоры умеют аппаратно ускорять ChaCha20 и Poly1305?

        Если же вы говорите про аппаратное ускорение AES, то, во-первых, AES в WireGuard не используется, во-вторых кинетики прекрасно ускоряют его, кроме совсем уж старых моделей.

        Вышенаписанное относится к роутерам с MIPS-процами. В новые кинетики сейчас вообще ставят ARM-процессоры, в их группе в телеграме скидывали результаты бенчмарков - спокойно жуют сотни мегабит.



  1. DrrRos
    29.06.2023 19:08
    +1

    А теперь задание со звёздочкой - обход блокировок когда блокировка не на стороне РКН, а на стороне сервера.


    1. karen07 Автор
      29.06.2023 19:08

      Тоже думал на эту тему, но тут уже без DPI ни как не обойтись)Только если кто то составит отдельный список таких)


    1. vikarti
      29.06.2023 19:08

      Любое решение с VPN но точка выхода НЕ в России?


      1. mayorovp
        29.06.2023 19:08

        Это сломает Госуслуги.


        1. SuhoffGV
          29.06.2023 19:08

          И не только. Не раз натыкался на рос сайты которые тупо не открываются через vpn


          1. vikarti
            29.06.2023 19:08

            Бывает смешнее — сайт открывается но логин не работает. Потому что site.ru и api.site.ru — за cloudflare но вот у api.site.ru настройки более более жесткие.


            Если блокировки на той стороне — можно просто считать их блокировками на этой и просто сайт в туннель заворачивать. Если же это становится неудобно — заворачивать в туннель все по умолчанию (с Youtube/Facebook сложно но списки сетей Google вполне себе публичны например) а то что должно быть в России — принудительно НЕ заворачивать. Либо руками по доменам либо раз список сгенерить либо вообще с https://antifilter.network/bgp брать — есть там списочек ру-сетей.
            У меня на основном так сказать месте откуда доступ — схема с получением по BGP списков куда нельзя ходить и отправление трафика на эти сети куда следует. В дополнение — ручное задание по DNS-имени (у роутера и машин за ним — один и тот же DNS-резолвер так что если уж резолвинг на роутере решил что habr.com это 1.2.3.4 то машинки за ним тоже полезут за habr.com через 1.2.3.4).
            С копией антизапрета (запуск их контейнера у себя) возится пока не хочется. В том числе потому что ну очень не хочется танцы с прокси устраивать, придется ж не только для браузеров это городить, например для gradle еще надо.
            (и без того хватает проблем — приходится вот в Firefox + MultiAccount Containers + Container Proxy несколько групп сайтов через отдельные спецпрокси и в данном случае тема совсем не про цензуру).


    1. HiroX
      29.06.2023 19:08

      Давно есть идея как реализовать подобное: самописная прокся, которая будет автоматически повторять запрос через какой-нить парент при обнаружении 403/access denied. На минутку призадумался, а может это вообще реализуемо через прозрачный squid


      1. karen07 Автор
        29.06.2023 19:08

        нет, потому что мало смотреть на уровне http/https, надо смотреть на уровне tcp/udp.


      1. olartamonov
        29.06.2023 19:08
        +2

        Там вообще может быть не 403, а 200 ОК — и страница-заглушка «извините, мы с вами больше не работаем».

        Вот, например, порнхаб...

        Но пока мы делаем это чисто для себя, вполне достаточно вести ручками список таких URL'ов. У меня, например, стоит ящик с pfSense, к нему из разных локаций (дача, другие квартиры — клиенты на роутерах, мобильные клиенты на мобилах) коннектятся по WireGuard (заодно получая доступ к локальным ресурсам этого ящика, от умного дома до файлопомойки и торрентокачалки), а на ящике уже стоит VPN и policy-based routing со списками, формируемыми как вручную, так и из подгружаемых снаружи перечней IP или доменов.


    1. Pavel7
      29.06.2023 19:08

      Если сервер блокирует по странам, то подойдёт geo ip роутинг. Есть отличная статья с готовым скриптом по выгрузке подсетей разных стран в формате удобном для запихивания в route add: https://habr.com/en/articles/659655/

      Поднимаем на роутере VPN соединения к серверам в нужных странах и роутим подсети этих стран через эти VPN.


  1. blind_oracle
    29.06.2023 19:08
    +3

    Я задолбался бороться с отдельными блокировками и просто зароутил все зарубежные (с т.з. RIPE) сети в VPN, а российские идут напрямую. Это не идеально, конечно, но лучше чем другие подходы как по мне. Если есть какие-то нестыковки - просто добавляю в исключения.


    1. karen07 Автор
      29.06.2023 19:08

      на моем роутере antiblock стоит уже 3 месяца и не возникает проблем, список доменов обновляется оперативно, так что если есть возможность, можете попробовать)


      1. blind_oracle
        29.06.2023 19:08

        Зачем? Зашифрованные DNS оно не отловит, да и стояла у меня подобная система какое-то время: https://habr.com/ru/articles/467547/


        1. karen07 Автор
          29.06.2023 19:08

          я как раз в том числе опирался на вашу статью)


  1. Javian
    29.06.2023 19:08
    +1

    Есть статья с оригинальным методом получения списка заблокированных сайтов:
    Точечный обход блокировок PKH на роутере с OpenWrt с помощью WireGuard и DNSCrypt (часть 1)
    https://habr.com/ru/articles/440030/


    subnet.lst — список заблокированных подсетей, изменяется не часто.
    ip.lst — список заблокированных адресов, прям из списка РКН
    community.lst — список заблокированных адресов, который составляется комьюнити antifilter.download


    1. karen07 Автор
      29.06.2023 19:08

      я через него домены и адреса как раз и получаю)


  1. Wesha
    29.06.2023 19:08
    +9

    Хм...


    1. karen07 Автор
      29.06.2023 19:08
      +2

      аахаха????