Настройка точечного обхода блокировок на роутере с OpenWRT

Опубликовано: 12 декабря 2020, Сб, автор: Андрей Семакин Обновлено: 23 мая 2021, Вс 11 минут

Думаю, не стоит объяснять, почему проблема блокировок в рунете стоит настолько остро. И дело даже не в пресловутых сериалах на торрентах. Если не обходить блокировки, то банально сложно работать: то bugs.python.org заблокирован, то документация по нужному тебе сервису недоступна, то какой-нибудь репозиторий с пакетами отвалился, из-за чего у тебя внезапно перестают собираться контейнеры, то LinkedIn пишет письма на почту, а зайти на него ты не можешь. Например, вот здесь можно найти список опенсорс-проектов, которые были заблокированы Роскомнадзором. Это всё тянется еще с тех времён в 2018 году, когда Роскомнадзор пытался накрыть Telegram ковровыми блокировками, блокируя без разбора всех направо и налево. Какое-то время я пытался с этим жить, игнорируя проблему, но однажды мое терпение лопнуло, во мне пробудился дух цифрового сопротивления, и я решил настроить себе VPN — да так, чтобы это работало на всех моих устройствах сразу.

Resistance Dog Good Boy

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

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

Стоит отметить, что если вам нужен обход блокировок только на одном устройстве, там вам намного проще будет настроить VPN именно на нём. У многих провайдеров VPN есть программы-клиенты, которые в несколько кликов по красивым кнопкам настроят вам всё, что нужно — просто погуглите, сравните цены и условия. Ещё можете посмотреть в сторону таких бесплатных проектов, как АнтиЗапрет.

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

Роутер и прошивка

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

Мой нынешний роутер — это Xiaomi Mi WiFi Router 3G. Выбрал его, потому что в нём достаточно мощи, чтобы на нём хорошо работала OpenWRT. Да и вообще, Mir3G — это, похоже, один из самых популярных девайсов в тусовке людей, которые занимаются дрессировкой роутеров, так что в сети по нему уже есть много мануалов и обсуждений на форумах. Если захотите купить такой же, то будьте аккуратны с конкретной моделью — их две под одним названием, а хорошая только та, у которой есть порт USB3. Такой роутер должно быть несложно купить на Авито или других досках объявлений.

У меня на данный момент установлена почти последняя версия прошивки:

OpenWrt 19.07.3, r11063-85e04e9f46

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

Блокировки

Грубо говоря, существует два типа блокировок:

  1. по доменным именам;
  2. по IP-адресам.

Нам нужно победить оба. Разберёмся с каждым из них по порядку.

Шаг 1. Шифрованный DNS

DNS (Domain Name System) — это распределенная система (нет, это не сеть магазинов), состоящая из множества серверов по всему миру, которая позволяет вашему компьютеру преобразовать человекочитаемое имя сайта в машиночитаемый IP-адрес, например, из github.com получить 140.82.121.3. Этот процесс называется "разрешением" или "резолвингом" доменного имени.

Блокировка по доменным именам зачастую реализуется провайдером следующим образом: ваш роутер по умолчанию использует DNS-сервер, который контролируется провайдером и для заблокированных доменных имён возвращает специальный адрес сайта-заглушки. Именно это происходит, когда вы видите в браузере "доступ к ресурсу ограничен в соответствии со 149-ФЗ".

Доступ ограничен

Кажется, что решение очевидно — просто не пользоваться DNS-сервером провайдера, а использовать, например, Google DNS, IP-адреса которого уже стоит знать наизусть как считалку — 8.8.8.8, 8.8.4.4. Раньше это действительно работало, но сегодня провайдеры уже не дадут вам так просто себя обмануть.

Поскольку DNS — это очень старый протокол, в котором никак не было предусмотрено шифрование от слова "вообще", у провайдеров есть возможность перехватывать ваши запросы и подменять ответы, вставляя свой сайт-заглушку, даже если запрос шёл, например, к Google DNS. Именно этим они и занимаются. По сути, провайдер проводит против вас атаку DNS hijacking.

