Настройка BGP для обхода блокировок, или «Как я перестал бояться и полюбил РКН»

Как вы все знаете, с 16 апреля 2018 года Роскомнадзор крайне широкими мазками блокирует доступ к ресурсам в сети, добавляя в "Единый реестр доменных имен, указателей страниц сайтов в сети «Интернет» и сетевых адресов, позволяющих идентифицировать сайты в сети «Интернет», содержащие информацию, распространение которой в Российской Федерации запрещено" (по тексту — просто реестр) по /10 иногда. В результате граждане Российской Федерации и бизнес страдают, потеряв доступ к необходимым им совершенно легальным ресурсам.


После того, как в комментариях к одной из статей на Хабре я сказал, что готов помочь пострадавшим с настройкой схемы обхода, ко мне обратились несколько человек с просьбой о такой помощи. Когда у них всё заработало, один из них порекомендовал описать методику в статье. Поразмыслив, решил нарушить свое молчание на сайте и попробовать в кои-то веки написать что-то промежуточное между проектом и постом в Facebook, т.е. хабрапост. Результат — перед вами.


Disclaimer


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


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


TL;DR


Автоматизируем доступ к ресурсам через существующий у вас туннель, используя копию реестра и протокол BGP. Цель — убрать весь трафик, адресованный заблокированным ресурсам, в туннель. Минимум объяснений, в основном пошаговая инструкция.


Что вам для этого потребуется


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


  1. У вас должен быть linux-сервер где-то за пределами поля блокировок. Или хотя бы желание такой сервер завести — благо это сейчас стоит от $9/год, а возможно и меньше. Метод также подходит, если у вас есть отдельный VPN-туннель, тогда сервер может располагаться и внутри поля блокировок.
  2. Ваш роутер должен быть достаточно умным, чтобы уметь
    • любой нравящийся вам VPN-клиент (я предпочитаю OpenVPN, но это может быть PPTP, L2TP, GRE+IPSec и любой другой вариант, создающий туннельный интерфейс);
    • протокол BGPv4. Что означает, что для SOHO это может быть Mikrotik или любой роутер с OpenWRT/LEDE/аналогичными кастомными прошивками, позволяющими установить Quagga или Bird. Использование PC-роутера также не возбраняется. В случае энтерпрайза смотрите поддержку BGP в документации к вашему бордер-роутеру.
  3. Вы должны иметь представление о использовании Linux и сетевых технологиях, в том числе о протоколе BGP. Или хотя бы хотеть получить такое представление. Поскольку объять необъятное в этот раз я не готов, некоторые непонятные для вас моменты вам придется изучить самостоятельно. Впрочем, на конкретные вопросы, конечно же, отвечу в комментариях и вряд ли окажусь единственным отвечающим, так что не стесняйтесь спрашивать.

Что используется в примере


  • Копия реестра — из https://github.com/zapret-info/z-i
  • VPS — Ubuntu 16.04
  • Сервис маршрутизации — bird 1.6.3
  • Маршрутизатор — Mikrotik hAP ac
  • Рабочие папки — поскольку работаем от рута, бОльшая часть всего будет размещаться в домашней папке рута. Соответственно:
    • /root/blacklist — рабочая папка со скриптом компиляции
    • /root/z-i — копия реестра с github
    • /etc/bird — стандартная папка настроек сервиса bird
  • Внешним IP-адресом VPS с сервером маршрутизации и точкой терминации туннеля принимаем 194.165.22.146, ASN 64998; внешним IP-адресом роутера — 81.177.103.94, ASN 64999
  • IP адреса внутри туннеля — 172.30.1.1 и 172.30.1.2 соответственно.

image


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


Кратко — логика решения


  1. Подготовительные действия
    1. Получаем VPS
    2. Поднимаем туннель от маршрутизатора к VPS
  2. Получаем и регулярно обновляем копию реестра
  3. Устанавливаем и настраиваем сервис маршрутизации
  4. Создаем на основании реестра список статических маршрутов для сервиса маршрутизации
  5. Подключаем роутер к сервису и настраиваем отправку всего трафика через туннель.

Собственно решение


Подготовительные действия


На просторах сети есть множество сервисов, предоставляющих VPS за крайне умеренные деньги. Пока я нашел и пользуюсь вариантом за $9/год, но даже если не особо заморачиваться — вариантов за 1E/месяц много на каждом углу. Вопрос выбора VPS лежит далеко за пределами этой статьи, поэтому если кому-то что-то непонятно в этом — спросите в комментариях.


