Тема довольно изъезжена, знаю. К примеру, есть отличная статья, но там рассматривается только IP-часть блоклиста. Мы же добавим еще и домены.


В связи с тем, что суды и РКН блокируют всё направо и налево, а провайдеры усиленно пытаются не попасть под штрафы, выписанные "Ревизорро" — сопутствующие потери от блокировок довольно велики. Да и среди "правомерно" заблокированных сайтов много полезных (привет, rutracker)


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


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


Типы блокировок


Для начала освежим в памяти что же блокируется.


В выгружаемом XML от РКН несколько типов блокировок:


  • IP
  • Домен
  • URL

Мы их сведем для простоты к двум: 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 всё понятно: просто анонсируем все заблокированные 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.


Как перехватывать DNS?


Тут тоже может быть несколько подходов.


  • Перехват DNS-траффика через PCAP или NFLOG
    Оба эти способа перехвата реализованы в утилите sidmat. Но она давно не поддерживается и функционал очень примитивен, так что к ней нужно всё равно нужно писать обвязку.
  • Анализ логов DNS-сервера
    К сожалению, известные мне рекурсоры не умеют логгировать ответы, а только запросы. В принципе это логично, так как в отличии от запросов ответы имеют сложную структуру и писать их в текстовой форме трудновато.
  • DNSTap
    К счастью, многие из них уже поддерживает DNSTap для этих целей.

Что такое DNSTap?



Это клиент-серверный протокол, основанный на Protocol Buffers и Frame Streams для передачи с DNS-сервера на некий коллектор структурированных DNS-запросов и ответов. По сути DNS-сервер передает метаданные запросов и ответов (тип сообщения,IP клиента/сервера и так далее) плюс полные DNS-сообщения в том (бинарном) виде в котором он работает с ними по сети.


Важно понимать, что в парадигме DNSTap DNS-сервер выступает в роли клиента, а коллектор — в роли сервера. То есть DNS-сервер подключается к коллектору, а не наоборот.


На сегодняшний день DNSTap поддерживается во всех популярных DNS-серверах. Но, например, BIND во многих дистрибутивах (вроде Ubuntu LTS) часто собран почему-то без его поддержки. Так что не будем заморачиваться пересборкой, а возьмём более легкий и быстрый рекурсор — Unbound.


Чем ловить DNSTap?


Есть некоторое количество CLI-утилит для работы с потоком DNSTap-событий, но для решения нашей задачи они подходят плохо. Поэтому я решил изобрести свой велосипед, который будет делать всё что необходимо: dnstap-bgp


Алгоритм работы:


  • При запуске загружает из текстового файла список доменов, инвертирует их (habr.com -> com.habr), исключает битые строки, дубликаты и поддомены (т.е. если в списке есть habr.com и www.habr.com — будет загружен только первый) и строит префиксное дерево для быстрого поиска по этому списку
  • Выступая в роли DNSTap-сервера ждет подключения от DNS-сервера. В принципе он поддерживает как UNIX- так и TCP-сокеты, но известные мне DNS-сервера умеют только в UNIX-сокеты
  • Поступающие DNSTap-пакеты десериализуются сначала в структуру Protobuf, а затем само бинарное DNS-сообщение, находящееся в одном из Protobuf-полей, парсится до уровня записей DNS RR
  • Проверяется есть ли запрашиваемый хост (или его родительский домен) в загруженном списке, если нет — ответ игнорируется
  • Из ответа выбираются только A/AAAA/CNAME RR и из них вытаскиваются соответствующие IPv4/IPv6 адреса
  • IP-адреса кешируются с настраиваемым TTL и анонсируются во все сконфигурированные BGP-пиры
  • При получении ответа, указывающего на уже закешированный IP — его TTL обновляется
  • После истечения TTL запись удаляется из кеша и из BGP-анонсов

Дополнительный функционал:


  • Перечитывание списка доменов по SIGHUP
  • Синхронизация кеша с другими экземплярами dnstap-bgp через HTTP/JSON
  • Дублирование кеша на диске (в базе BoltDB) для восстановление его содержимого после перезапуска
  • Поддержка переключения в иной network namespace (зачем это нужно будет описано ниже)
  • Поддержка IPv6

