waqur: (Евро)
[personal profile] waqur
OpenVPN — это программное решение для организации шифрованного проброса через интернет трафика между вашими локальными сетями. Он поддерживает два режима: tun и tap.

Режим "tun" легче настроить, однако он умеет передавать только IP-пакеты с NAT-трансляцией (что недостаточно для работы сетевых дисков Windows, плохо подходит для SIP-телефонии, RTSP-видеонаблюдения и т.д.).

Режим "tap" передаёт Ethernet-кадры независимо от содержимого (IPv4, IPv6, ARP/RARP, NetBIOS, IPX/SPX, STP, всё что угодно) и представляет собой как бы разделённый свитч, один порт которого "тут", а второй — "там". Никакой NAT-трансляции, по VPN-у в обе стороны свободно гуляет широковещательный UDP трафик, ICMP и всё остальное, очень удобно. Единственный недостаток режима "tap" — его сложно настроить (об этом сразу честно предупреждает официальное руководство по OpenVPN). В интернете мало информации, она противоречивая, неполная, устаревшая.



1. Сетевая топология


У нас есть два клиента, которые подключаются к интернету по IPv4, и возможно находятся за провайдерским NAT (cgNAT). Т.е. у них динамические серые IP-адреса. Также у нас есть сервер (dedicated или VPS), который имеет статический белый IPv4-адрес. Через этот сервер клиенты обмениваются трафиком друг с другом, но не ходят в интернет.

Все компьютеры в локальной сети имеют IP-адреса 172.21.13.N и маску подсети 255.255.255.0. Все компьютеры из первой локальной подсети (N<128) имеет свободный доступ друг к другу, а также ко всем комьютерам из второй локальной подсети (N>=128). Так же и в обратную сторону.

Наш сервер будет VPSкой под управлением FreeBSD 10. Клиенты — FreeBSD 10 (выделенный сервер, архитектура amd64) и Linux (архитектура mips, а точнее dd-wrt vpn edition на WRT54GL). Клиентам этого VPN'а вовсе не обязательно быть маршрутизаторами в своих подсетях, они могут быть обычными рядовыми хостами.

2. Генерация ключей


Для разнообразия — под Windows. Качаем дистрибутив OpenVPN, при установке не забываем отметить птичкой скрипты EasyRSA. Всё это хозяйство находится в папке "C:\Program Files (x86)\OpenVPN\easy-rsa". Остальное удобно делать из Far.

Переименовываем vars.bat.sample в vars.bat, там же правим "set HOME=%ProgramFiles%\OpenVPN\easy-rsa" -> "set HOME=C:\Program Files (x86)\OpenVPN\easy-rsa" и добавляем в начало "set PATH=C:\Program Files (x86)\OpenVPN\bin;%PATH%". По ходу можно подкорректировать переменные KEY_ на более осмысленные, чтобы не вводить потом каждый раз вручную одно и то же. Далее удаляем файл "init-config.bat", а в начало всех остальных добавляем строчку "call vars.bat".

Сначала инициализируем хранилище ключей, затем учреждаем новый центр сертификации, затем генерируем параметры Диффи-Хеллмана, и только после этого собственно генерируем ключи для сервера и обоих клиентов. Как видите, всё просто, это же EasyRSA:
clean-all.bat
build-ca.bat
build-dh.bat
build-key-server.bat server
build-key-pkcs12.bat client1
build-key-pkcs12.bat client2

Country, Province, City, Org должны содержать любой совпадающий между ключами бред, и желательно покороче, например код вашей страны по ISO 3166 Alpha-2. Email, Common Name, Name, Org.Unit должны немножко отличаться между ключами, например для Email: ca@myvpn server@myvpn client1@myvpn client2@myvpn. Никто вам почту на эти e-mail'ы слать не будет и резолвить домены в Common Name — тоже. Все пароли, связанные с экспортом ключей, должны быть пусты. Если запутаетесь или сделаете что-то неправильно — не пытайтесь исправлять, начинайте сначала.

Напоследок понадобится ключик для TLS-авторизации. Перейдите в "C:\Program Files (x86)\OpenVPN\bin" и оттуда дайте команду:
openvpn.exe --genkey --secret ..\easy-rsa\keys\ta.key


Результат ваших усилий — папка "C:\Program Files (x86)\OpenVPN\easy-rsa\keys". Её следует заархивировать, всё остальное — можно снести.

3. Подводные камни