Вот как это работает, если всё хорошо. В таком сценарии можно даже не заметить этого "человека посередине" в лице вашего провайдера:

DNS-запрос к незаблокированному домену

А вот так это работает, когда всё плохо. Провайдер наверняка даже не отправляет запрос к DNS-серверу, которому он предназначался, а просто сразу же возвращает ответ, подставляя адрес своей заглушки:

DNS-запрос к заблокированному домену

Но по-настоящему эффективное решение есть. Если провайдер не сможет увидеть, к какому домену происходит обращение, то не сможет и подменить ответ. А ещё лучше, если он вообще не будет знать, что происходит резолвинг имени. Этого можно достичь шифрованием.

Совсем недавно стали появляться шифрованные версии протокола DNS — DNSCrypt, DoT (DNS-over-TLS) и DoH (DNS-over-HTTPS). Судя по всему, именно последний из них (DoH) получит наибольшую популярность. При использовании этого протокола DNS-запрос складывается внутрь HTTPS-запроса, который, само собой, зашифрован. Для человека посередине, который перехватит такой трафик, это будет выглядеть как просто один из миллиарда шифрованных HTTP-запросов к Google или Cloudflare.

DNS-over-HTTPS мы и собираемся настроить в первую очередь. Ничего принципиально нового я не скажу, вся информация взята с соответствующей страницы вики OpenWRT.

  1. Подключимся к роутеру по SSH:

    ~ $ ssh root@192.168.1.1
    root@192.168.1.1's password:
    BusyBox v1.30.1 () built-in shell (ash)
    _______                     ________        __
    |       |.-----.-----.-----.|  |  |  |.----.|  |_
    |   -   ||  _  |  -__|     ||  |  |  ||   _||   _|
    |_______||   __|_____|__|__||________||__|  |____|
            |__| W I R E L E S S   F R E E D O M
    -----------------------------------------------------
    OpenWrt 19.07.3, r11063-85e04e9f46
    -----------------------------------------------------
    root@OpenWrt:~#
    
  2. Обновим список пакетов. Эту команду нужно выполнять после каждой перезагрузки роутера перед установкой пакетов:

    opkg update
    
  3. Установим необходимые зависимости, которые в связке позволят принимать DNS-запросы от клиентов в локальной сети и отправлять их во внешний интернет, замаскированными под HTTPS-запросы. Третий пакет — это просто плагин для веб-интерфейса OpenWRT LuCI, чтобы можно было через GUI настраивать поведение DoH:

    opkg install dnsmasq https-dns-proxy luci-app-https-dns-proxy
    
  4. Чтобы плагин для LuCI заработал, нужно сделать выполнить вот такую команду:

    /etc/init.d/rpcd restart
    
  5. Готово! Теперь на компьютере, подключенном к роутеру, можно проверить как это работает:

    # это было до настройки
    $ nslookup linkedin.com
    ...
    Name:   linkedin.com
    Address: 95.213.158.61
    
    # это после настройки
    $ nslookup linkedin.com
    ...
    Name:   linkedin.com
    Address: 13.107.42.14
    Name:   linkedin.com
    Address: 2620:1ec:21::14
    

Обратите внимание, что адреса возвращаются разные: до настройки DoH — неправильный, это результат подмены адресов провайдером, а после — правильный. Можно считать это маленькой победой!

По умолчанию используются DNS-over-HTTPS сервера от Google и Cloudflare — очень разумный выбор, потому что на этих двух компаниях держится добрая половина глобального интернета, и их блокировка наверняка приведёт к катастрофическим последствиям, так что блокировать их, надеюсь, никто не будет. Если же хочется сменить используемые сервера, то сделать это теперь можно через веб-интерфейс: Services > DNS HTTPS Proxy.

OpenWRT LuCI DoH settings

Сам по себе обход блокировок по DNS вряд ли даст ожидаемый эффект, потому что, как правило, в довесок к доменному имени, ресурс блокируется еще и по IP-адресам — видимо, для надежности. Это следующий тип блокировок, которые нужно победить.