Ограничения:


  • IDN домены пока не поддерживаются
  • Мало настроек BGP

Я собрал RPM и DEB пакеты для удобной установки. Должны работать на всех относительно свежих OS с systemd, т.к. зависимостей у них никаких нет.


Схема


Итак, приступим к сборке всех компонентов воедино. В результате у нас должна получиться примерно такая сетевая топология:


Логика работы, думаю, понятна из диаграммы:


  • У клиента настроен наш сервер в качестве DNS, причем DNS запросы тоже должны ходить по VPN. Это нужно для того чтобы провайдер не мог использовать перехват DNS для блокировки.
  • Клиент при открытии сайта посылает DNS-запрос вида "а какие IP у xxx.org"
  • Unbound резолвит xxx.org (или берет из кеша) и отправляет ответ клиенту "у xxx.org такие-то IP", параллельно дублируя его через DNSTap
  • dnstap-bgp анонсирует эти адреса в BIRD по BGP в том случае если домен есть в списке заблокированных
  • BIRD анонсирует маршрут до этих IP с next-hop self клиентскому роутеру
  • Последующие пакеты от клиента к этим IP идут уже через туннель

На сервере для маршрутов к заблокированным сайтам у меня внутри BIRD используется отдельная таблица и с ОС она никак не пересекается.


В этой схеме есть недостаток: первый SYN пакет от клиента, скорее всего, успеет уйти через отечественного провайдера т.к. маршрут анонсируется не мгновенно. И тут возможны варианты в зависимости от того как провайдер делает блокировку. Если он просто дропает траффик, то проблем нет. А если он редиректит его на какой-то DPI, то (теоретически) возможны спецэффекты.


Также возможны чудеса с несоблюдением клиентами DNS TTL, что может привести к тому что клиент будет юзать какие-то устаревшие записи из своего протухшего кеша вместо того чтобы спросить Unbound.


На практике у меня ни первое ни второе не вызывало проблем, but your mileage may vary.


Настройка сервера


Для удобства раскатывания я написал роль для Ansible. Она может настраивать как сервера, так и клиенты на базе Linux (рассчитано на deb-based дистрибутивы). Все настройки достаточно очевидны и задаются в inventory.yml. Эта роль вырезана из моего большого плейбука, поэтому может содержать ошибки — pull requests welcome :)


Пройдёмся по основным компонентам.


BGP


При запуске двух 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.


Пример скрипта для создания namespace
#!/bin/bash

NS="dtap"

IP="/sbin/ip"
IPNS="$IP netns exec $NS $IP"

IF_R="veth-$NS-r"
IF_NS="veth-$NS-ns"

IP_R="192.168.149.1"
IP_NS="192.168.149.2"

/bin/systemctl stop dnstap-bgp || true

$IP netns del $NS > /dev/null 2>&1
$IP netns add $NS

$IP link add $IF_R type veth peer name $IF_NS
$IP link set $IF_NS netns $NS

$IP addr add $IP_R remote $IP_NS dev $IF_R
$IP link set $IF_R up

$IPNS addr add $IP_NS remote $IP_R dev $IF_NS
$IPNS link set $IF_NS up

/bin/systemctl start dnstap-bgp

dnstap-bgp.conf
namespace = "dtap"
domains = "/var/cache/rkn_domains.txt"
ttl = "168h"

[dnstap]
listen = "/tmp/dnstap.sock"
perm = "0666"

[bgp]
as = 65000
routerid = "192.168.149.2"

peers = [
    "192.168.149.1",
]

bird.conf
router id 192.168.1.1;

table rkn;

# Clients
protocol bgp bgp_client1 {
    table rkn;
    local as 65000;
    neighbor 192.168.1.2 as 65000;
    direct;
    bfd on;
    next hop self;
    graceful restart;
    graceful restart time 60;
    export all;
    import none;
}

# DNSTap-BGP
protocol bgp bgp_dnstap {
    table rkn;
    local as 65000;
    neighbor 192.168.149.2 as 65000;
    direct;
    passive on;
    rr client;
    import all;
    export none;
}