В интернете полно советов, как сделать конфигурацию OpenVPN-сервера в tap-режиме, но для чего-то все они используют какие-то IP-адреса в настройках со стороны сервера. При чём тут к L2-мосту IP-адреса — я так и не понял, наверное это какие-то наркоманы копипастят разный бред друг у друга. IP-адреса в L2 не нужны, потому что на уровне 2 модели OSI по сети ходят не пакеты, а кадры. Кадр (фрейм) ethernet может нести в себе IPv4-пакет, а может нести и что-то совсем другое, например IPv6, ARP/RARP, STP, NetBIOS (весь этот L2-трафик заканчивается на ближайшем маршрутизаторе).

Второй важный момент касается опции client-to-client. Её можно указывать, тогда OpenVPN-сервер будет работать как мост (программный свитч) между клиентами, а можно и не указывать, тогда клиенты не будут видеть друг друга. Недостаток этой опции в том, что tcpdump, запущенный на tap-интерфейсе сервера не видит межклиентский трафик, т.к. OpenVPN-сервер не отправляет его операционной системе. Лично я предпочитаю вариант с запуском двух openvpn-серверов на двух разных tap-интерфейсах с организацией моста между ними силами операционной системы. Если что-то не работает на клиентах — можно запустить tcpdump на сервере и весь трафик как на ладони.

Третий нюанс заключается в том, что tap-интерфейсы, которые открыл OpenVPN, по умолчанию выключены (находятся в состоянии down). Их должен включать (переводить в up) специальный up-скрипт, вызываемый openvpn'ом.

Также есть сюрпризы, связанные с протоколом STP. "В мирное время" этот малоприметный протокол используют свитчи для исключения циклов в топологии Ethernet. Если между двумя свитчами нашлось более одного пути, тогда свитч во избежание network congestion на время блокирует один из своих портов. Когда происходит пересоздание сетевых интерфейсов tap и bridge, у них меняются MAC-адреса, что (с точки зрения стационарных свитчей по обе стороны тоннеля) эквивалентно наличию другого пути в L2-топологии и может приводить к затыку трафика на несколько минут. Чтобы этого не происходило, MAC-адреса tap'ов и бриджей вдоль всего пути необходимо зафиксировать.

Ещё один момент связан с фильтрацией трафика, который не должен проникать на другую сторону тоннеля. Напомню, работа tap-интерфейса основана на том, что каждый из VPN-клиентов по обе стороны тоннеля переводит свою сетевую карту (Ethernet или Wi-Fi) в promiscuous mode и начинает слышать весь L2-трафик в своей подсети. Тот L2-трафик, который не должен передаваться на другую сторону тоннеля, теоретически, должен отсекаться как физическими свитчами в обеих подсетях, так и bridge-драйвером операционной системы (программным свитчом между сетевой картой и tap-интерфейсом OpenVPN). Но так происходит не всегда. Иногда, по каким-то лунным причинам, драйвер бриджа почему-то решает зациклить в тоннель openvpn-трафик, идущий во внешний интернет. Разумеется, сервер, который оказался в таком состоянии, нуждается в перезагрузке. Во избежание подобных спецэффектов, бриджи на клиентах крайне желательно зафайерволлить. Это называется filtered bridge и работает так: пространство IPv4-адресов всей сети делится надвое, а затем создаются правила файерволла, которые разрешают межсетевой локальный трафик через бридж, а весь остальной IPv4-трафик — запрещают. При этом ARPы и прочее L2-only продолжают циркулировать свободно во всех направлениях.

4. Конфигурация сервера


Изменения в файле автозагрузки /etc/rc.conf:
cloned_interfaces="tap0 tap1 bridge0"
ifconfig_tap0="ether 00:bd:2e:29:2a:00 up"
ifconfig_tap1="ether 00:bd:d0:1d:2a:01 up"
ifconfig_bridge0="ether 02:52:e1:21:58:00 addm tap0 addm tap1"
openvpn_myvpn1_enable="YES"
openvpn_myvpn1_configfile="/usr/local/etc/openvpn_myvpn/server1.conf"
openvpn_myvpn1_dir="/usr/local/etc/openvpn_myvpn"
openvpn_myvpn2_enable="YES"
openvpn_myvpn2_configfile="/usr/local/etc/openvpn_myvpn/server2.conf"
openvpn_myvpn2_dir="/usr/local/etc/openvpn_myvpn"


Виртуальные сетевые карты tap0, tap1 и мостик между ними создаются и настраиваются ещё при загрузке сервера, чтобы не делать лишних движений при (пере)запуске серверных процессов OpenVPN.