Стоит упомянуть про побочные эффекты от такой настройки — могут стать недоступными различные внутренние ресурсы провайдера. Например, когда у меня кончается интернет и я забываю его оплатить, провайдер просто не может меня переадресовать на страницу, куда он обычно направляет своих забывчивых абонентов. Для меня выглядит это всё так, будто интернет просто пропал, без каких-либо объяснений. Я даже первый раз из-за этого чуть не поругался с поддержкой, а оказалось, что это я сам дурак. Что ж, просто приходится платить заранее.

Если нравится статья, то подпишитесь на уведомления о новых постах в блоге, чтобы ничего не пропустить! А ещё читайте другие мои посты!

Шаг 2. Настройка VPN WireGuard

Для того, чтобы обходить блокировки по IP-адресам, придётся настроить VPN — другого способа нет. VPN "спрячёт" от провайдера ваши IP-пакеты так, что он не будет знать куда именно они направляются и что в себе содержат. В качестве протокола предлагаю использовать WireGuard — легковесный VPN-протокол, который к тому же довольно легко настраивать.

Всё описанное дальше вдохновлено и по сути является пересказом вот этой замечательной статьи на Хабре с некоторыми (минимальными) дополнениями от меня.

VPN — это клиент-серверный протокол. Наш роутер будет клиентом, но также нам обязательно нужен сервер. Можно либо поднять свой VPN-сервер самостоятельно (смотрите инструкцию в статье по ссылке выше, это не сложно), либо оформить подписку на какой-нибудь готовый VPN-сервис. Я пробовал оба варианта, но поддерживать свой VPN-сервер оказалось достаточно трудозатратно, и это тоже, как ни странно, стоит денег. Сложнее всего найти хостинг для своего VPN с незаруиненной репутацией. Проблема, с которой я столкнулся, заключалась в том, что все дешевые хостинги уже имеют плохую репутацию (спасибо вам, господа спамеры), и некоторые ресурсы, типа Пикабу (который тоже, как выяснилось, немножко заблокирован), сразу же заранее отвечают ошибкой 403. Намного проще купить готовый VPN, и пусть лучше кто-то другой за меня беспокоится о том, чтобы мне были доступны все нужные мне сайты.

В последнее время появляется очень много VPN-сервисов, которые работают по протоколу WireGuard — это нынче горячая тема. Недавно поддержка WireGuard была добавлена в основной состав ядра Linux. Погуглите, выбор сервисов действительно есть. Сейчас я пользуюсь RedShieldVPN, и меня пока что всё устраивает.

Бонус для читателей: при регистрации по этой ссылке вам подарят месяц бесплатного VPN.

Дальше буду описывать процесс для RedShieldVPN, но, думаю, для других подобных сервисов процесс должен быть похожим.

  1. Переходим в раздел WireGuard, авторизуемся, выбираем страну и скачиваем конфиг. Я обычно выбираю какую-нибудь европейскую страну, чтобы пинг был приемлемым. В этот раз возьму Финляндию.

    Вот примерно такой файл конфигурации скачался (ключи я заменил):

    [Interface]
    Address = 10.7.1.177/32, 6343:72e7:7ac3:e245:2675:1b79:4638:193a/128
    PrivateKey = private_key_goes_here
    DNS = 10.254.254.254
    MTU = 1412
    
    [Peer]
    PublicKey = public_key_goes_here
    AllowedIPs = 0.0.0.0/0, ::/0
    Endpoint = fin.lopata.today:51820
    PersistentKeepalive = 10
    
  2. Переключимся на роутер и установим там нужную зависимость:

    opkg install wireguard
    
  3. Дальше нужно настроить сеть. Это происходит в файле /etc/config/network. Открываем его при помощи vi (если не умеете пользоваться этим редактором, то лучше предварительно почитайте что-нибудь для подготовки психики) и дописываем в конец две секции вот такого содержания:

    config interface 'wg0'
        option private_key 'private_key_goes_here'
        list addresses '10.7.1.177/32'
        option listen_port '51820'
        option proto 'wireguard'
    
    config wireguard_wg0
        option public_key 'public_key_goes_here'
        option allowed_ips '0.0.0.0/0'
        option route_allowed_ips '0'
        option persistent_keepalive '10'
        option endpoint_host 'fin.lopata.today'
        option endpoint_port '51820'
    

    Приватный и публичный ключи замените соответствующими значениями из конфига. Поле list addresses заполняется IPv4-адресом из строчки Address в конфиге, а IPv6 адрес мы просто игнорируем — позже станет понятно почему. Поле Endpoint из конфига WireGuard распадается, соответственно, в поля endpoint_host и endpoint_port в конфиге OpenWRT. Остальные поля статичные.

  4. Перезапустим сеть на роутере:

    /etc/init.d/network restart
    
  5. Проверим VPN:

    # wg show
    interface: wg0
    public key: C4zR9CoziizrJcPFtcUg4yyyG1S6+a+/bAuGi2BpmlM=
    private key: (hidden)
    listening port: 51820
    
    peer: public_key_goes_here
    endpoint: 185.103.110.133:51820
    allowed ips: 0.0.0.0/0
    latest handshake: 33 seconds ago
    transfer: 1012 B received, 3.53 KiB sent
    persistent keepalive: every 10 seconds
    

    Если команда wg show выводит что-то подобное, то всё идёт хорошо.