Если вы будете использовать VPS не только для сервиса маршрутизации, но и для терминации на нем туннеля — вам нужно поднять этот туннель и, практически однозначно, настроить NAT для него. В сети большое количество инструкций по этим действиям, здесь я их повторять не буду. Основное требование к такому туннелю — он должен создавать на вашем роутере, поддерживающем туннель в сторону VPS, отдельный интерфейс. Этому требованию соответствует большинство используемых технологий VPN — например, OpenVPN в tun режиме прекрасно подходит.


Получение копии реестра


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


Заходим на ваш линукс-сервер, проваливаемся в контекст root'а (sudo su -) и устанавливаем git, если он еще не установлен.


apt install git

Переходим в домашнюю директорию и вытягиваем копию реестра.


cd ~ && git clone --depth=1 https://github.com/zapret-info/z-i 

Настраиваем обновление по крону (у меня раз в 20 минут, но вы можете выбрать любой интересный вам интервал). Для этого запускаем crontab -e и добавляем в него следующую строку:


*/20 * * * * cd ~/z-i && git pull && git gc

Подключаем хук, который будет создавать файлы для сервиса маршрутизации после обновления реестра. Для этого создаем файл /root/z-i/.git/hooks/post-merge со следующим содержимым:


#!/usr/bin/env bash
changed_files="$(git diff-tree -r --name-only --no-commit-id ORIG_HEAD HEAD)"
check_run() {
    echo "$changed_files" | grep --quiet "$1" && eval "$2"
}
check_run dump.csv "/root/blacklist/makebgp"

и не забываем сделать его исполняемым


chmod +x /root/z-i/.git/hooks/post-merge

Скрипт makebgp, на который ссылается хук, мы создадим чуть позже.


Установка и настройка сервиса маршрутизации


Устанавливаем bird. К сожалению, выложенная в репозиториях Ubuntu на данный момент версия bird по свежести сопоставима с фекалиями археоптерикса, поэтому нам необходимо предварительно добавить в систему официальный PPA разработчиков ПО.


add-apt-repository ppa:cz.nic-labs/bird
apt update
apt install bird

После этого сразу отключаем bird для IPv6 — в этой инсталляции он для нас не потребуется.


systemctl stop bird6
systemctl disable bird6

Ниже приведен минималистичный файл конфигурации сервиса bird (/etc/bird/bird.conf), которого нам вполне хватит (и еще раз напоминаю, что никто не запрещает развивать и дотюнивать идею под ваши собственные потребности)


log syslog all;
router id 172.30.1.1;

protocol kernel {
        scan time 60;
        import none;
#       export all;   # Actually insert routes into the kernel routing table
}

protocol device {
        scan time 60;
}

protocol direct {
        interface "venet*", "tun*"; # Restrict network interfaces it works with
}

protocol static static_bgp {
        import all;
        include "pfxlist.txt";
        #include "iplist.txt";
}

protocol bgp OurRouter {
        description "Our Router";
        neighbor 81.177.103.94 as 64999;
        import none;
        export where proto = "static_bgp";
        local as 64998;
        passive off;
        multihop;
}

router id — идентификатор роутера, визуально выглядящий как IPv4-адрес, но им не являющийся. В нашем случае может быть вообще любым 32-битным числом в формате IPv4-адреса, но хорошим тоном является указывать там именно IPv4-адрес вашего устройства (в данном случае VPS).


protocol direct определяет, какие интерфейсы будут работать с процессом маршрутизации. В примере дана пара примеров имен, можно добавлять и другие. Можно и просто удалить строку, в этом случае сервер будет слушать все доступные интерфейсы с IPv4-адресом.


protocol static — наше волшебство, которое подгружает из файлов списки префиксов и ip-адресов (которые на самом деле, конечно, префиксы по /32) для последующего анонса. Откуда появляются эти списки — будет рассмотрено ниже. Обратите внимание, что загрузка ip-адресов по умолчанию закомментирована, причина этого — большой объем выгрузки. Для сравнения, в списке префиксов на момент написания статьи 78 строк, а в списке ip-адресов — 85898. Настоятельно рекомендую запускаться и отлаживаться только на списке префиксов, а включать или не включать в дальнейшем подгрузку ip — решать после экспериментов со своим роутером. Далеко не каждый из них легко переварит 85 тысяч записей в таблице маршрутизации.


