Хотел бы поделиться опытом объединения сетей в трех географически удаленных квартирах, в каждой из которых в качестве шлюза используются роутеры с OpenWRT, в одну общую сеть. При выборе способа объединения сетей между L3 с маршрутизацией подсетей и L2 с бриджингом, когда все узлы сети будут находиться в одной подсети, было отдано предпочтение второму способу, более сложному в настройке, но дающим бОльшие возможности, так как в создаваемой сети планировалось прозрачное использование технологий Wake-on-Lan и DLNA.
Часть 1: Предыстория
В качестве протокола для реализации этой задачи изначально был выбран OpenVPN, так как, во-первых, он может создавать устройство tap, которое без проблем добавляется в мост, а во-вторых, OpenVPN поддерживает работу по протоколу TCP, что было также немаловажно, ведь ни в одной из квартир не имелось выделенного IP-адреса, а использовать STUN мне не удалось, так как мой провайдер почему-то блокирует входящие подключения по протоколу UDP из своих сетей, в то время как протокол TCP позволял мне пробросить порт VPN-сервера на арендуемый VPS при помощи SSH. Да, такой подход дает большую нагрузку, так как данные шифруются дважды, но вводить VPS в свою частную сеть я не захотел, так как оставался риск получения третьими лицами контроля над ним, следовательно, иметь в домашней сети такое устройство являлось крайне нежелательным и было решено заплатить за безопасность большим оверхедом.
Для проброса порта на роутере, на котором планировалось развернуть сервер, была использована программа sshtunnel. Описывать тонкости ее конфигурации не буду — это делается достаточно легко, просто отмечу, что ее задачей был проброс TCP-порта 1194 с роутера на VPS. Далее был настроен сервер OpenVPN на устройстве tap0, которое заводилось в мост br-lan. Проверив подключение к только что созданному серверу с ноутбука — стало понятно, что идея с пробросом порта себя оправдала и мой ноутбук стал членом сети роутера, хотя физически в ней не находился.
Дело оставалось за малым: нужно было распределить IP-адреса в разных квартирах так, чтобы они не конфликтовали и настроить маршрутизаторы в качестве OpenVPN-клиентов.
Были выбраны такие IP-адреса маршрутизаторов и диапазоны DHCP-серверов:
- 192.168.10.1 с диапазоном 192.168.10.2 — 192.168.10.80 для сервера
- 192.168.10.100 с диапазоном 192.168.10.101 — 192.168.10.149 для маршрутизатора в квартире №2
- 192.168.10.150 с диапазоном 192.168.10.151 — 192.168.10.199 для маршрутизатора в квартире №3
Также было необходимо назначить именно эти адреса для маршрутизаторов-клиентов OpenVPN-сервера, путем добавления в его конфигурацию строчки:
ifconfig-pool-persist /etc/openvpn/ipp.txt 0
и добавлением следующих строк в файл /etc/openvpn/ipp.txt:
flat1_id 192.168.10.100
flat2_id 192.168.10.150
где flat1_id и flat2_id — это имена устройств, указываемые при создании сертификатов для подключения к OpenVPN
Далее на роутерах были настроены OpenVPN-клиенты, устройства tap0 на обоих были занесены в мост br-lan. На этом этапе казалось, что все в порядке, так как все три сети видят друг друга и работают как единое целое. Однако, выяснилась не очень приятная деталь: иногда устройства могли получить IP-адрес не от своего маршрутизатора, со всеми вытекающими последствиями. По какой-то причине, маршрутизатор в какой-то из квартир не успевал ответить вовремя на DHCPDISCOVER и устройство получало не свой адрес. Я понял, что мне нужно отфильтровать такие запросы в tap0 на каждом из маршрутизаторов, но, как выяснилось, iptables не может работать с устройством, если оно является частью моста и мне на помощь должен прийти ebtables. К моему сожалению, в моих прошивках его не оказалось и пришлось пересобирать образы для каждого устройства. Сделав это и добавив такие строки в /etc/rc.local каждого маршрутизатора проблема была решена:
ebtables -A INPUT --in-interface tap0 --protocol ipv4 --ip-protocol udp --ip-destination-port 67:68 -j DROP
ebtables -A INPUT --in-interface tap0 --protocol ipv4 --ip-protocol udp --ip-source-port 67:68 -j DROP
ebtables -A FORWARD --out-interface tap0 --protocol ipv4 --ip-protocol udp --ip-destination-port 67:68 -j DROP
ebtables -A FORWARD --out-interface tap0 --protocol ipv4 --ip-protocol udp --ip-source-port 67:68 -j DROP
Такая конфигурация просуществовала на протяжении трех лет.
Часть 2: Знакомство с WireGuard
В последнее время в Интернете все чаще стали говорить о WireGuard, восхищаясь простотой его конфигурации, высокой скоростью передачи, низким пингом при сопоставимой безопасности. Поиск дополнительной информации о нем давал понять, что ни работа в качестве члена моста, ни работа по протоколу TCP им не поддерживается, что наводило меня на мысли о том, что альтернатив OpenVPN для меня по-прежнему нет. Так я откладывал знакомство с WireGuard.
Несколько дней назад по ресурсам, так или иначе связанным с IT, пролетела новость о том, что WireGuard наконец будет включен в ядро Linux, начиная с версии 5.6. Новостные статьи, как всегда, хвалили WireGuard. Я вновь погрузился в поиски путей замены старого доброго OpenVPN. На этот раз я напоролся на
Вновь было принято решение в пользу избыточного шифрования, путем использования VPN поверх VPN по следующей схеме:
VPN первого уровня:
VPS является сервером с внутренним адресом 192.168.30.1
МС является клиентом VPS с внутренним адресом 192.168.30.2
МК2 является клиентом VPS с внутренним адресом 192.168.30.3
МК3 является клиентом VPS с внутренним адресом 192.168.30.4
VPN второго уровня:
МС является сервером с внешним адресом 192.168.30.2 и внутренним 192.168.31.1
МК2 является клиентом МС с адресом 192.168.30.2 и имеет внутренний IP 192.168.31.2
МК3 является клиентом МС с адресом 192.168.30.2 и имеет внутренний IP 192.168.31.3
* МС — маршрутизатор-сервер в квартире 1, МК2 — маршрутизатор в квартире 2, МК3 — маршрутизатор в квартире 3
* Конфигурации устройств опубликованы в спойлере в конце статьи.
И так, пинги между узлами сети 192.168.31.0/24 ходят, пора перейти к настройке GRE-туннеля. Перед этим, дабы не терять доступ к маршрутизаторам, стоит настроить SSH-туннели для проброса 22 порта на VPS, таким образом, что, например, на 10022-ом порту VPS будет доступен маршрутизатор из квартиры 2, а на 11122-порту VPS будет доступен маршрутизатор из квартиры 3. Настройку проброса лучше всего выполнить все тем-же sshtunnel, так как он восстановит туннель в случае его падения.
Туннель настроен, можно подключаться к SSH через проброшенный порт:
ssh root@МОЙ_VPS -p 10022
Далее следует отключить OpenVPN:
/etc/init.d/openvpn stop
Теперь настроим GRE-туннель на маршрутизаторе из квартиры 2:
ip link add grelan0 type gretap remote 192.168.31.1 local 192.168.31.2
ip link set grelan0 up
И добавим созданный интерфейс в мост:
brctl addif br-lan grelan0
Аналогичную процедуру выполним на маршрутизаторе-сервере:
ip link add grelan0 type gretap remote 192.168.31.2 local 192.168.31.1
ip link set grelan0 up
И, также, добавим созданный интерфейс в мост:
brctl addif br-lan grelan0
начиная с этого момента пинги начинают успешно ходить в новую сеть и я, с удовлетворением отправляюсь пить кофе. Затем, чтобы оценить, как работает сеть на другом конце провода, я пытаюсь подключиться по SSH к одному из компьютеров в квартире 2, но ssh-клиент «зависает», не предлагая ввести пароль. Пробую подключиться к этому компьютеру по telnet на 22 порт и вижу строку, из которой можно понять, что соединение устанавливается, SSH-сервер отвечает, просто по какой-то причине не предлагает мне войти.
$ telnet 192.168.10.110 22
SSH-2.0-OpenSSH_8.1
Пытаюсь подключиться к нему же по VNC и вижу черный экран. Я убеждаю себя, что дело в удаленном компьютере, ведь к маршрутизатору из этой квартиры я спокойно могу подключиться по внутреннему адресу. Однако, я решаю подключиться к SSH этого компьютера через маршрутизатор и с удивлением обнаруживаю, что подключение удается, а удаленный компьютер работает вполне штатно, но при этом также не может подключиться к моему компьютеру.
Я вывожу устройство grelan0 из моста и запускаю OpenVPN на маршрутизаторе в квартире 2 и убеждаюсь, что сеть вновь работает как положено и соединения не обрываются. Поиском натыкаюсь на форумы, где люди жалуются на такие же проблемы, где им советуют поднять MTU. Сказано — сделано. Однако, пока MTU не был установлен достаточно большим — 7000 для устройств gretap, наблюдались либо обрывы соединений TCP, либо низкая скорость передачи. Из-за высокого MTU для gretap — MTU для соединений WireGuard первого и второго уровня были выставлены в 8000 и 7500 соответственно.
Провел аналогичную настройку и на маршрутизаторе из квартиры 3, с той лишь разницей, что на маршрутизаторе сервере добавился второй интерфейс gretap с именем grelan1, который также был добавлен в мост br-lan.
Все работает. Теперь можно поместить сборку gretap в автозагрузку. Для этого:
Поместил эти строки в /etc/rc.local на маршрутизаторе в квартире 2:
ip link add grelan0 type gretap remote 192.168.31.1 local 192.168.31.2
ip link set dev grelan0 mtu 7000
ip link set grelan0 up
brctl addif br-lan grelan0
Добавил это в /etc/rc.local на маршрутизаторе в квартире 3:
ip link add grelan0 type gretap remote 192.168.31.1 local 192.168.31.3
ip link set dev grelan0 mtu 7000
ip link set grelan0 up
brctl addif br-lan grelan0
И на маршрутизаторе-сервере:
ip link add grelan0 type gretap remote 192.168.31.2 local 192.168.31.1
ip link set dev grelan0 mtu 7000
ip link set grelan0 up
brctl addif br-lan grelan0
ip link add grelan1 type gretap remote 192.168.31.3 local 192.168.31.1
ip link set dev grelan1 mtu 7000
ip link set grelan1 up
brctl addif br-lan grelan1
После перезагрузки клиентских роутеров я обнаружил, что они по какой-то причине не подключаются к серверу. Подключившись к их SSH (благо, я предварительно настроил sshtunnel для этого) было обнаружено, что WireGuard зачем-то создает маршрут для endpoint, при этом неверный. Так, для 192.168.30.2 в таблице маршрутов был указан маршрут через интерфейс pppoe-wan, то есть через интернет, хотя маршрут до него должен был быть направлен через интерфейс wg0. После удаления этого маршрута соединение восстановилось. Найти где-то инструкций о том, как заставить WireGuard не создавать этих маршрутов мне не удалось. Более того, я даже не понял, особенность это OpenWRT, либо самого WireGuard. Не став долго разбираться с этой проблемой, я просто добавил на оба роутера в скрипт, зацикленный по таймеру, строку удалявшую этот маршрут:
route del 192.168.30.2
Подводя итоги
Полного отказа от OpenVPN я пока не добился, так как мне нужно иногда подключаться к новой сети с ноутбука, либо телефона, а настройка gretap устройства на них в общем случае невозможна, но, несмотря на это, я получил преимущество в скорости передачи данных между квартирами и, например, использование VNC теперь не доставляет неудобств. Пинг уменьшился незначительно, но стал более стабильным:
При использовании OpenVPN:
[r0ck3r@desktop ~]$ ping -c 20 192.168.10.110
PING 192.168.10.110 (192.168.10.110) 56(84) bytes of data.
64 bytes from 192.168.10.110: icmp_seq=1 ttl=64 time=133 ms
...
64 bytes from 192.168.10.110: icmp_seq=20 ttl=64 time=125 ms
--- 192.168.10.110 ping statistics ---
20 packets transmitted, 20 received, 0% packet loss, time 19006ms
rtt min/avg/max/mdev = 124.722/126.152/136.907/3.065 ms
При использовании WireGuard:
[r0ck3r@desktop ~]$ ping -c 20 192.168.10.110
PING 192.168.10.110 (192.168.10.110) 56(84) bytes of data.
64 bytes from 192.168.10.110: icmp_seq=1 ttl=64 time=124 ms
...
64 bytes from 192.168.10.110: icmp_seq=20 ttl=64 time=124 ms
--- 192.168.10.110 ping statistics ---
20 packets transmitted, 20 received, 0% packet loss, time 19003ms
rtt min/avg/max/mdev = 123.954/124.423/126.708/0.675 ms
На него в большей степени влияет высокий пинг до VPS, который составляет примерно 61.5 мс
Однако, скорость увеличилась значительно. Так, в квартире с маршрутизатором-сервером я имею скорость подключения к Интернету 30 мбит/сек, а в остальных квартирах по 5 мбит/сек. При этом, во время использования OpenVPN мне не удавалось достичь скорости передачи данных между сетями больше 3,8 мбит/сек по показаниями iperf, в то время как WireGuard «прокачал» ее до тех же 5 мбит/сек.
Конфигурация WireGuard на VPS[Interface]
Address = 192.168.30.1/24
ListenPort = 51820
PrivateKey = <ЗАКРЫТЫЙ_КЛЮЧ_ДЛЯ_VPS>
AllowedIPs = 192.168.30.2/32 [Peer] PublicKey = <ОТКРЫТЫЙ_КЛЮЧ_VPN_2_МК2>
AllowedIPs = 192.168.30.3/32 [Peer] PublicKey = <ОТКРЫТЫЙ_КЛЮЧ_VPN_2_МК3>
AllowedIPs = 192.168.30.4/32
Конфигурация WireGuard на МС (добавляется в /etc/config/network)
#VPN первого уровня - клиент
config interface 'wg0'
option proto 'wireguard'
list addresses '192.168.30.2/24'
option private_key 'ЗАКРЫТЫЙ_КЛЮЧ_VPN_1_МС'
option auto '1'
option mtu '8000'
config wireguard_wg0
option public_key 'ОТКРЫТЫЙ_КЛЮЧ_VPN_1_VPS'
option endpoint_port '51820'
option route_allowed_ips '1'
option persistent_keepalive '25'
list allowed_ips '192.168.30.0/24'
option endpoint_host 'IP_АДРЕС_VPS'
#VPN второго уровня - сервер
config interface 'wg1'
option proto 'wireguard'
option private_key 'ЗАКРЫТЫЙ_КЛЮЧ_VPN_2_МС'
option listen_port '51821'
list addresses '192.168.31.1/24'
option auto '1'
option mtu '7500'
config wireguard_wg1
option public_key 'ОТКРЫТЫЙ_КЛЮЧ_VPN_2_МК2'
list allowed_ips '192.168.31.2'
config wireguard_wg1ip link add grelan0 type gretap remote 192.168.31.1 local 192.168.31.3
option public_key 'ОТКРЫТЫЙ_КЛЮЧ_VPN_2_МК3'
list allowed_ips '192.168.31.3'
Конфигурация WireGuard на МК2 (добавляется в /etc/config/network)
#VPN первого уровня - клиент
config interface 'wg0'
option proto 'wireguard'
list addresses '192.168.30.3/24'
option private_key 'ЗАКРЫТЫЙ_КЛЮЧ_VPN_1_МК2'
option auto '1'
option mtu '8000'
config wireguard_wg0
option public_key 'ОТКРЫТЫЙ_КЛЮЧ_VPN_1_VPS'
option endpoint_port '51820'
option persistent_keepalive '25'
list allowed_ips '192.168.30.0/24'
option endpoint_host 'IP_АДРЕС_VPS'
#VPN второго уровня - клиент
config interface 'wg1'
option proto 'wireguard'
option private_key 'ЗАКРЫТЫЙ_КЛЮЧ_VPN_2_МК2'
list addresses '192.168.31.2/24'
option auto '1'
option listen_port '51821'
option mtu '7500'
config wireguard_wg1
option public_key 'ОТКРЫТЫЙ_КЛЮЧ_VPN_2_МС'
option endpoint_host '192.168.30.2'
option endpoint_port '51821'
option persistent_keepalive '25'
list allowed_ips '192.168.31.0/24'
Конфигурация WireGuard на МК3 (добавляется в /etc/config/network)
#VPN первого уровня - клиент
config interface 'wg0'
option proto 'wireguard'
list addresses '192.168.30.4/24'
option private_key 'ЗАКРЫТЫЙ_КЛЮЧ_VPN_1_МК3'
option auto '1'
option mtu '8000'
config wireguard_wg0
option public_key 'ОТКРЫТЫЙ_КЛЮЧ_VPN_1_VPS'
option endpoint_port '51820'
option persistent_keepalive '25'
list allowed_ips '192.168.30.0/24'
option endpoint_host 'IP_АДРЕС_VPS'
#VPN второго уровня - клиент
config interface 'wg1'
option proto 'wireguard'
option private_key 'ЗАКРЫТЫЙ_КЛЮЧ_VPN_2_МК3'
list addresses '192.168.31.3/24'
option auto '1'
option listen_port '51821'
option mtu '7500'
config wireguard_wg1
option public_key 'ОТКРЫТЫЙ_КЛЮЧ_VPN_2_МС'
option endpoint_host '192.168.30.2'
option endpoint_port '51821'
option persistent_keepalive '25'
list allowed_ips '192.168.31.0/24'
В описанных конфигурациях для VPN второго уровня я указываю клиентам WireGuard порт 51821. По идее это не нужно, так как клиент установит соединение с любого свободного непривилегированного порта, но я сделал так, чтобы можно было запретить все входящие соединения на интерфейсах wg0 всех маршрутизаторов, кроме входящих UDP-соединений на порт 51821.
Надеюсь, что статья будет кому-то полезна.
P.S. Также, хочу поделиться своим скриптом, который шлет мне PUSH-уведомление на телефон в приложение WirePusher, когда в моей сети появляется новое устройство. Вот ссылка на скрипт:
UPDATE: Конфигурация OpenVPN-сервера и клиентов
OpenVPN-сервер
client-to-client
ca /etc/openvpn/server/ca.crt
cert /etc/openvpn/server/vpn-server.crt
dh /etc/openvpn/server/dh.pem
key /etc/openvpn/server/vpn-server.key
dev tap
ifconfig-pool-persist /etc/openvpn/ipp.txt 0
keepalive 10 60
proto tcp4
server-bridge 192.168.10.1 255.255.255.0 192.168.10.80 192.168.10.254
status /var/log/openvpn-status.log
verb 3
comp-lzo
OpenVPN-клиент
client
tls-client
dev tap
proto tcp
remote VPS_IP 1194 # Change to your router's External IP
resolv-retry infinite
nobind
ca client/ca.crt
cert client/client.crt
key client/client.key
dh client/dh.pem
comp-lzo
persist-tun
persist-key
verb 3
Для генерации сертификатов использовал easy-rsa
Источник: habr.com