# Static routes list
protocol static static_rkn {
    table rkn;
    include "rkn_routes.list";
    import all;
    export none;
}

rkn_routes.list
route 3.226.79.85/32 via "ens3";
route 18.236.189.0/24 via "ens3";
route 3.224.21.0/24 via "ens3";
...

DNS


По умолчанию в Ubuntu бинарник Unbound зажат AppArmor-профилем, который запрещает ему коннектиться ко всяким там DNSTap-сокетам. Можно либо удалить нафиг этот профиль, либо отключить его:


# cd /etc/apparmor.d/disable && ln -s ../usr.sbin.unbound .
# apparmor_parser -R /etc/apparmor.d/usr.sbin.unbound

Это, наверное, надо добавить в плейбук. Идеально, конечно, поправить профиль и выдать нужные права, но мне было лень.


unbound.conf
server:
    chroot: ""
    port: 53
    interface: 0.0.0.0
    root-hints: "/var/lib/unbound/named.root"
    auto-trust-anchor-file: "/var/lib/unbound/root.key"
    access-control: 192.168.0.0/16 allow

remote-control:
    control-enable: yes
    control-use-cert: no

dnstap:
    dnstap-enable: yes
    dnstap-socket-path: "/tmp/dnstap.sock"
    dnstap-send-identity: no
    dnstap-send-version: no

    dnstap-log-client-response-messages: yes

Скачивание и обработка списков


Скрипт для скачивания и обработки списка IP-адресов
Он скачивает список, суммаризует до префикса pfx. В dont_add и dont_summarize можно сказать IP и сети, которые нужно пропустить или не суммаризовать. Мне это было нужно т.к. подсеть моего VPS оказалась в блоклисте :)


Самое смешное что API РосКомСвободы блокирует запросы с дефолтным юзер-агентом Питона. Видать скрипт-кидди достали. Поэтому меняем его на Огнелиса.


Пока что он работает только с IPv4 т.к. доля IPv6 невелика, но это будет легко исправить. Разве что придется использовать еще и bird6.


rkn.py
#!/usr/bin/python3

import json, urllib.request, ipaddress as ipa

url = 'https://api.reserve-rbl.ru/api/v2/ips/json'
pfx = '24'

dont_summarize = {
    # ipa.IPv4Network('1.1.1.0/24'),
}

dont_add = {
    # ipa.IPv4Address('1.1.1.1'),
}

req = urllib.request.Request(
    url,
    data=None, 
    headers={
        'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.47 Safari/537.36'
    }
)

f = urllib.request.urlopen(req)
ips = json.loads(f.read().decode('utf-8'))

prefix32 = ipa.IPv4Address('255.255.255.255')

r = {}
for i in ips:
    ip = ipa.ip_network(i)
    if not isinstance(ip, ipa.IPv4Network):
        continue

    addr = ip.network_address

    if addr in dont_add:
        continue

    m = ip.netmask
    if m != prefix32:
        r[m] = [addr, 1]
        continue

    sn = ipa.IPv4Network(str(addr) + '/' + pfx, strict=False)

    if sn in dont_summarize:
        tgt = addr
    else:
        tgt = sn

    if not sn in r:
        r[tgt] = [addr, 1]
    else:
        r[tgt][1] += 1

o = []
for n, v in r.items():
    if v[1] == 1:
        o.append(str(v[0]) + '/32')
    else:
        o.append(n)

for k in o:
    print(k)

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


Делает следующее:


  • Запускает первый скрипт и обновляет список маршрутов (rkn_routes.list) для BIRD
  • Релоадит BIRD
  • Обновляет и подчищает список доменов для dnstap-bgp
  • Релоадит dnstap-bgp

rkn_update.sh
#!/bin/bash

ROUTES="/etc/bird/rkn_routes.list"
DOMAINS="/var/cache/rkn_domains.txt"

# Get & summarize routes
/opt/rkn.py | sed 's/\(.*\)/route \1 via "ens3";/' > $ROUTES.new

if [ $? -ne 0 ]; then
    rm -f $ROUTES.new
    echo "Unable to download RKN routes"
    exit 1
