Чем отличается от подобных материалов?
- Реализация на чистом OpenWrt
- Использование WireGuard
- Конфигурация роутера организуется с помощью конфигов OpenWrt, а не кучей в одном скрипте
- Предусмотрены ситуации при рестарте сети и перезагрузке
- Потребляет мало ресурсов роутера: заблокированные подсети содержатся в iptables, а не в таблицах маршрутизации. Что позволяет развернуть это дело даже на слабых устройствах
- Автоматизация конфигурации с помощью Ansible (не требуется python на роутере)
Видеоверсия
Почему OpenWrt и WireGuard?
OpenWrt ставится на очень много моделей soho роутеров, конфигурируется и расширяется как душа пожелает. Сейчас многие прошивки роутеров — это надстройки над OpenWrt.
Wireguard используется из-за его быстрой и простой настройки, а так же из-за высокой скорости передачи через туннель.
Немного о WireGuard
В нашем случае сервер — это 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
Конфигурация
Конфиг хранится в /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 сервер готов.
Настройка роутера
Я использую OpenWrt версии 18.06.1 на Xiaomi mi 3G и Asus RT-N16.
Логика работы роутера
Загружаем списки, помещаем их в iptables, все адреса из этих списков iptables помечает маркером 0x1. Далее все пакеты помеченные 0x1 идут в отдельную таблицу маршрутизации, все пакеты попавшие в эту таблицу маршрутизации идут через wg интерфейс.
Установка пакетов
Насчет занимаемого места на флеше, на всё понадобится примерно 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
и запускаем скрипт:
/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.
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
Илюстрация работы без 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