Точечный обход блокировок PKH на роутере с OpenWrt с помощью WireGuard и DNSCrypt

в 13:36, , рубрики: dnscrypt, linux, OpenWrt, wireguard, обход блокировок, ркн, Сетевые технологии, системное администрирование

Чем отличается от подобных материалов?

  • Реализация на чистом OpenWrt
  • Использование WireGuard
  • Конфигурация роутера организуется с помощью конфигов OpenWrt, а не кучей в одном скрипте
  • Предусмотрены ситуации при рестарте сети и перезагрузке
  • Потребляет мало ресурсов роутера: заблокированные подсети содержатся в iptables, а не в таблицах маршрутизации. Что позволяет развернуть это дело даже на слабых устройствах
  • Автоматизация конфигурации с помощью Ansible (не требуется python на роутере)

Видеоверсия

Почему OpenWrt и WireGuard?

OpenWrt ставится на очень много моделей soho роутеров, конфигурируется и расширяется как душа пожелает. Сейчас многие прошивки роутеров — это надстройки над OpenWrt.

Wireguard используется из-за его быстрой и простой настройки, а так же из-за высокой скорости передачи через туннель.

Немного о WireGuard

В нашем случае сервер — это VPS вне РКН, клиент — OpenWrt роутер дома. Когда вы захотите зайти на pornolab telegram, ваш роутер направит трафик через сервер с WireGuard.
WireGuard поднимает site-to-site соединение, т.е. и у сервера и у клиента имеется серверная и клиентская часть конфигурации. Если не понятно — станет понятно когда увидите конфигурацию.

У сервера и у клиента есть свои собственные приватный и публичный ключи.

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

Я проделываю всё на Ubuntu 18.04, но в официальной документации есть инструкции по установке для всех известных и не очень ОС.

Установка

sudo add-apt-repository ppa:wireguard/wireguard

При возникновении ошибки

sudo: add-apt-repository: command not found

Установите software-properties-common — пакет предоставляет возможность добавления и удаления PPA

sudo apt install software-properties-common

sudo apt update
sudo apt install wireguard-dkms wireguard-tools

Генерируем ключи для сервера. Ключи сохраним в директории WireGuard для удобства

cd /etc/wireguard/
wg genkey | tee privatekey-server | wg pubkey > publickey-server

Соответственно в файле privatekey-server будет приватный ключ, а в publickey-server — публичный.
Так же сгенерируем сразу ключ для клиента:

wg genkey | tee privatekey-client | wg pubkey > publickey-client

Точечный обход блокировок PKH на роутере с OpenWrt с помощью WireGuard и DNSCrypt - 1

Конфигурация

Конфиг хранится в /etc/wireguard/wg0.conf. Серверная часть выглядит так:

[Interface]
Address = 192.168.100.1
PrivateKey = privatekey-server
ListenPort = 51820
PostUp   = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o ens3 -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o ens3 -j MASQUERADE

Address — адрес для интерфейса wg (адрес внутри туннеля)
PrivateKey — Приватный ключ (privatekey-server)
ListenPort — Порт на котором служба ожидает подключения

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

Клиентская часть

[Peer]
PublicKey = publickey-client
AllowedIPs = 192.168.100.3/24

PublicKey — публичный ключ нашего роутера (publickey-client)
AllowedIPs — подсети, которые будут доступны через этот туннель. Серверу требуется доступ только до адреса клиента.

Обе части хранятся в одном конфиге.

Включаем автозапуск при перезагрузке:

systemctl enable wg-quick@wg0

Делаем сервер маршрутизатором:

sysctl -w net.ipv4.ip_forward=1

Настроим фаервол. Предположим, что у нас на сервере только WireGuard и ssh:

sudo iptables -A INPUT -i lo -j ACCEPT
sudo iptables -A INPUT -p udp -m udp --dport 51820 -j ACCEPT
sudo iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
sudo iptables -A INPUT -p icmp -j ACCEPT
sudo iptables -A INPUT -p tcp -m tcp --dport 22 -j ACCEPT
sudo iptables -A INPUT -j DROP

Сохраним конфигурацию iptables:

sudo apt-get install iptables-persistent
sudo netfilter-persistent save

Поднимаем wg интерфейс первый раз вручную:

wg-quick up wg0

Точечный обход блокировок PKH на роутере с OpenWrt с помощью WireGuard и DNSCrypt - 2