Замените 4 последних байта в MAC-адресах на случайные числа.

В папке /usr/local/etc/openvpn_myvpn/keys/ находятся ключи — ca.crt, dh1024.pem, server.crt, server.key и ta.key.

Файл настроек сервера: /usr/local/etc/openvpn_myvpn/server1.conf ### server2.conf:
proto tcp-server
port 21766 ### port 28737
dev tap0 ### dev tap1

script-security 2
up /usr/local/etc/openvpn_myvpn/up.sh
down /usr/local/etc/openvpn_myvpn/down.sh

mode server
cipher BF-CBC

ca /usr/local/etc/openvpn_myvpn/keys/ca.crt
cert /usr/local/etc/openvpn_myvpn/keys/server.crt
key /usr/local/etc/openvpn_myvpn/keys/server.key
dh /usr/local/etc/openvpn_myvpn/keys/dh1024.pem

tls-auth /usr/local/etc/openvpn_myvpn/keys/ta.key 0
tls-server
tls-timeout 120
auth SHA1

comp-lzo no
keepalive 10 120
max-clients 32
sndbuf 0
rcvbuf 0

status /var/log/openvpn/openvpn-myvpn1-status.log
### status /var/log/openvpn/openvpn-myvpn2-status.log
log /var/log/openvpn/openvpn-myvpn1.log
### log /var/log/openvpn/openvpn-myvpn2.log
verb 1


Пара комментариев: как видите, никаких IP-адресов и никто не умер; шифроалгоритмы побыстрее, ключи покороче, выключение LZO — это потому, что один из клиентов — Linksys WRT54GL; {snd,rcv}buf 0 — это старый, хорошо известный хак; verb 1 — это для того, чтобы логи были покороче, на время отладки следует поставить verb 4; порты 21766 и 28737 — это просто случайные числа.

Скрипты по управлению tap-интерфейсами на сервере:

/usr/local/etc/openvpn_myvpn/up.sh:
#!/usr/local/bin/bash
echo "*** Up script called for interface $1 ***"
/sbin/ifconfig $1 up


/usr/local/etc/openvpn_myvpn/down.sh:
#!/usr/local/bin/bash
echo "*** Down script called for interface $1 ***"


5. Конфигурация первого клиента (FreeBSD), сеть 172.21.13.0/25


Подготовка ключей: понадобятся файлы ca.crt, client1.p12 и ta.key. Они должны быть скопированы в папку /usr/local/etc/openvpn.

В /boot/loader.conf следует добавить:
if_bridge_load="YES"
bridgestp_load="YES"
if_tap_load="YES"


В /etc/sysctl.conf следует добавить:
net.inet.ip.fw.one_pass=0
net.link.bridge.pfil_bridge=1


Вопреки популярным рекомендациям из других источников, net.link.bridge.ipfw=1 делать не надо — пакетики попадают в файерволл и счётчики тикают, но драйвер бриджа игнорирует запреты файерволла.

Хотя ifconfig умеет автоматически загружать модули ядра if_bridge.ko и bridgestp.ko при создании первого моста, мы организуем их одновременную загрузку вместе с ядром для того, чтобы настройки из файла /etc/sysctl.conf не отработали вхолостую (переменные net.link.bridge.* не существуют до загрузки модуля ядра if_bridge.ko).

В /etc/rc.conf следует добавить:
cloned_interfaces="tap0 bridge0"
ifconfig_tap0="ether 00:bd:7e:56:62:00 up"
ifconfig_bridge0="ether 02:00:87:54:a9:00 addm tap0 addm re0"
firewall_enable="YES"
firewall_type="/etc/firewall.rules"
firewall_logging="YES"
openvpn_enable="YES"
openvpn_configfile="/usr/local/etc/openvpn/client.conf"
openvpn_dir="/usr/local/etc/openvpn"


Мы создаём виртуальную сетевую карту tap0 и связываем её с реальной re0 в мост bridge0 ещё при загрузке клиента, чтобы при каждом обрыве клиентского OpenVPN-подключения не происходил перевод re0 в promiscuous mode и обратно. Заодно, при каждом обрыве клиентского OpenVPN-подключения бридж не будет забывать свой кэш MAC-адресов по обе стороны тоннеля.

Внимание: ваша сетевая карта может называться не re0, а em0 или как-то ещё; см. вывод ifconfig. В таком случае понадобится соответствующая замена.

Замените 4 последних байта в MAC-адресах на случайные числа.