protocol bgp, собственно, настраивает bgp-пиринг с вашим роутером. ip-адрес — это адрес внешнего интерфейса роутера (либо адрес интерфейса туннеля со стороны роутера), 64998 и 64999 — номера автономных систем. Их в данном случае можно назначить в виде любых 16-битных чисел, но хорошим тоном является использование номеров AS из приватного диапазона, определенного RFC6996 — 64512-65534 включительно (существует формат 32-битных ASN, но в нашем случае это точно излишество). Описанная конфигурация использует eBGP пиринг, при котором номера автономных систем сервиса маршрутизации и роутера должны различаться.


Как вы можете заметить, сервису нужно знать IP-адрес роутера, поэтому если у вас динамический или немаршрутизируемый private (RFC1918) или shared (RFC6598) адрес, варианта поднимать пиринг на внешнем интерфейсе у вас нет, но внутри туннеля сервис все равно будет работать.


Достаточно прозрачно также, что с одного сервиса вы можете обеспечивать маршрутами несколько разных роутеров — достаточно продублировать настройки для них копированием секции protocol bgp со сменой IP-адреса соседа. Именно поэтому в примере показаны настройки для пиринга вне туннеля, как наиболее универсальные. Убрать их в туннель несложно, поменяв соответственно IP-адреса в настройках.


Обработка реестра для сервиса маршрутизации


Сейчас нам надо, собственно, создать списки префиксов и ip-адресов, которые на предыдущем этапе упомянуты в protocol static. Для этого мы берем файл реестра и делаем из него нужные нам файлы следующим скриптом, размещаемым в /root/blacklist/makebgp


#!/bin/bash
cut -d";" -f1 /root/z-i/dump.csv| tr '|' '\n' |  tr -d ' ' > /root/blacklist/tmpaddr.txt
cat /root/blacklist/tmpaddr.txt | grep / | sed 's_.*_route & reject;_' > /etc/bird/pfxlist.txt
cat /root/blacklist/tmpaddr.txt | sort | uniq | grep -Eo "([0-9]{1,3}[\.]){3}[0-9]{1,3}" | sed 's_.*_route &/32 reject;_' > /etc/bird/iplist.txt
/etc/init.d/bird reload
logger 'bgp list compiled'

Не забываем сделать его исполняемым


chmod +x /root/blacklist/makebgp

Теперь можно запустить его вручную и пронаблюдать появление файлов в /etc/bird.


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


systemctl start bird
birdc show route

Вывод второй команды должен показать около 80 записей (это на данный момент, а когда будете настраивать, всё будет зависеть от рьяности РКН в блокировках сетями) вида приблизительно такого:


54.160.0.0/12      unreachable [static_bgp 2018-04-19] * (200)

Команда


birdc show protocol

покажет состояние протоколов внутри сервиса. Пока вы не настроили роутер (см. следующий пункт), протокол OurRouter будет в состоянии start (фазы Connect или Active), а после успешного подключения перейдет в состояние up (фаза Established). Например, в моей системе вывод этой команды выглядит следующим образом:


BIRD 1.6.3 ready.
name     proto    table    state  since       info
kernel1  Kernel   master   up     2018-04-19
device1  Device   master   up     2018-04-19
static_bgp Static   master   up     2018-04-19
direct1  Direct   master   up     2018-04-19
RXXXXXx1 BGP      master   up     13:10:22    Established
RXXXXXx2 BGP      master   up     2018-04-24  Established
RXXXXXx3 BGP      master   start  2018-04-22  Connect       Socket: Connection timed out
RXXXXXx4 BGP      master   up     2018-04-24  Established
RXXXXXx5 BGP      master   start  2018-04-24  Passive

Подключение роутера


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


Однако могу показать пару примеров. Основная логика — поднять BGP-пиринг и на все получаемые префиксы навесить nexthop, указывающий на наш туннель (если нужно выводить трафик через p2p интерфейс) или ip-адрес некстхопа, если трафик будет уходить в ethernet).


Например, на Mikrotik в RouterOS это решается следующим образом


/routing bgp instance set default as=64999 ignore-as-path-len=yes router-id=172.30.1.2
/routing bgp peer add in-filter=dynamic-in multihop=yes name=VPS remote-address=194.165.22.146 remote-as=64998 ttl=default
/routing filter add action=accept chain=dynamic-in protocol=bgp comment="Set nexthop" set-in-nexthop=172.30.1.1