WireGuard сервер готов.

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

Я использую OpenWrt версии 18.06.1 на Xiaomi mi 3G и Asus RT-N16.

Логика работы роутера

Загружаем списки, помещаем их в iptables, все адреса из этих списков iptables помечает маркером 0x1. Далее все пакеты помеченные 0x1 идут в отдельную таблицу маршрутизации, все пакеты попавшие в эту таблицу маршрутизации идут через wg интерфейс.

Точечный обход блокировок PKH на роутере с OpenWrt с помощью WireGuard и DNSCrypt - 3

Установка пакетов

Насчет занимаемого места на флеше, на всё понадобится примерно 0.9МБ. Если у вас совсем плохо с местом, замените curl wget'ом и можете не ставить dnscrypt-proxy.

Ставим пакеты. В OpenWrt это просто сделать через менеджер пакетов opkg:

opkg update
opkg install ipset wireguard curl

Загрузка списков

Всё, что можно сделать через стандартные возможности OpenWrt, сделано через них. Всё остальное (кроме hotplug) я поместил в небольшой скрипт:

#!/bin/sh

dir=/tmp/lst

mkdir -p $dir

echo "Run download lists"
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 "Firewall restart"
/etc/init.d/firewall restart

Списки запрещенных подсетей и адресов получаем файлами. Для них создаём директорию в /tmp. В /tmp — потому что это RAM, такая особенность OpenWrt, довольно удобная. На ROM роутера что-то писать лишний раз не стоит.

Выкачиваем списки с antifilter.download curl'ом, флаг z означает, что curl будет скачивать файл, только если удаленный файл отличается от локального или если его нет, как например в случае при загрузке роутера.

subnet.lst — список заблокированных подсетей, изменяется не часто.
ipsum.lst — список заблокированных адресов, который суммаризирован по маске. Вместо 150 тысяч записей получаем 15 тысяч — удобно.

После того как файлы у нас — рестартуем firewall, это нужно для того что бы ipset отработал и добавил списки в iptables, ipset у нас будет сконфигурен в /etc/config/firewall.

Скрипт этот мы добавляем в /etc/init.d/ назовём hirkn. Сделаем его исполняемым

chmod +x /etc/init.d/hirkn

Теперь у нас не просто скрипт, а целая служба. Для того, что бы он запускался при загрузке, делаем симлинк в /etc/rc.d. Нам нужно, что бы он запускался после всех остальных служб, поэтому делаем приставку S99

ln -s /etc/init.d/hirkn /etc/rc.d/S99hirkn

Списки нужно обновлять время от времени, добавляем запись в cron:

crontab -e

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

Мне кажется вполне достаточным обновлять их раз в сутки. Имейте в виду, что при добавлении списков в ipset, отваливается сеть, в моём случае это 2 секунды.

Так же включите крон, по дефолту он отключен:

/etc/init.d/cron enable
/etc/init.d/cron start

Конфигурация таблицы маршрутизации

Создаем таблицу маршрутизации для трафика через туннель, просто добавив строку:

99  vpn

в файл /etc/iproute2/rt_tables.

Создать дефолтный маршрут для таблицы "vpn" через wg интерфейс можно командой:

ip route add table vpn default dev wg0

Но при рестарте сети маршрут пропадёт, поэтому создаём файл 30-rknroute в директории /etc/hotplug.d/iface/ с простым содержимым:

#!/bin/sh

ip route add table vpn default dev wg0

Это означает, что при включениивыключении интерфейсов будет добавляться наш маршрут. И соответственно, этот маршрут будет всегда прописан.

Конфигурация сети

Нам необходимо сконфигурировать WireGuard и правило для пакетов с меткой 0x1.

Конфигурация WireGuard располагается в /etc/config/network

"Серверная" часть:

config interface 'wg0'
        option private_key 'privatekey-client'
        list addresses '192.168.100.3/24'
        option listen_port '51820'
        option proto 'wireguard'

private_key — это privatekey-client, который мы генерировали при настройке сервера
list addresses — адрес wg интерфейса
listen_port — порт на котором WireGuard принимает соединения. Но соединение будет происходить через порт на сервере, поэтому здесь мы не будем открывать для него порт на firewall
proto — указываем протокол, что бы openwrt понимало что это конфигурация WireGuard

"Клиентская" часть:

config wireguard_wg0
        option public_key 'publickey-server'
        option allowed_ips '0.0.0.0/0'
        option route_allowed_ips '0'
        option endpoint_host 'wg-server-ip'
        option persistent_keepalive '25'
        option endpoint_port '51820'

public_key — ключ publickey-server
allowed_ips — подсети, в которые может ходить трафик через тунель, в нашем случае никаких ограничей не требуется, поэтому 0.0.0.0/0
route_allowed_ips — флаг, который делает роут через wg интерфейс для перечисленных сетей из параметра allowed_ips. В нашем случае это не нужно, эту работу выполняет iptables
endpoint_host — ip/url нашего wg сервера
persistent_keepalive — интервал времени, через который отправляются пакеты для поддержки соединения
endpoint_port — порт wireguard на сервере

Ещё в конфигурацию network добавим правило, которое будет отправлять весь трафик, помеченный 0x1, в таблицу маршрутизации "vpn":

config rule
        option priority '100'
        option lookup 'vpn'
        option mark '0x1'

Конфигурация firewall

Добавим два правила маркировки пакетов, они не вписываются в синтаксис UCI openwrt, поэтому добавляем их "как есть" в /etc/firewall.user.

iptables -t mangle -A PREROUTING -i br-lan -m set --match-set vpn_subnets dst -j MARK --set-xmark 0x1
iptables -t mangle -A PREROUTING -i br-lan -m set --match-set vpn_ipsum dst -j MARK --set-xmark 0x1

Эти правила подразумевают под собой, что все пакеты идущие в подсети из списков vpn_subnets и vpn_ipsum необходимо помечать маркером 0x1.

Переходим непосредственно в конфигурацию фаервола в /etc/config/firewall.

Добавляем зону для wireguard. В openwrt зоны — это кастомные цепочки в iptables. Таким образом создаётся зона с однимнесколькими интерфейсами и уже на неё вешаются правила. Зона для wg выглядит например вот так:

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'

Мы разрешаем только выход трафика из интерфейса и включаем маскарадинг.

Теперь нужно разрешить переадресацию с lan зоны на wg зону:

config forwarding
        option src 'lan'
        option dest 'wg'

Ну и последнее — это формирование списков в iptables с помощью ipset:

config ipset
        option name 'vpn_subnets'
        option storage 'hash'
        option loadfile '/tmp/lst/subnet.lst'
        option match 'src_net'

config ipset
        option name 'vpn_ipsum'
        option storage 'hash'
        option loadfile '/tmp/lst/ipsum.lst'
        option match 'src_net'

loadfile — файл из которого берем список
name — имя для нашего списка
storage, match — здесь указываем как хранить и какой тип данных. Будем хранить тип "подсеть"

После этого рестартуем сеть:

/etc/init.d/network restart

Точечный обход блокировок PKH на роутере с OpenWrt с помощью WireGuard и DNSCrypt - 4

и запускаем скрипт:

/etc/init.d/hirkn

Точечный обход блокировок PKH на роутере с OpenWrt с помощью WireGuard и DNSCrypt - 5

После отработки скрипта у вас должно всё заработать. Проверьте маршрут на клиенте роутера:

mtr/traceroute telegram.org/linkedin.com

Точечный обход блокировок PKH на роутере с OpenWrt с помощью WireGuard и DNSCrypt - 6

Бонусом настроим DNSCrypt

Зачем? Ваш провайдер может заботливо подменять ip-адрес заблокированного ресурса, таким образом перенаправляя вас на свой ip с заглушкой, ну и наш обход по ip в данном случае не поможет. Для подмены не всегда даже нужно использовать dns сервер провайдера, ваши запросы могут перехватываться и ответы подменяться. Ну и к слову, это может делать не только провайдер.

opkg install dnscrpt-proxy

Настраиваем конфиг /etc/config/dnscrypt-proxy примерно так:

config dnscrypt-proxy ns1
        option address '127.0.0.1'
        option port '5353'
        option resolver 'cpunks-ru'

Таким образом у нас есть сервис dnscrypt на порту 5353 доступный на localhost.