Вопреки популярным рекомендациям из других источников, gateway_enable="YES" ни на что в нашем случае не влияет и не обязателен.

Правила файерволла /etc/firewall.rules:
add 10 deny ipv6 from any to any via bridge0
add 11 deny udp from any to any 67,68 via bridge0
add 12 allow udp from 172.21.13.0/24 to 172.21.13.255 via bridge0
add 13 allow udp from 172.21.13.0/24 to 255.255.255.255 via bridge0
add 14 allow ipv4 from 172.21.13.0/25 to 172.21.13.128/25 via bridge0
add 15 allow ipv4 from 172.21.13.128/25 to 172.21.13.0/25 via bridge0
add 16 deny all from any to any via bridge0
add 60000 allow all from any to any


Комментарий: убираем из тоннеля IPv6-сквозняки (например, винда что-то постоянно дрючит на 1900 порту в широковещательном режиме), разрешаем IPv4 между подсетями и UDP broadcast, остальное — фтопку. Отдельно запрещаем прохождение DHCP-запросов и ответов через бридж, чтобы локальные хосты не получали динамические IP-адреса от DHCP-серверов с другой стороны тоннеля, и наоборот.

Посмотреть счётчики файерволла можно с помощью команды ipfw -a list.
Посмотреть трафик через тоннель можно с помощью команды tcpdump -i tap0.


Файл настроек клиента: /usr/local/etc/openvpn/client.conf
proto tcp-client
dev tap0

remote myvpn.example.com 21766
nobind
resolv-retry infinite
connect-timeout 10
connect-retry-max infinite

auth-retry nointeract
pkcs12 /usr/local/etc/openvpn/keys/client1.p12
ns-cert-type server   # Require peer to be server
cipher BF-CBC

tls-client
tls-auth /usr/local/etc/openvpn/keys/ta.key 1
tls-timeout 120
auth SHA1

comp-lzo no
keepalive 10 120
sndbuf 0
rcvbuf 0

script-security 2
up /usr/local/etc/openvpn/up.sh
down /usr/local/etc/openvpn/down.sh

status /var/log/openvpn/openvpn-status.log                  
log /var/log/openvpn/openvpn.log
verb 1


Как видите, никаких IP-адресов и никто не умер. Параметры resolv-retry infinite, connect-retry-max infinite и auth-retry nointeract крайне полезны на клиентской стороне, от них ваши волосы станут здоровыми и шелковистыми.

Скрипты по управлению tap-интерфейсом на первом клиенте:
/usr/local/etc/openvpn/up.sh:
#!/usr/local/bin/bash
echo "*** Up script called for interface $1 ***"
/sbin/ifconfig $1 up


/usr/local/etc/openvpn/down.sh:
#!/usr/local/bin/bash
echo "*** Down script called for interface $1 ***"



6. Конфигурация второго клиента (Linux dd-wrt), сеть 172.21.13.128/25


Подготовка ключей: понадобятся файлы ca.crt, client2.crt, client2.key и ta.key (dd-wrt не понимает файлы .p12). Из client2.crt следует выбросить всё, кроме base64-кодированного блока, ограниченного строками с префиксом ---, после чего он будет называться cert.pem. Файл client2.key после аналогичных манипуляций будет называться key.pem.

Ключи заливаются через web-интерфейс, на вкладке "Службы — PPTP — OpenVPN — OpenVPN Daemon". ca.crt идёт в поле "Public Server Cert", cert.pem в "Public Client Cert", key.pem в "Private Client Key", ta.key в "OpenVPN TLS Auth", а openvpn.conf в "OpenVPN Config".

Кстати, вот он:
proto tcp-client
dev tap0

remote myvpn.example.com 28737
nobind
resolv-retry infinite
connect-timeout 10
connect-retry-max infinite

script-security 2
up /tmp/openvpn/route-up.sh
down /tmp/openvpn/route-down.sh

auth-retry nointeract
ca /tmp/openvpn/ca.crt
cert /tmp/openvpn/cert.pem
key /tmp/openvpn/key.pem
ns-cert-type server   # Require peer to be server
cipher BF-CBC

tls-client
tls-auth /tmp/openvpn/ta.key 1
tls-timeout 120
auth SHA1

comp-lzo no
keepalive 10 120
sndbuf 0
rcvbuf 0
verb 1

management localhost 5001


Ваши ключики вместе с up/down-скриптами и конфигом лежат в папке /tmp/openvpn/ ; порт 5001 обязателен для того, чтобы видеть статус OpenVPN-подключения в web-интерфейсе роутера (на вкладке "Статус — OpenVPN").

