Перехід із OpenVPN на WireGuard для об'єднання мереж в одну мережу L2

Перехід із 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.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. На цей раз я напоровся на цю статтю. У ній йшлося про створення 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

Сподіваюся, що стаття буде комусь корисною.

PS Також хочу поділитися своїм скриптом, який шле мені 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

Джерело: habr.com

Додати коментар або відгук