Шаг 3. Скрипт для сбора списков заблокированных адресов

Это возможно благодаря крутейшему сервису antifilter.download, который обрабатывает выгрузки Роскомнадзора и отдает их в виде списков IP-адресов в машиночитаемых форматах. Мы настроим периодическую выгрузку списков заблокированных адресов с antifilter.download, чтобы всегда иметь актуальную информацию о блокировках. По моему опыту обновлять этот список раз в сутки (например, ночью) вполне достаточно.

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

вот так будут бегать пакетики

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

  1. Установим зависимости:

    opkg install ipset curl
    
  2. Создадим файл /etc/init.d/hirkn со скриптом следующего содержания:

    #!/bin/sh
    START=99
    dir=/tmp/lst
    mkdir -p $dir
    echo "Downloading lists of blocked addresses..."
    curl -z $dir/subnet.lst https://antifilter.download/list/subnet.lst --output $dir/subnet.lst
    curl -z $dir/ipsum.lst https://antifilter.download/list/ipsum.lst --output $dir/ipsum.lst
    echo "Restarting firewall..."
    /etc/init.d/firewall restart
    

    Этот скрипт скачивает два списка заблокированных адресов — один в виде сетей (иногда РКН блокирует адреса целыми подсетями), другой — обобщенный список отдельных заблокированных адресов. За счёт обобщения в небольшие подсети он содержит не миллионы строк, а всего лишь что-то около 15 тысяч. Нам нужны оба списка, они не пересекаются между собой. Файлы сохраняются в каталог /tmp, который в OpenWRT хранится в оперативной памяти — это должно работать быстро. В конце скрипт перезапускает файлволл. Настройка файлволла будет происходит позже.

  3. Добавим этому скрипту права на выполнение:

    chmod +x /etc/init.d/hirkn
    
  4. Добавим скрипт в "автозапуск", чтобы он выполнялся при включении роутера после всех остальных скриптов:

    ln -s /etc/init.d/hirkn /etc/rc.d/S99hirkn
    
  5. Через cron запланируем выполнение этого скрипта раз в сутки:

    crontab -e
    

    В открывшемся редакторе нужно добавить на новой строке:

    0 4 * * * /etc/init.d/hirkn
    

    На crontab.guru можно посмотреть объяснение этой строчки.

  6. Включим cron, потому что по умолчанию в OpenWRT он выключен:

    /etc/init.d/cron enable
    /etc/init.d/cron start
    
  7. Убедимся, что скрипт работает.

    Запустим его:

    /etc/init.d/hirkn
    

    Если файлы со списками заблокированных адресов появились, то всё ок:

    # ls -lh /tmp/lst/
    -rw-r--r--    1 root     root      270.4K Dec  7 07:18 ipsum.lst
    -rw-r--r--    1 root     root         132 Dec  7 07:18 subnet.lst
    