fi

if [ -e $ROUTES ]; then
    mv $ROUTES $ROUTES.old
fi

mv $ROUTES.new $ROUTES

/bin/systemctl try-reload-or-restart bird

# Get domains
curl -s https://api.reserve-rbl.ru/api/v2/domains/json -o - | jq -r '.[]' | sed 's/^\*\.//' | sort | uniq > $DOMAINS.new

if [ $? -ne 0 ]; then
    rm -f $DOMAINS.new
    echo "Unable to download RKN domains"
    exit 1
fi

if [ -e $DOMAINS ]; then
    mv $DOMAINS $DOMAINS.old
fi

mv $DOMAINS.new $DOMAINS

/bin/systemctl try-reload-or-restart dnstap-bgp

Они были написаны не особо задумываясь, поэтому если видите что можно улучшить — дерзайте.


Настройка клиента


Тут я приведу примеры для Linux-роутеров, но в случае Mikrotik/Cisco это должно быть еще проще.


Для начала настраиваем BIRD:


bird.conf
router id 192.168.1.2;
table rkn;

protocol device {
    scan time 10;
};

# Servers
protocol bgp bgp_server1 {
    table rkn;
    local as 65000;
    neighbor 192.168.1.1 as 65000;
    direct;
    bfd on;
    next hop self;
    graceful restart;
    graceful restart time 60;
    rr client;
    export none;
    import all;
}

protocol kernel {
    table rkn;
    kernel table 222;
    scan time 10;
    export all;
    import none;
}

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


После этого достаточно попросить ядро глядеть в эту табличку перед тем как заглядывать в дефолтную:


# ip rule add from all pref 256 lookup 222
# ip rule
0:  from all lookup local
256:    from all lookup 222
32766:  from all lookup main
32767:  from all lookup default

Всё, осталось настроить DHCP на роутере на раздачу туннельного IP-адреса сервера в качестве DNS и схема готова.


Недостатки


При текущем алгоритме формирования и обработки списка доменов в него попадает, в том числе, youtube.com и его CDNы.


А это приводит к тому что все видео будут ехать через VPN, что может забить весь канал. Возможно стоит составить некий список популярных доменов-исключений, которые блокировать у РКН пока что кишка тонка. И пропускать их при парсинге.


Заключение


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


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


Но для нужд обхода блокировок этого вполне достаточно.


