Переход с OpenVPN на WireGuard для объединения сетей в одну сеть L2

от автора

Хотел бы поделиться опытом объединения сетей в трех географически удаленных квартирах, в каждой из которых в качестве шлюза используются роутеры с 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.2192.168.10.80 для сервера
  • 192.168.10.100 с диапазоном 192.168.10.101192.168.10.149 для маршрутизатора в квартире №2
  • 192.168.10.150 с диапазоном 192.168.10.151192.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. На этот раз я напоролся на эту статью. В ней говорилось о создании Ethernet-туннеля поверх L3 при помощи GRE. Эта статья вселила в меня надежду. Оставалось неясно что делать с протоколом UDP. Поиск приводил меня к статьям об использовании socat в связке с SSH-туннелем, для проброса UDP-порта, однако, в них отмечалось, что такой подход работает только в режиме одного соединения, то есть работа нескольких VPN-клиентов оказалась бы невозможной. Мне пришла идея поднять VPN-сервер на VPS, а для клиентов настроить GRE, но, как оказалось, GRE не поддерживает шифрование, что приведет к тому, что в случае получения доступа к серверу третьими лицами, в их руках оказывается весь трафик между моими сетями, что меня не устраивало в принципе.

Вновь было принято решение в пользу избыточного шифрования, путем использования 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>

[Peer]
PublicKey = <ОТКРЫТЫЙ_КЛЮЧ_VPN_1_МС>
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, когда в моей сети появляется новое устройство. Вот ссылка на скрипт: github.com/r0ck3r/device_discover.

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

ссылка на оригинал статьи https://habr.com/ru/post/486864/


Комментарии

Добавить комментарий

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