Шаг 4. Дальнейшая настройка

Теперь осталось настроить роутер так, чтобы он правильно маршрутизировал пакеты через VPN-туннель.

  1. Создадим таблицу маршрутизации для VPN, добавив в файл /etc/iproute2/rt_tables строчку следующего вида:

    99      vpn
    
  2. Добавим дефолтный маршрут для VPN через туннельный интерфейс:

    ip route add table vpn default dev wg0
    

    Чтобы сделать это изменение постоянным, чтобы оно переживало перезагрузки роутера, создадим файл /etc/hotplug.d/iface/30-rknroute с вот таким содержимым:

    #!/bin/sh
    ip route add table vpn default dev wg0
    
  3. В файл /etc/config/network, который мы уже редактировали в процессе настройки WireGuard, добавим правило, которое будет перенаправлять маркированные пакеты в таблицу маршрутизации VPN:

    config rule
        option priority '100'
        option lookup 'vpn'
        option mark '0x1'
    
  4. Теперь приступим к настройке файрволла. Это самая важная часть логики, которая просматривает списки заблокированных адресов, и маркирует пакеты. В файл /etc/config/firewall допишем несколько абзацев:

    config zone
        option name 'wg'
        option family 'ipv4'
        option masq '1'
        option output 'ACCEPT'
        option forward 'REJECT'
        option input 'REJECT'
        option mtu_fix '1'
        option network 'wg0'
    
    config forwarding
        option src 'lan'
        option dest 'wg'
    
    config ipset
        option name 'vpn_subnets'
        option storage 'hash'
        option loadfile '/tmp/lst/subnet.lst'
        option match 'dst_net'
    
    config ipset
        option name 'vpn_ipsum'
        option storage 'hash'
        option loadfile '/tmp/lst/ipsum.lst'
        option match 'dst_net'
    
    config rule
        option name 'mark_subnet'
        option src 'lan'
        option proto 'all'
        option ipset 'vpn_subnets'
        option set_mark '0x1'
        option target 'MARK'
    
    config rule
        option name 'mark_ipsum'
        option src 'lan'
        option proto 'all'
        option ipset 'vpn_ipsum'
        option set_mark '0x1'
        option target 'MARK'
    

    Первый из них настраивает зону для VPN — из неё разрешён лишь выход пакетов и включён маскарадинг.

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

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

    Пятый и шестой абзацы отвечают за маркировку пакетов: "если пакет направляется на заблокированный адрес, то добавим ему пометку 0x1".

  5. Перезапустим сеть:

    /etc/init.d/network restart
    

    И запустим наш скрипт:

    /etc/init.d/hirkn
    
  6. После этого трафик к заблокированным сайтам должен побежать через VPN. Если нет, то попробуйте перезагрузить роутер.

Шаг 5. Отключаем IPv6

Этот шаг мой самый нелюбимый, потому что я за всё новое и против всего старого. По моему глубокому убеждению IPv4 уже давно должен умереть и быть вытеснен шестой версией протокола. К сожалению, дела у IPv6 пока идут не так гладко, как хотелось бы (сейчас он занимает всего около 30% процентов трафика).

Проблема в том, что antifilter.download выдаёт только заблокированные IPv4 адреса. Это значит, что наш обход блокировок не будет работать по IPv6. Если разрешить вашему роутеру работать по IPv6, то многие ваши устройства предпочтут открывать сайты по IPv6 либо напарываясь на страницы с блокировками от провайдеров, либо просто получая ошибки подключения по таймауту.

Отключаем IPv6 (команды взяты отсюда):

uci set 'network.lan.ipv6=off'
uci set 'network.wan.ipv6=off'
uci set 'dhcp.lan.dhcpv6=disabled'
/etc/init.d/odhcpd disable
uci commit

После этого может потребоваться ещё одна перезагрузка роутера, чтобы он перестал раздавать вновь подключенным устройствам IPv6-адреса.

Заключение

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

Добро пожаловать в Интернет

тэги: network, vpn, wiregurad, openwrt