Для автоматической перезаписи файлов /tmp/openvpn/route-{up,down}.sh на наш манер и для управления бриджем с файерволлом понадобится соорудить вот такой скрипт автозагрузки. Он вводится через web-интерфейс, вкладка "Тех.обслуживание — Команды — Параметры запуска":

/usr/sbin/openvpn --mktun --dev tap0
/sbin/ifconfig tap0 hw ether 00:FF:05:30:A3:88
/sbin/ifconfig tap0 up
/usr/sbin/iptables -I INPUT -i tap0 -j ACCEPT
/usr/sbin/iptables -I INPUT -i tap0 -s 172.21.13.0/25 -d 172.21.13.0/25 -j DROP
/usr/sbin/iptables -I INPUT -i tap0 -s 172.21.13.128/25 -d 172.21.13.128/25 -j DROP
/usr/sbin/iptables -I OUTPUT -o tap0 -j ACCEPT
/usr/sbin/iptables -I OUTPUT -o tap0 -s 172.21.13.0/25 -d 172.21.13.0/25 -j DROP
/usr/sbin/iptables -I OUTPUT -o tap0 -s 172.21.13.128/25 -d 172.21.13.128/25 -j DROP
/sbin/ifconfig br0 hw ether 00:22:6B:95:8D:71
/usr/sbin/brctl addif br0 tap0
/sbin/ifconfig br0 up
if [ $(date | grep -c 'UTC 19') -gt 0 ]; then
  date 2016.02.08-18:00
fi
echo "#!/bin/sh" > /tmp/openvpn/route-up.sh
echo "/sbin/ifconfig tap0 up" >> /tmp/openvpn/route-up.sh
chmod +x /tmp/openvpn/route-up.sh
echo "#!/bin/sh" > /tmp/openvpn/route-down.sh
chmod +x /tmp/openvpn/route-down.sh
echo "#!/bin/sh" > /tmp/openvpn/watchdog.sh
echo "while true; do" >> /tmp/openvpn/watchdog.sh
echo '  if [ $(/usr/sbin/brctl show br0 | grep -c tap0) -eq 0 ]; then' >> /tmp/openvpn/watchdog.sh
echo "    /usr/sbin/brctl addif br0 tap0" >> /tmp/openvpn/watchdog.sh
echo "  fi" >> /tmp/openvpn/watchdog.sh
echo "  sleep 5" >> /tmp/openvpn/watchdog.sh
echo "done" >> /tmp/openvpn/watchdog.sh
chmod +x /tmp/openvpn/watchdog.sh
/tmp/openvpn/watchdog.sh &


Фокус с переводом часов нужен потому, что тупая железка, если не увидит NTP-сервер на WAN-интерфейсе в момент загрузки, не увидит его уже никогда и останется в 1970 году, по поводу чего библиотека OpenSSL в составе OpenVPN начнёт хныкать, что дата выдачи клиентского сертификата ещё не наступила, и OpenVPN никуда не подключится. Угомоните тупорылое создание путём подстановки любой даты позже даты генерации ключей.

Мост br0 существует априори между WiFi-интерфейсом и Ethernet-интерфейсом, поэтому мы просто добавляем tap0 к уже существующему мосту. При этом, правила фильтрации, очевидно, пишутся для tap0, а не для br0. Хотя Linux всё равно их применяет к другим интерфейсам, входящим в состав моста, отсюда их "разрешительная по умолчанию" природа.

Впрочем, это же Linux, так что виртуальный сетевой интерфейс tap0 какого-то лешего отваливается от моста br0, время от времени. Почему так происходит — х.з., но если приговнокодить watchdog (см. выше), то вроде всё нормально работает.

Наш файервол не пытается помешать проникновению внешне-интернетного трафика в тоннель, но это и не нужно, т.к. в данном случае OpenVPN выполняется на роутере, а роутер и так хорошо знает по IP-адресам и маскам на интерфейсах, какой трафик должен направляться на внешний интерфейс, а какой на внутренний.

Статистику файерволла можно смотреть через ssh, командой ipconfig -vL. tcpdump на этом устройстве не поддерживается, остаётся только tcpdump на сервере.

March 2024

S M T W T F S
     12
3456789
10111213141516
17181920212223
24252627282930
31      

Автор стиля

Развернуть

No cut tags
Page generated 2026-01-28 12:29 am
Powered by Dreamwidth Studios