В последнее время в нашей стране крайне нестабильно работают различные бесплатные/открытые протоколы для организации виртуальных частных сетей. Коммерческие решения работают лучше, но по карману не всем. В качестве решения проблемы можно использовать открытую реализацию протокола CISCO AnyConnect: сервер ocserv и клиент OpenConnect. На этом сайте уже был материал, который в целом не потерял актуальность за семь с лишним лет. Но пришла пора его немного дополнить)

Отключение DTLS
AnyConnect использует в качестве транспорта TCP и опционально так же может использовать UDP с протоколом DTLS. DTLS позволяет снизить задержки внутри тоннеля и повысить скорость передачи данных. Однако в РФ использование DTLS приводит к нестабильной работе тоннеля и поэтому лучше этот протокол отключить. Для этого достаточно в файле /etc/ocserv/ocserv.conf закомментировать опции:
#udp-listen-host = [IP|HOSTNAME] #udp-port = 8443
И перезапустить ocserv.
Логирование
ocserv пишет логи на stdout и stderr. Для доступа к ним можно воспользоваться командой:
journalctl -xe -u ocserv.service
Однако для анализа информация оттуда не очень удобна. У ocserv есть возможность запускать скрипты при подключении и отключении пользователей. Этим можно воспользоваться для ведения логов. Для начала добавим в файл /etc/ocserv/ocserv.conf строки:
connect-script = /etc/ocserv/script-connect.sh disconnect-script = /etc/ocserv/script-connect.sh
И далее создадим скрипт /etc/ocserv/script-connect.sh следующего содержания:
#!/bin/sh
# При подключении нового пользователя скрипт запускается
# со следующими переменными окружения:
# REASON
# USERNAME
# GROUPNAME
# DEVICE
# IP_REAL
# IP_REAL_LOCAL
# IP_LOCAL
# IP_REMOTE
# IPV6_LOCAL
# IPV6_REMOTE
# IPV6_PREFIX
# ID
# REASON
# OCSERV_ROUTES
# OCSERV_NO_ROUTES
# OCSERV_DNS
# При отключении пользователя так же передаются
# дополнительные переменные:
# STATS_BYTES_IN
# STATS_BYTES_OUT
# STATS_DURATION
# Примеры переменных:
# REASON: disconnect
# USERNAME: moose-phone
# GROUPNAME:
# DEVICE: vpns0
# IP_REAL: 85.140.6.80
# IP_REAL_LOCAL: 179.43.151.27
# IP_LOCAL: 172.23.76.1
# IP_REMOTE: 172.23.76.180
# STATS_BYTES_IN: 15300316
# STATS_BYTES_OUT: 111224084
# STATS_DURATION: 56
LOGFILE="/var/log/ocserv.log"
TS=$(date --rfc-3339=seconds)
LOGLINE="[${TS}]: ${REASON} [${USERNAME}/${GROUPNAME}] from ${IP_REAL} on ${DEVICE} with IP ${IP_REMOTE}"
if [ "${REASON}" = "disconnect" ]; then
LOGLINE="${LOGLINE}. rx/tx: ${STATS_BYTES_IN}/${STATS_BYTES_OUT} bytes"
fi
echo "${LOGLINE}" >> ${LOGFILE}
Делаем скрипт исполняемым и перезапускаем сервис:
chmod +x /etc/ocserv/script-connect.sh && systemctl restart ocserv.service
Привязка IP-адресов к пользователям
Иногда возникает необходимость выдавать пользователю фиксированный IP-адрес при подключении. Для этого можно использовать опцию "config-per-user". В ней указывается директория с настройками пользователй:
config-per-user = /etc/ocserv/config-per-user/
В этой директории можно создавать файлы с именам логинов пользователей и вписывать опции, которые будут действовать только для указанного пользователя. Например если нужно чтобы пользователь "moose-phone" всегда получал адрес "172.23.76.200" нужно создать файл "/etc/ocserv/config-per-user/moose-phone" и добавить в него строку:
explicit-ipv4 = 172.23.76.200
После добавления опции нужно перезапустить сервис. После добавления/изменения файлов в этой директории перезапуск уже не нужен. Файлы считываются в момент подключения пользователя.
Так же стоит упомнять опцию "config-per-group". Работает эта опция схожим образом, но не для конкретных пользователей, а для групп пользователей.
Автоматическое получение server-pin
В свежих версиях клиента openconnect удалена опция "--no-cert-check" и теперь после каждой смены сертификата (привет, letsencrypt!) нужно заново разрешать принимать новый отпечаток сертификата, либо явно передавать значение этого отпечатка в опции "--servercert" и после каждого обновления сертификата обновлять передаваемое значение. В итоге был написан скрипт:
#!/bin/sh
OC_USER="vasya"
OC_PASS="p4ssw0rd333"
OC_SERV_HOST="oc.some.host"
OC_SERV_PORT="8443"
OC_SERV="$OC_SERV_HOST:$OC_SERV_PORT"
while [ true ]; do
OC_PIN=pin-sha256:$(echo \
| openssl s_client -showcerts -servername $OC_SERV_HOST -connect $OC_SERV 2>/dev/null \
| openssl x509 -pubkey -noout \
| openssl pkey -pubin -outform der \
| openssl dgst -sha256 -binary \
| openssl enc -base64)
echo $OC_PIN
echo $OC_PASS | openconnect --passwd-on-stdin --verbose \
--user $OC_USER --servercert $OC_PIN $OC_SERV
done
Здесь с помощью openssl получается сертификат сервера, вычисляется его отпечаток и дальше передаётся openconnect как доверенный.
ВАЖНО! следует понимать что доверие любому сертификату создаёт определённую брешь в безопасности. Потому не надо так делать если без крайней необходимости!
Ещё один клиент для Android
Для Android уже довольно давно развивается открытый клиент OpenConnect. Сборки можно скачать из каталога F-Droid. Клиент в частности удобен тем что позволяет сохранять введённый пароль. В том числе в системном хранилище Android.
На этом пока всё. Приятной работы!