Resolver — это dns, сервер поддерживающий шифрование. На роутере в файле /usr/share/dnscrypt-proxy/dnscrypt-resolvers.csv содержится список доступных, на момент выпуска установленной версии dnscrypt, серверов. А вот здесь https://dnscrypt.info/public-servers/ вообще все доступные серверы dnscrypt. Можете выбрать другого резолвера и/или добавить серверов для отказоустойчивости. Имейте в виду, что бы DNSCrypt работал с выбраным резолвером, он должен быть указан в dnscrypt-resolvers.csv.

Настраиваем dnsmasq на работу с dnscrypt. В /etc/config/dhcp комментируем строчку:

option resolvfile       '/tmp/resolv.conf.auto'

для того что бы не были задействованы dns серверы провайдера.

И добавляем:

        option noresolv '1'
        list server '/pool.ntp.org/208.67.222.222'
        list server '/antifilter.download/208.67.222.222'
        list server '127.0.0.1#5353'

noresolv '1' — отключает обработку файла /etc/resolv.conf

Запись list server 'domain/ip_dns' указывает какой dns сервер использовать для резолва указанного домена. Таким образом мы не задействуем dnscrypt для синхронизации ntp — для работы службе dnscrypt важно иметь актуальное время.

При загрузке роутера, скрипт hirkn запускается быстрее чем стартует dnscrypt, таким образом домен antifilter.download не резолвится и списки не скачиваются. Можно сделать задержку или ещё что придумать, но пока что не вижу смысла.

В итоге мы получаем такую вставку в конфиг:

        #option resolvfile       '/tmp/resolv.conf.auto'
        option noresolv '1'
        list server '/pool.ntp.org/208.67.222.222'
        list server '/antifilter.download/208.67.222.222'
        list server '127.0.0.1#5353'

Добавляем в автозагрузку и стартуем dnscrypt:

/etc/init.d/dnscrypt-proxy enable
/etc/init.d/dnscrypt-proxy start

Рестартуем dnsmasq:

/etc/init.d/dnsmasq restart

Точечный обход блокировок PKH на роутере с OpenWrt с помощью WireGuard и DNSCrypt - 7
Илюстрация работы без DNSCrypt и c DNSCrypt

Автоматически развертываем с помощью Ansible

Playbook и темплейты лежат на github. Используется модуль, в нём не нужен python на роутере и есть поддержка uci. Я постарался сделать так, что бы ваша конфигурация OpenWrt осталась не тронутой, но всё равно будьте бдительны.

Устанавливаем модуль gekmihesg/ansible-openwrt:

ansible-galaxy install gekmihesg.openwrt

Копируем плейбук и темлпейты:

cd /etc/ansible
git clone https://github.com/itdoginfo/ansible-openwrt-hirkn
mv ansible-openwrt-hirkn/* .
rm -rf ansible-openwrt-hirkn

Добавляйте ваш роутер в hosts:

[openwrt]
192.168.1.1

Подставляете свои переменные в hirkn.yml:

  vars:
    ansible_template_dir: /etc/ansible/templates/
    wg_server_address: wg_server_ip/url
    wg_private_key: privatekey-client
    wg_public_key: publickey-server
    wg_listen_port: 51820
    wg_client_port: 51820
    wg_client_address: 192.168.100.3/24

Обязательно нужно задать:

wg_server_address — ip/url wireguard сервера
wg_private_key, wg_public_key — приватный ключ клиента и публичный сервера
Остальное можно не менять или менять, в зависимости от того как настроен WireGuard сервер

Запускаем playbook

ansible-playbook playbooks/hirkn.yml

После выполнения плейбука, роутер сразу начнёт выполнять обход блокировок через ваш wireguard сервер.

Почему не BGP?

Под openwrt есть две утилиты реализующих BGP — quagga и bird. Quagg'у мне не удалось заставить забирать данные с antifilter. Bird подружился с сервисом с полпинка, но как заставить добавлять полученным подсетям интерфейс по умолчанию я, к сожалению, не понял. (Буду рад узнать как это можно реализовать).

В комментариях к подобным статьям я видел, что роутеры у людей "призадумывались" на некоторое время, когда те загоняют списки в таблицу маршрутизации. С реализацией через ipset мой Xiaomi mi 3G задумывается на 2 секунды (Asus rt-n16 на 5 секунд), когда скармливаешь ему список из 15ти тысяч подсетей. При дальнейшей работе нагрузки на процессор не замечал.

Все материалы не являются призывом к действию и представлены для ознакомления с функционалом ОС Linux.

Автор: itdog

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js