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

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

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

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

Видеоверсия

Почему OpenWrt и WireGuard?

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

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

Немного о WireGuard

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

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

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

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

Установка

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

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

Серверная часть выглядит так: Конфиг хранится в /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

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

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

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

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

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

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

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

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

opkg update
opkg install ipset wireguard curl

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

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

#!/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. Списки запрещенных подсетей и адресов получаем файлами. На ROM роутера что-то писать лишний раз не стоит. В /tmp — потому что это RAM, такая особенность OpenWrt, довольно удобная.

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

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

После того как файлы у нас — рестартуем 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'

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

0. public_key — ключ publickey-server
allowed_ips — подсети, в которые может ходить трафик через тунель, в нашем случае никаких ограничей не требуется, поэтому 0. 0/0
route_allowed_ips — флаг, который делает роут через wg интерфейс для перечисленных сетей из параметра allowed_ips. 0. В нашем случае это не нужно, эту работу выполняет 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.

В openwrt зоны — это кастомные цепочки в iptables. Добавляем зону для wireguard. Зона для 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

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

/etc/init.d/hirkn

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

mtr/traceroute telegram.org/linkedin.com

Бонусом настроим 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.

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

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

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

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

Можно сделать задержку или ещё что придумать, но пока что не вижу смысла. При загрузке роутера, скрипт 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


Илюстрация работы без DNSCrypt и c DNSCrypt

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

Используется модуль, в нём не нужен python на роутере и есть поддержка uci. Playbook и темплейты лежат на github. Я постарался сделать так, что бы ваша конфигурация 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?

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

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

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


Оставить комментарий

Ваш email нигде не будет показан
Обязательные для заполнения поля помечены *

*

x

Ещё Hi-Tech Интересное!

Мониторинг мёртв? — Да здравствует мониторинг

Соответственно, на поддержке очень разнообразная архитектура. Наша компания с 2008 года занимается преимущественно управлением инфраструктурами и круглосуточной технической поддержкой веб-проектов: у нас более 400 клиентов, это порядка 15% электронной коммерции России. Но чтобы понять, что авария произошла, нужно мониторить проект ...

Метапрограммирование в C++ и русская литература: через страдания к просветлению

#define variant_TL1( T1 ) detail::typelist< T1, detail::nulltype > #define variant_TL2( T1, T2) detail::typelist< T1, variant_TL1( T2) > #define variant_TL3( T1, T2, T3) detail::typelist< T1, variant_TL2( T2, T3) > #define variant_TL4( T1, T2, T3, T4) detail::typelist< T1, variant_TL3( T2, T3, T4) ...