6 окт. 2016 г.

ocserv DTLS error

Попался на глаза довольно интересный на первый взгляд продукт - Openconnect VPN Server, который реализует SSL VPN (по образу и подобию решения от Cisco и совместимый с их клиентом AnyConnect). Для Debian он доступен в репозитории (на момент написания - в testing). Если дистрибутив на нестабильный обновлять нет желания или возможности, можно подключить testing только для установки ocserv:
root@vpn:~# cat /etc/apt/preferences.d/testing
Package: *
Pin: release a=testing
Pin-Priority: 50
Соответственно, установка будет выглядеть как
root@vpn:~# apt-get install -t testing ocserv
Базовая конфигурация довольно подробно описана в документации (есть и примеры, и сам конфиг-файл хорошо самодокументирован).
Я же хотел отметить следующее...
Собранный для Debian пакет ocserv идет с предустановленными unit-файлами для systemd, который в стабильном jessie теперь есть из коробки. И возникла вот какая проблема: при запуске через systemd сервер запускается и работает корректно, без ошибок, однако после установления соединения от клиента (находящегося в этой же подсети, с отключенными фаерволами), в логах при любом обмене данными появляются сообщения
bind UDP to [::]:443: Invalid argument
connect UDP socket from [::ffff:128.128.128.128]:62862: Network is unreachable
Со стороны клиента это выглядит как сообщение о невозможности установить DTLS соединение. Сам туннель работает, трафик ходит, однако, выходит, udp-метаданные или работают вовсе, или не защищены.
Исследования выявили, что при запуске без участия systemd, например
root@vpn:~# ocserv  -f -d 10 --config /etc/ocserv/ocserv.conf
Соединение устанавливается и ошибок с DTLS нет. Также было замечено, что в случае запуска через systemd сам ocserv не слушает 443 порт, а этим вместо него занимается systemd.
Оказалось, что (это мои предположения, которые могут быть далеки от истины) systemd запускает сокет, через который уже запускается и работает сам процесс worker от ocserv. То есть у нас есть ocserv.socket и ocserv.service, связанные друг с другом. При ручном запуске этого не происходит (хотя сам сокет, как и описано в конфиге, создается и, полагаю, используется), а netstat -nlp показывает слушаемый порт:
root@vpn:~# netstat -nlp | grep ocserv
tcp        0      0 0.0.0.0:443             0.0.0.0:*               LISTEN      547/ocserv-main
udp        0      0 0.0.0.0:443             0.0.0.0:*                           547/ocserv-main
unix  2      [ ACC ]     STREAM     LISTENING     14164    547/ocserv-main     /var/run/occtl.socket
unix  2      [ ACC ]     STREAM     LISTENING     14174    822/ocserv-sm       /var/run/ocserv-socket.547
Таким образом, проблема локализуется где-то во взаимодействии systemd и ocserv.service через ocserv.socket
Текущая версия пакета:
root@vpn:~# apt-cache policy ocservocserv:Установлен: 0.11.4-1+b1Кандидат: 0.11.4-1+b1Таблица версий:*** 0.11.4-1+b1 0500 http://mirror.yandex.ru/debian/ testing/main amd64 Packages100 /var/lib/dpkg/status
Предлагаемый workaround:

Отключаем запуск сокета через systemd вовсе:
root@vpn:~# systemctl mask ocserv.socket
Комментируем зависимость службы от юнита сокета:
root@vpn:~# cp /lib/systemd/system/ocserv.service /etc/systemd/system/ocserv.service
root@vpn:~# sed -i s/Also=ocserv.socket/#Also=ocserv.socket/ /etc/systemd/system/ocserv.service
root@vpn:~# systemctl daemon-reload
Наблюдаем после перезапуска:
root@vpn:~# journalctl -n -u ocserv.service | grep 443
окт 06 11:24:13 vpn ocserv[547]: listening (TCP) on 0.0.0.0:443...
окт 06 11:24:13 vpn ocserv[547]: listening (UDP) on 0.0.0.0:443...
После этого в моем случае проблема перестала воспроизводиться.