а в Cisco IOS — вот так


router bgp 64999
  neighbor 194.165.22.146 remote-as 64998
  neighbor 194.165.22.146 route-map BGP_NEXT_HOP in
  neighbor 194.165.22.146 ebgp-multihop 250
!
route-map BGP_NEXT_HOP permit 10
  set ip next-hop 172.30.1.1

В том случае, если один и тот же туннель используется и для BGP-пиринга, и для передачи полезного трафика, выставлять nexthop не обязательно, он выставится правильно средствами протокола. Но если выставите вручную — хуже от этого тоже не будет.


На других платформах вам придется разбираться в конфигурировании самостоятельно, но если возникнут затруднения — пишите в комментариях, попробую помочь.


После того, как у вас поднялась BGP-сессия, прилетели и установились в таблицу маршруты на крупные сети, трафик на адреса из них пошел и счастье близко, вы можете вернуться к сервису bird и попробовать раскомментировать там запись, подключающую список ip-адресов, выполнить после этого


systemctl reload bird

и посмотреть, как ваш роутер перенес эти 85 тысяч маршрутов. Будьте готовы отключать и думать, что с этим делать :)


Итого


Чисто теоретически после выполнения вышеописанных действий у вас появился сервис, который автоматически перенаправляет трафик к забаненным в РФ IP-адресам мимо системы фильтрации.


Его, конечно, можно дорабатывать. Например, достаточно легко сделать суммаризацию списка ip-адресов через решения на perl или python. Простой скрипт на perl, делающий это с помощью Net::CIDR::Lite, превращает 85 тысяч префиксов в 60 (не тысяч), но, естественно, перекрывает гораздо бОльший диапазон адресов, чем заблокировано.


Поскольку сервис работает на третьем уровне модели ISO/OSI, он не спасет от блокировок по сайту/странице, если оно резолвится не в тот адрес, который записан в реестре. Но вместе с реестром из github прилетает файл nxdomain.txt, который несколькими штрихами скрипта легко превращается в источник адресов для, например, плагина SwitchyOmega в Chrome.


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


При возникновении вопросов — задавайте, готов отвечать.


UPD. Спасибо navion и TerAnYu за параметры для git, позволяющие уменьшить объемы скачиваемого.


UPD2. Коллеги, похоже, я совершил ошибку, не добавив в статью инструкцию по настройке туннеля между VPS и роутером. Очень много вопросов вызваны этим.
На всякий случай еще раз отмечу — подразумевается, что перед началом действий по этому руководству вы уже настроили VPN-туннель в нужном вам направлении и проверили его работоспособность (например, завернув туда трафик по дефолту или статикой). Если эта фаза у вас еще не выполнена, выполнять действия из статьи не очень имеет смысл. Пока у меня своего текста на этот счет нет, но если погуглить "настройка сервера OpenVPN" вместе с именем операционной системы, установленной на VPS, и "настройка клиента OpenVPN" с именем вашего маршрутизатора — вероятнее всего, вы найдете некоторое количество статей на этот счет, в том числе и на хабре.


UPD3. Unsacrificed написал код, который делает из dump.csv результирующий файл для bird с опциональной суммаризацией ip-адресов. Поэтому раздел "Обработка реестра для сервиса маршрутизации" можно заменить на вызов его программы. https://habr.com/post/354282/#comment_10782712


UPD4. Небольшая работа над ошибками (не вносил по тексту):
1) вместо systemctl reload bird имеет смысл использовать команду birdc configure.
2) в маршрутизаторе Mikrotik вместо смены некстхопа на IP второй стороны туннеля /routing filter add action=accept chain=dynamic-in protocol=bgp comment="Set nexthop" set-in-nexthop=172.30.1.1 имеет смысл указывать маршрут прямо в интерфейс туннеля, без адреса /routing filter add action=accept chain=dynamic-in protocol=bgp comment="Set nexthop" set-in-nexthop-direct=<имя интерфейса>


UPD5. Появился новый сервис https://antifilter.download, откуда можно забирать уже готовые списки ip-адресов. Обновляются раз в полчаса. На клиентской стороне при этом остается только обрамить записи соответствующими "route… reject".
И на этом, наверное, уже хватит лохматить бабушку и обновлять статью.


Who's online

There are currently 1 user and 1 guest online.