Дополнения, правки, пуллреквесты — приветствуются!

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


  1. zzzzzzzzzzzz
    18.09.2019 17:37

    О, боги! Неужели это самый «легкий для далеких от ИТ личностей способ»?
    Чем для обхода блокировок не устраивает тор-браузер? Ставится «из коробки», скорость приемлемая, если видяшки не гонять. Ну и если гоняем всё (почти всё) «через зарубеж», то бонусом получаем минус десять к мыслям о КГБ-шной прослушке.


    1. blind_oracle Автор
      18.09.2019 17:49

      О, боги! Неужели это самый «легкий для далеких от ИТ личностей способ»?

      По-моему очевидно что под «далекими от ИТ людьми» подразумеваются те, кто будет пользоваться этой схемой, а не те, кто ее будет настраивать. Для них просто все будет работать как до блокировок, никаких TOR не надо.


      1. zzzzzzzzzzzz
        18.09.2019 18:47

        Само собой. Но всё равно это не «сделал и забыл». Могут быть разные ситуации: у людей сгорел роутер, заменили на новый — не работает (а в вашем случае ещё и обретение доступа к новому роутеру из-за границы может добавить веселья), забыли оплатить VPS — не работает, и т.д. И я не понимаю, какие преимущества это даёт по сравнению с тем же тор-браузером (кроме скорости) или прокси-включающим расширением браузера или ещё чем-то подобным. Хотя такие глубины админства впечатляют.


        1. blind_oracle Автор
          18.09.2019 18:52

          у людей сгорел роутер, заменили на новый — не работает (а в вашем случае ещё и обретение доступа к новому роутеру из-за границы может добавить веселья)
          Это настолько редкое явление что я его не рассматриваю. За многие годы такое происходило может быть раз. Ну и получить доступ удаленно проблем никаких в наше время нет.

          И я не понимаю, какие преимущества это даёт по сравнению с тем же тор-браузером (кроме скорости)

          Главное преимущество — что об этом не нужно думать. А не вспоминать что чтобы зайти туда-то мне нужно лезть через Тор.


        1. Barabek
          18.09.2019 21:27

          Преимущество в том, что для конечного юзера все работает как будто не существует никакого РКН. С любого устройства в семье.


      1. xdimquax
        18.09.2019 21:32

        Есть множество решений при которых у пользователей все будет работать прозрачно. А вот эффективность и сложность настройки/поддержки у всех разная.
        Вот, к примеру, чем ваше решение лучше использования dnsmasq и ipset, как это описано тут? Кстати последняя версия unbound тоже умеет работать с ipset.


        1. blind_oracle Автор
          18.09.2019 23:10

          Вот, к примеру, чем ваше решение лучше использования dnsmasq и ipset, как это описано тут?

          Как минимум тем, что от клиентского роутера почти ничего не нужно, кроме поддержки BGP и какого-либо VPN. Во все это любой копеечный микротик нынче умеет. А в решении с ipset всё ложится на локальный клиентский роутер.


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


          А в остальном всё примерно аналогично, да.


  1. ValdikSS
    18.09.2019 17:43
    +1

    Супер сложно. Я для VPN антизапрета написал специальный DNS-резолвер, который:

    1. Запрашивает A-записи домена у рекурсивного резолвера
    2. Получив ответ, устанавливает соответствие настоящим IP-адресам в большой внутренней приватной подсети (iptables … -d 10.224.0.1 -j DNAT --to 1.2.3.4)
    3. Отдаёт запросившему клиенту внутренний IP-адрес.

    У такого подхода есть множество преимуществ:

    • Клиенту устанавливается только один или несколько маршрутов, вместо десятков тысяч маршрутов;
    • Маршрутизируются только заблокированные домены, а не все сайты на заблокированном IP-адресе;
    • Возможность обновления списка заблокированных сайтов без переподключения клиента;
    • Корректная работа с доменами, постоянно меняющими IP-адреса, и с CDN-сервисами;
    • Корректная работа с провайдерами, блокирующими все поддомены заблокированного домена (блокировка всей DNS-зоны). Пример такого провайдера — Yota.


    Но есть и минусы:

    • Необходимо использовать только DNS-сервер внутри VPN. С другими DNS-серверами работать не будет.
    • Работает только для заблокированных доменов и программ, использующих доменные имена. Для заблокированных IP-адресов необходимо использовать обычную маршрутизацию.


    Перед резолвером стоит кэширующий knot-resolver, который перенаправляет запросы на этот резолвер только для заблокированных доменных зон. В принципе, можно всё реализовать средствами самого knot-resolver или powerdns, когда-нибудь перепишу и выложу.


    1. blind_oracle Автор
      18.09.2019 18:00

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

      Маршрутизируются только заблокированные домены, а не все сайты на заблокированном IP-адресе
      Да, это хорошо, но с моей точки зрения несущественно. Если какая-то часть доменов на том же IP пойдет тоже через VPN — ну и пусть.

      Возможность обновления списка заблокированных сайтов без переподключения клиента
      Тут не понял. Что такое «переподключение клиента»? У меня список обновляет по крону в фоне на сервере и клиенты там никак не участвуют.

      Корректная работа с доменами, постоянно меняющими IP-адреса, и с CDN-сервисами
      Не вижу проблем с этим в моей схеме. Если TTL в DNS протух и домен отрезолвился в новый IP — маршрут до него будет добавлен. Старый IP протухнет со временем. Эту схему можно, конечно, немного улучшить активно убивая старые IP, но если там Round-Robin DNS с низким TTL то IP будут постоянно добавляться-удаляться.

      Корректная работа с провайдерами, блокирующими все поддомены заблокированного домена (блокировка всей DNS-зоны). Пример такого провайдера — Yota
      У меня этот кейс тоже срабатывает и даже описан в статье.


      1. ValdikSS
        18.09.2019 18:15

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

        Тут все ровно так же. Сотня тысяч маршрутов только для IP-блокировок, иначе никак.
        А на антизапрете — пара десятков маршрутов (большой внутренний диапазон и самые крупные заблокированные диапазоны).


        1. blind_oracle Автор
          18.09.2019 18:58

          А на антизапрете — пара десятков маршрутов (большой внутренний диапазон и самые крупные заблокированные диапазоны).

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

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


          1. ValdikSS
            18.09.2019 19:01

            На данный момент — никак, но реализовать это просто: если в DNS-ответе есть A-запись на заблокированный IP-адрес, то ему тоже нужно установить соответствие, а клиенту отдать адрес из внутреннего диапазона.


            1. blind_oracle Автор
              18.09.2019 19:17

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


              Но тут для мобильных клиентов, конечно, без вариантов. Пушить 100к маршрутов через OpenVPN чтобы покрыть 0.1% юзкейсов так себе идея :)


              А для домашних роутеров современных эти маршруты не проблема ни разу.


              1. XanKraegor
                19.09.2019 01:24

                Можно немного дополнить Вашу схему, устранив ряд минусов.
                Мобильные клиенты могут быть Road warrior'ами через туннель до хорошего такого гейта поблизости, которому не сложно переварить и миллион BGP-маршрутов. Даже не особо приходится заморачиваться с тем, чтобы какой-нибудь IPSec c IKEv2 был всегда включен на мобильном клиенте. Да, оверхед на лишний туннель, но если туннель заворачивается в UDP, он небольшой, да и дополнительный слой безопасности для мобильного устройства не помешает.
                Там же, рядом с гейтом (в границах своей сети поблизости), лучше и размещать Unbound: быстрее будет отрабатывать bgp-tap. Не могу ручаться, где скорость работы самого DNS будет больше, при DNS-over-TLS Unbound или при маршрутизации обычного DNS-трафика через туннель до заграницы, но практика показывает, что первый вариант работает достаточно быстро и стабильно. Еще и не придется заморачиваться с неймспейсом, так как второй Bird можно будет поднять там же и обрабатывать его префиксы отдельно от тех, что присылает Bird с VPS. Минус — в необходимости наличия еще одного устройства кроме роутера, но подойдут и совсем недорогие типа RPi.


          1. ValdikSS
            18.09.2019 19:08
            +1

            Как абстрактное клиентское устройство с OpenVPN клиентом должно понять что ему нужно отправлять траффик в VPN, а не напрямую?


            Клиент подключается к VPN. VPN-сервер ему устанавливает маршрут 10.224.0.1/16 и DNS-сервер внутри VPN.

            Клиент запрашивает rutracker.org.
            image
            Клиент маршрутизирует трафик в VPN, потому что у него установлен маршрут 10.224.0.1/16.
            Для незаблокированных сайтов DNS-сервер отдаёт настоящие IP-адреса, и трафик не маршрутизируется в VPN.


            1. blind_oracle Автор
              18.09.2019 19:12

              Про домены то понятно, я имел в виду IP блокировки.


              1. ValdikSS
                18.09.2019 19:14
                +1

                Аналогично — если в DNS-ответе незаблокированного домена A-запись указывает на заблокированный IP-адрес, то тоже устанавливаем ему соответствие и отдаём клиенту адрес из внутренней сети.
                Такой подход не сработает при обращении к IP-адресу напрямую, не через DNS, но такие случаи редки.


  1. poige
    18.09.2019 20:05

    > траффик

    нет.


  1. Barabek
    18.09.2019 21:59

    В целом серьезно все. Но не очень, когда весь DNS приходится гнать по vpn на 5-баксовый сервер без sla. Узкое горлышко и звено ненадежности. А не думали пускать трафик через VPN только в том случае, когда от провайдера приходит явный признак блока (характерная страница или отсутствие ответа)? Ведь реальному пользователю из сотен тысяч забаненных адресов и хостов нужны не более десятка.


    1. blind_oracle Автор
      18.09.2019 23:16

      Но не очень, когда весь DNS приходится гнать по vpn на 5-баксовый сервер без sla

      Да вроде не мешает ни разу. DNS траффик копеечный, пинг около 50мс, ответы кешируются. Я поднял два VPS в разных местах Европы и если один падает второй возьмет бразды правления — на клиентах задан BGP preference чтобы один из них был приоритетнее.


      А не думали пускать трафик через VPN только в том случае, когда от провайдера приходит явный признак блока (характерная страница или отсутствие ответа)?

      Алгоритм детектирования этого дела довольно сложный думается, поэтому проще сделать кучу маршрутов. Тем более что эти ничем не напрягает оборудование особо. По крайней мере мои ARM-роутеры с OpenWRT 100-200к маршрутов даже не замечают.


  1. Alexey2005
    18.09.2019 22:26

    Вот ещё бы найти хостера, который разрешает VPN. А то сейчас чуть не у всех, даже иностранных, хостеров в лицензионном соглашении прописан запрет поднимать прокси/VPN.


    1. blind_oracle Автор
      18.09.2019 23:18

      Ну, мало ли что там написано :) Я использую несколько разных VPS-провайдеров, никто пока не жаловался. На одном даже выходная нода TOR висит, абузы сыпятся, отвечаю что TOR, провайдера устраивает.


      1. xdimquax
        18.09.2019 23:31

        Дело полезное, но не боитесь, что IP будет на некоторых ресурсах заблокирован (полностью или частично), и использование VPN превратится в боль?


        1. blind_oracle Автор
          18.09.2019 23:44

          Само собой боюсь :) Поэтому VPN у меня через другие VPS, где Tor только скрытым бриджом есть.


  1. xdimquax
    18.09.2019 23:43

    попадает, в том числе, youtube.com и его CDNы.

    Но ютуб не блокируют по IP, его блочат при помощи DPI (тип блокировки по URL), следовательно, для обхода блокировки достаточно обмануть DPI.


    1. blind_oracle Автор
      18.09.2019 23:45

      Ютуб никак не блочат т.к. он наглухо в HTTPS и доступа к URL нет ни у какого DPI. Поэтому его можно просто использовать без VPN.


      1. xdimquax
        19.09.2019 00:12

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


  1. mayorovp
    19.09.2019 09:01

    Неужели BGP настраивается проще чем OSPF или вообще какой-нибудь RIP?


    1. blind_oracle Автор
      19.09.2019 09:36

      Минимальная конфигурация, как мне кажется, даже проще чем OSPF — не нужны никакие area и прочее. Ну и BGP работает по TCP и можно пириться хоть через полмира даже без тоннелей, если необходимо. А OSPF более капризный — мультикаст ему подавай...


      Я использую оба, у каждого протокола свое применение — BGP больше EGP, а OSPF — в основном IGP.


    1. vp7
      19.09.2019 11:24

      Да, проще.


      OSPF хочет мультикасты, в BGP прописываем пиров и всё.


      А ещё в BGP можно легко менять next-hop для анонсируемях маршрутов и это заложено в протокол by design всеми производителями софта/оборудования с поддержкой BGP (bgp peer не обязан быть гейтвеем), а вот про тот же OSPF есть сомнения (хотя могу и ошибаться).


  1. chelsea2
    19.09.2019 09:37
    -1

    Бороться таким способом с РКН все-равно что играть на приставке пока батя на заводе.
    Tor не во всех организациях разрешен. Самый выгодный вариант — купить линуховую vps, напр. за 5$ в azure и гонять траффик через нее.


  1. unwrecker
    19.09.2019 16:06

    А имеет ли смысл сейчас делать разделение по доменам, если в браузерах вот-вот будет DoH?
    Я гоню всё через заграничный VPS с помощью прозрачного socks-прокси на shadowsocks.


    1. blind_oracle Автор
      19.09.2019 16:15

      А как связан DoH с тем что блокируется провайдерам?


      DoH не позволит им юзать перехват DNS для подсовывания своих блокстраничек вместо правильного IP и все.


      В итоге будут бить по площадям, ай ай ай от Ревизора то никто не хочет получить.


      Я гоню всё через заграничный VPS с помощью прозрачного socks-прокси на shadowsocks.

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