Поднимаем OpenConnect SSL VPN сервер (ocserv) в docker для внутренних проектов
Обновлено 02.10.2024
Приветствую!
Сегодня будем разворачивать свой VPN на базе OpenConnect сервера (ocserv), работающего поверх HTTPS и который совместим с Cisco Anyconnect. Все это добро мы упакуем в docker контейнер для простоты использования и лёгкой переносимости.
Данный сервер планируется использовать, как основной связующий элемент, с помощью которого будем настраивать сетевое взаимодействие будущих проектов и сервисов. Сама установка сервера выполняется всего в несколько команд. Будет интересно 😉
Присоединяйтесь к нашему каналу: t.me/r4ven_me и чату: t.me/r4ven_me_chat в Telegram .
Пожалуйста не пугайтесь “длинности” поста. В статье описано множество подробностей, но сама процедура развертывания очень проста и выполняется в несколько команд (загляните в TLDR). Настройка клиентов и то занимает больше времени.
#############################################################
## при использовании домена для получения SSL сертификатов ##
## расскоментируйте сервис certbot и параметр depends_on ##
## у сервиса openconnect + укажите свои значения вместо ##
## *example* в файле docker-compose.yml ##
#############################################################
### установка и настройка сервера с помощью готового образа ###
# создание директории проекта
mkdir /opt/openconnect && cd /opt/openconnect
# копирование файла docker-compose.yml
curl -O https://raw.githubusercontent.com/r4ven-me/openconnect/main/docker-compose.yml
# запуск OpenConnect сервера
docker compose up -d && docker compose logs -f
# создание пользователя с id "exampleuser" и именем "Example User"
# файл сертификата .p12 будет создан в ./data/secrets
docker exec -it openconnect ocuser exampleuser 'Example User'
# настройка автозапуска с systemd
docker compose down
curl -fLo /etc/systemd/system/openconnect.service https://raw.githubusercontent.com/r4ven-me/openconnect/main/src/server/openconnect.service
systemctl daemon-reload
systemctl enable --now openconnect
### пример подключения клиента с помощью утилиты openconnect ###
# без домена
sudo openconnect -c /home/exampleuser/exampleuser.p12 12.345.67.89:43443 <<< $(echo "examplepassword"$'\n'yes$'\n')
# с доменом
sudo openconnect -c /home/exampleuser/exampleuser.p12 example.com:43443 <<< $(echo "examplepassword"$'\n')
Обращаю ваше внимание, что предоставленная в данной статье информация предназначена исключительно для образовательных целей. Любые действия, основанные на этой информации, осуществляются на ваш собственный риск и ответственность.
Введение
Пару слов о том, в чем смысОл всего происходящего.
В корпоративном мире существует продукт Cisco AnyConnect, который представляет собой проприетарное ПО для организации виртуальных частных сетей (VPN), разработанное компанией Cisco.
Как это часто бывает в IT индустрии, если образуется весомый спрос на продукт, который имеет закрытый исходный код, появляется его “открытая” реализация. Самым популярным примером такого явления является Linux.
Как вы могли догадаться, рассматриваемый нами сегодня OpenConnect – это open source реализация ПО для организации VPN сервиса, совместимого с Cisco AnyConnect.
Вот что говорит зарубежная вики про OpenConnect:
OpenConnect is a free and open-source cross-platform multi-protocol virtual private network (VPN) client software which implement secure point-to-point connections.
Wikipedia
Проект OpenConnect также включает в себя сервер, который называется ocserv (OpenConnect server) и тем самым обеспечивает полноценное клиент-серверное VPN решение.
Основные преимущества такого решения:
- Кроссплатформенность: клиенты под Linux, Windows, MacOS, Android, iOS, HarmonyOS;
- Гибкость настройки, в т.ч. для каждого пользователя;
- Шифрование соединения на основе SSL/TLS;
- Простота настройки и использования;
- Множество способов авторизации + двухфакторная;
- и многое другое.
Предварительная подготовка
Для удобства и простоты использования мы с вами будем разворачивать OpenConnect сервер в docker контейнере, путем сборки образа и его запуска с помощью docker-compose. Обязательным условием является настроенный Linux сервер с установленным и запущенным в нем docker engine. А также наличие root привилегий (для моего примера) или группы docker у пользователя, если будете выполнять установку по своему.
Если с какой-то из пунктов отсутствует, возможно вам будут полезны следующие статьи:
- Установка сервера Debian 12
- Начальная настройка Linux сервера на примере Debian
- Установка Docker engine на Linux сервер под управлением Debian
В статье про установку docker’а также есть ссылки на полезные материалы, где доступным языком объясняется, что это такое, для чего нужен и из чего состоит. Если вы ранее не работали с данной технологией контейнерной виртуализации, то я настоятельно рекомендую их к прочтению.
Так, как ocserv работает поверх HTTPS c SSL шифрованием, рекомендованным (но необязательным) условиям является наличие валидного доменного имени. Конкретнее – DNS записи типа A, указывающую на внешний IP адрес вашего сервера. Добавление такой записи производится в настройках DNS вашего провайдера домена. В моём примере используется адрес: vpn.r4ven.me
. При настройке OpenConnect адрес DNS необходим для бесплатного получения официальных SSL сертификатов от проекта Let’s Encrypt.
Если домена у вас нет, то при настройке сервера вместо DNS имени указывайте его IP адрес. При такой настройке на VPN клиентах при подключении будет выплывать уведомление о подключении к неподтвержденному источнику. В этом нет ничего страшного, просто это неудобно, и если честно, раздражает)
Схема проекта
Для визуального подкрепления, ниже представлена схема проекта будущего OpenConnect VPN сервера:
Сохранив картинку в голову, приступаем к установке и настройке.
Развертывание OpenConnect VPN сервера в docker
Пример развертывания OpenConnect сервера из статьи выполнялся в среде дистрибутива Debian 12.
Для быстрого и удобного поднятия своего VPN сервера я подготовил небольшой проект с использованием docker, docker-compose и bash.
Весь процесс настройки сервера OpenConnect внутрни контейнера был автоматизирован.
Вот краткое описание проекта:
- Сборка образа docker на основе Debian 12;
- Установка в образ сервера ocserv и вспомогательных утилит;
- Копирование в образ bash скрипта
oscerv.sh
; - При первом старте скрипт создает необходимые файлы и папки, в т.ч. основной конфиг
ocserv.conf
, скрипты подключения/отключения и файлы сертификатов; - Всё это сохраняется в docker volume, который монтируется в папочку
./data
; - Файлы сервера можно с легкостью переносить на другие системы/платформы, где работает docker (просто перенесите папку
./data
); - Конфиг файлы сервера можно кастомизировать под свои нужды;
- При старте контейнера скрипт
oscserv.sh
проверяет наличие файлов в директории./data
, если они есть, то просто запускается сервер, если нет, файлы создаются заново, и после запускается сервер; - Все необходимые значения для конфигов и сертификатов берутся из переменных окружения, определенные в файле
.env
(если не указать, будут использованы дефолтные значения);
Так, воды налил, теперь давайте приступать к установке и настройка нашего сервера.
Обновление системы
Первым делом рекомендуется обновить вашу систему до актуального состояния:
sudo -s
apt update && apt upgrade -y
Клонирование репозитория с исходными файлами проекта
Для клонирования git репозитория нам потребуется утилита командной строки, из состава пакета одноимённой системы контроля версий – git. Если эта система у вас не установлена, то выполняем:
apt install git
Теперь клонируем репозиторий с подготовленными файлами из моего GitHub, далее копируем директорию server по пути /opt/openconnect
и переходим в неё:
git clone https://github.com/r4ven-me/openconnect
cp -rv ./openconnect/src/server /opt/openconnect && rm -rf ./openconnect
cd /opt/openconnect
Директория
/opt
(optional) предназначена для установки “дополнительного” программного обеспечения, которое не является стандартным для данной ОС.
Файлы проекта имеет следующую структуру:
Где:
Клик ЛКМ для просмотра содержимого файла
.env
- файл с переменнымиTZ="Europe/Moscow"
SRV_PORT="43443"
SRV_CN="example.com"
SRV_CA="Example CA"
USER_EMAIL="mail@example.com"
docker-compose.yml
- файл описания сервисов (контейнеров) openconnect и certbot (Let's Encrypt)---
networks:
vpn:
ipam:
driver: default
config:
- subnet: 172.22.22.0/24
gateway: 172.22.22.1
services:
certbot:
image: certbot/certbot
container_name: certbot
hostname: certbot
env_file:
- .env
volumes:
- ./data/ssl:/etc/letsencrypt
ports:
- 80:80
command: certonly --non-interactive --keep-until-expiring --standalone --preferred-challenges http --agree-tos --email ${USER_EMAIL} -d ${SRV_CN}
openconnect:
depends_on:
certbot:
condition: service_completed_successfully
build: .
image: openconnect
container_name: openconnect
restart: unless-stopped
deploy:
resources:
limits:
cpus: '0.50'
memory: 200M
cap_add:
- NET_ADMIN
hostname: openconnect
env_file:
- .env
volumes:
- ./data:/etc/ocserv
devices:
- /dev/net/tun:/dev/net/tun
ports:
- ${SRV_PORT}:443/tcp
networks:
vpn:
ipv4_address: 172.22.22.22
Dockerfile
- файл описания docker образа сервера openconnect для сборкиFROM debian:12
ARG DEBIAN_FRONTEND=noninteractive
ENV OCSERV_DIR="/etc/ocserv"
ENV CERTS_DIR="${OCSERV_DIR}/certs"
ENV SSL_DIR="${OCSERV_DIR}/ssl"
ENV SECRETS_DIR="${OCSERV_DIR}/secrets"
ENV PATH="${OCSERV_DIR}:${PATH}"
RUN apt-get update && \
apt-get install -y --no-install-recommends \
ocserv \
gnutls-bin \
iptables \
iproute2 \
iputils-ping \
less && \
rm -rf /var/lib/apt/lists
COPY ./ocserv.sh /
CMD [ "/ocserv.sh" ]
HEALTHCHECK --interval=5m --timeout=3s \
CMD pidof -q ocserv-main || exit 1
ocserv.sh
- bash скрипт, запускаемый при старте контейнера, который выполняет начальную конфигурацию и инициацию процесса ocserv#!/bin/bash
# Some protection
set -Eeuo pipefail
# Define default server vars if they are not set
SRV_CN="${SRV_CN:=example.com}"
SRV_CA="${SRV_CA:=Example CA}"
# Ocserv vars (do not modify)
OCSERV_DIR="/etc/ocserv"
CERTS_DIR="${OCSERV_DIR}/certs"
SSL_DIR="${OCSERV_DIR}/ssl"
SECRETS_DIR="${OCSERV_DIR}/secrets"
# Start server if data files exist
# if [[ $(find "${OCSERV_DIR}" -type f -not -path "${SSL_DIR}/*" | wc -l) -gt 0 ]]; then
if [[ -r "${OCSERV_DIR}"/ocserv.conf ]]; then
echo "Starting OpenConnect Server"
/usr/sbin/ocserv --foreground || { echo "Starting failed" >&2; exit 1; }
else
echo "Running OpenConnect Server at first with new certs generation"
fi
# Create certs dirs
if [[ -d $OCSERV_DIR ]]; then
for sub_dir in "${OCSERV_DIR}"/{"ssl/live/${SRV_CN}","certs","secrets"}; do
mkdir -p "$sub_dir"
done
fi
# Create ocserv config file
cat << _EOF_ > "${OCSERV_DIR}"/ocserv.conf
auth = "certificate"
#auth = "plain[passwd=${OCSERV_DIR}/ocpasswd]"
#enable-auth = "certificate"
tcp-port = 443
socket-file = /run/ocserv-socket
server-cert = ${SSL_DIR}/live/${SRV_CN}/fullchain.pem
server-key = ${SSL_DIR}/live/${SRV_CN}/privkey.pem
ca-cert = ${CERTS_DIR}/ca-cert.pem
isolate-workers = true
max-clients = 20
max-same-clients = 2
rate-limit-ms = 200
server-stats-reset-time = 604800
keepalive = 10
dpd = 120
mobile-dpd = 1800
switch-to-tcp-timeout = 25
try-mtu-discovery = false
cert-user-oid = 0.9.2342.19200300.100.1.1
tls-priorities = "NORMAL:%SERVER_PRECEDENCE:%COMPAT:-VERS-SSL3.0:-VERS-TLS1.0:-VERS-TLS1.1:-VERS-TLS1.3"
auth-timeout = 1000
min-reauth-time = 300
max-ban-score = 100
ban-reset-time = 1200
cookie-timeout = 600
deny-roaming = false
rekey-time = 172800
rekey-method = ssl
connect-script = ${OCSERV_DIR}/connect.sh
disconnect-script = ${OCSERV_DIR}/disconnect.sh
use-occtl = true
pid-file = /run/ocserv.pid
log-level = 1
device = vpns
predictable-ips = true
default-domain = $SRV_CN
ipv4-network = 10.10.10.0
ipv4-netmask = 255.255.255.0
tunnel-all-dns = true
dns = 8.8.8.8
ping-leases = false
config-per-user = ${OCSERV_DIR}/config-per-user/
cisco-client-compat = true
dtls-legacy = true
client-bypass-protocol = false
crl = /etc/ocserv/certs/crl.pem
_EOF_
# Create template for CA SSL cert
cat << _EOF_ > "${CERTS_DIR}"/ca.tmpl
organization = $SRV_CN
cn = $SRV_CA
serial = 001
expiration_days = -1
ca
signing_key
cert_signing_key
crl_signing_key
_EOF_
# Create template for users SSL certs
cat << _EOF_ > "${CERTS_DIR}"/users.cfg
organization = $SRV_CN
cn = Example User
uid = exampleuser
expiration_days = -1
tls_www_client
signing_key
encryption_key
_EOF_
# Create template for server self-signed SSL cert
cat << _EOF_ > "${SSL_DIR}"/server.tmpl
cn = $SRV_CA
dns_name = $SRV_CN
organization = $SRV_CN
expiration_days = -1
signing_key
encryption_key #only if the generated key is an RSA one
tls_www_server
_EOF_
# Generate empty revoke file
cat << _EOF_ > "${CERTS_DIR}"/crl.tmpl
crl_next_update = 365
crl_number = 1
_EOF_
# Create connect script which runs for every user connection
cat << _EOF_ > "${OCSERV_DIR}"/connect.sh && chmod +x "${OCSERV_DIR}"/connect.sh
#!/bin/bash
set -Eeuo pipefail
echo "\$(date) User \${USERNAME} Connected - Server: \${IP_REAL_LOCAL} VPN IP: \${IP_REMOTE} Remote IP: \${IP_REAL} Device:\${DEVICE}"
echo "Running iptables MASQUERADE for User \${USERNAME} connected with VPN IP \${IP_REMOTE}"
iptables -t nat -A POSTROUTING -s \${IP_REMOTE}/32 -o eth0 -j MASQUERADE
_EOF_
# Create disconnect script which runs for every user disconnection
cat << _EOF_ > "${OCSERV_DIR}"/disconnect.sh && chmod +x "${OCSERV_DIR}"/disconnect.sh
#!/bin/bash
set -Eeuo pipefail
echo "\$(date) User \${USERNAME} Disconnected - Bytes In: \${STATS_BYTES_IN} Bytes Out: \${STATS_BYTES_OUT} Duration:\${STATS_DURATION}"
_EOF_
# Create script to create new users
cat << _EOF_ > "${OCSERV_DIR}"/ocuser && chmod +x "${OCSERV_DIR}"/ocuser
#!/bin/bash
set -Eeuo pipefail
# Check and set script params
if [[ \$# -eq 2 ]]; then
USER_UID="\$1"
USER_CN="\$2"
elif [[ \$# -eq 3 ]]; then
if [[ "\$1" == "-A" ]]; then
USER_UID="\$2"
USER_CN="\$3"
else
echo "Use -A key as a first param to generate cert for IOS devices" >&2
exit 1
fi
else
echo "Please run script with two params: username and 'Common Username'" >&2
echo "Example: ocuser john 'John Doe'" >&2
echo "For IOS or HarmonyOS devices add -A key as first param in command" >&2
echo "Example: ocuser -A steve 'Steve Jobs'" >&2
exit 1
fi
# Modify user cert template and generate user key, cert and protected .p12 file
sed -i -e "s/^organization.*/organization = \$SRV_CN/" -e "s/^cn.*/cn = \$USER_CN/" -e "s/^uid.*/uid = \$USER_UID/g" "\${CERTS_DIR}"/users.cfg
echo "\$(tr -cd "[:alnum:]" < /dev/urandom | head -c 60)" | ocpasswd -c "\${OCSERV_DIR}"/ocpasswd "\$USER_UID"
certtool --generate-privkey --outfile "\${CERTS_DIR}"/"\${USER_UID}"-privkey.pem
certtool --generate-certificate --load-privkey "\${CERTS_DIR}"/"\${USER_UID}"-privkey.pem --load-ca-certificate "\${CERTS_DIR}"/ca-cert.pem --load-ca-privkey "\${CERTS_DIR}"/ca-key.pem --template "\${CERTS_DIR}"/users.cfg --outfile "\${CERTS_DIR}"/"\${USER_UID}"-cert.pem
if [[ "\$1" == "-A" ]]; then
sleep 1 && certtool --to-p12 --load-certificate "\${CERTS_DIR}"/"\${USER_UID}"-cert.pem --load-privkey "\${CERTS_DIR}"/"\${USER_UID}"-privkey.pem --pkcs-cipher 3des-pkcs12 --hash SHA1 --outder --outfile "\${SECRETS_DIR}"/"\${USER_UID}".p12
else
sleep 1 && certtool --load-certificate "\${CERTS_DIR}"/"\${USER_UID}"-cert.pem --load-privkey "\${CERTS_DIR}"/"\${USER_UID}"-privkey.pem --pkcs-cipher aes-256 --to-p12 --outder --outfile "\${SECRETS_DIR}"/"\${USER_UID}".p12
fi
_EOF_
# Add revoke script
cat << _EOF_ > "${OCSERV_DIR}"/ocrevoke && chmod +x "${OCSERV_DIR}"/ocrevoke
#!/bin/bash
set -Eeuo pipefail
if [[ ! -e "\${CERTS_DIR}"/crl.tmpl ]]; then
cat << __EOF__ > "\${CERTS_DIR}"/crl.tmpl
crl_next_update = 365
crl_number = 1
__EOF__
fi
if [[ \$# -eq 1 ]]; then
if [[ "\$1" == "HELP" ]]; then
echo "Usage:
CMD to revoke cert of some user: ocrevoke <exist_user>
CMD to apply current revoked.pem: ocrevoke RELOAD
CMD to reset all revokes: ocrevoke RESET
CMD to print this help: ocrevoke HELP"
elif [[ "\$1" == "RESET" ]]; then
certtool --generate-crl --load-ca-privkey "\${CERTS_DIR}"/ca-key.pem --load-ca-certificate "\${CERTS_DIR}"/ca-cert.pem --template "\${CERTS_DIR}"/crl.tmpl --outfile "\${CERTS_DIR}"/crl.pem
occtl reload
elif [[ "\$1" == "RELOAD" ]]; then
certtool --generate-crl --load-ca-privkey "\${CERTS_DIR}"/ca-key.pem --load-ca-certificate "\${CERTS_DIR}"/ca-cert.pem --load-certificate "\${CERTS_DIR}"/revoked.pem --template "\${CERTS_DIR}"/crl.tmpl --outfile "\${CERTS_DIR}"/crl.pem
else
USER_UID="\$1"
cat "\${CERTS_DIR}"/"\${USER_UID}"-cert.pem >> "\${CERTS_DIR}"/revoked.pem
certtool --generate-crl --load-ca-privkey "\${CERTS_DIR}"/ca-key.pem --load-ca-certificate "\${CERTS_DIR}"/ca-cert.pem --load-certificate "\${CERTS_DIR}"/revoked.pem --template "\${CERTS_DIR}"/crl.tmpl --outfile "\${CERTS_DIR}"/crl.pem
occtl reload
fi
else
echo "Usage:
CMD to revoke cert of some user: ocrevoke <exist_user>
CMD to apply current revoked.pem: ocrevoke RELOAD
CMD to reset all revokes: ocrevoke RESET
CMD to print this help: ocrevoke HELP"
fi
_EOF_
# Server certificates generation
certtool --generate-privkey --outfile "${CERTS_DIR}"/ca-key.pem
certtool --generate-self-signed --load-privkey "${CERTS_DIR}"/ca-key.pem --template "${CERTS_DIR}"/ca.tmpl --outfile "${CERTS_DIR}"/ca-cert.pem
certtool --generate-crl --load-ca-privkey "${CERTS_DIR}"/ca-key.pem --load-ca-certificate "${CERTS_DIR}"/ca-cert.pem --template "${CERTS_DIR}"/crl.tmpl --outfile "${CERTS_DIR}"/crl.pem
if [[ ! -e "${SSL_DIR}"/live/"${SRV_CN}"/privkey.pem && ! -e "${SSL_DIR}"/live/"${SRV_CN}"/fullchain.pem ]]; then
certtool --generate-privkey --outfile "${SSL_DIR}"/live/"${SRV_CN}"/privkey.pem
certtool --generate-certificate --load-privkey "${SSL_DIR}"/live/"${SRV_CN}"/privkey.pem --load-ca-certificate "${CERTS_DIR}"/ca-cert.pem --load-ca-privkey "${CERTS_DIR}"/ca-key.pem --template "${SSL_DIR}"/server.tmpl --outfile "${SSL_DIR}"/live/"${SRV_CN}"/fullchain.pem
fi
# Start ocserv service
echo "Starting OpenConnect Server"
/usr/sbin/ocserv --foreground || { echo "Starting failed" >&2; exit 1; }
openconnect.service
- файл unit'а systemd для автозапуска[Unit]
Description=OpenConnect VPN service
Requires=docker.service
After=docker.service
[Service]
Restart=always
RestartSec=5
WorkingDirectory=/opt/openconnect
ExecStart=/usr/bin/docker compose up
ExecStop=/usr/bin/docker compose down
[Install]
WantedBy=multi-user.target
ssl_update.sh
- bash скрипт обновления SSL сертификатов от Let's Encrypt#!/usr/bin/env bash
set -e
WORK_DIR="/opt/openconnect"
CONTAINER_NAME="openconnect"
to_log () {
local text="$1"
echo "[$(date '+%F %T')] ${text}"
}
cd "$WORK_DIR" || exit 1
if [[ -r ./docker-compose.yml ]]; then
to_log "Run certbot service container"
docker compose up certbot
sleep 3
to_log "Reload ocserv config"
docker exec "$CONTAINER_NAME" occtl reload
to_log "Delete all unused docker images"
docker system prune -af
fi
Переопределение переменных: домен, имя сервера, email и пр.
Теперь необходимо задать переменные окружения, которые будут использованы для автоматической подстановки при сборке и запуске контейнера. Для этого открываем любым редактором файл .env
, лежащий в директории с файлами проекта:
Файл
.env
обычно используется для определения переменных окружения, которые считываются утилитой docker-compose, при обращении к ним в файле описания сервисов –docker-compose.yml
. Также эти переменные мы передадим внутрь контейнера openconnect.
vim .env
Всего 5 переменных:
TZ
– временная зона;SRV_PORT
– сетевой порт подключения к серверу;SRV_CN
– тут задаём доменное имя, которое указывает на Linux сервер (если домена нет, то указываем произвольное имя);SRV_CA
– названия центра сертификации для самоподписанных сертификатов (произвольное, задать обязательно);USER_EMAIL
– адрес электронной почты, если используется доменное имя. Данный параметр обязателен для получения SSL сертификатов от letsencrypt.
В комментариях подсказали, что в случае подключения к ocserv роутером Keenetic, “то порт в
.env
нужно ставить дефолтный 443 (который не нужно указывать в строке подключения)“. Иные порты, данный роутер, игнорирует.
Сохраняем файл и выходим:
:wq
К слову, в качестве консольного редактора я предпочитаю vim/neovim. Подробнее про этот редактор смотрите статьи по соответствующему тегу: Vim/Neovim.
Сборка образа и первый запуск контейнера с помощью docker-compose
Если у вас нет домена, переходите к шагам внутри спойлера ниже: “Клик сюда, если домена нет”
Теперь просто запускаем команду сборки образа с последующим запуском контейнера в фоновом режиме и после подключаемся к стандартному выводу docker-compose:
docker compose up -d && docker compose logs -f
Обратите внимание, что сервис openconnect, описанный в файле docker-compose.yml намеренно ограничен в аппаратных ресурсах на использование
cpus: '0.50'
иmemory: 200M
, т.е максимально разрешенное использование CPU составляет 50% одного ядра и 200 мб RAM. При необходимости скорректируйте данные параметры в соответствии со своими потребностями.Подробнее про лимиты ресурсов сервисов при использовании docker-compose читайте тут.
В docker-compose.yml
параметром depends-on
указан порядок запуска сервисов. Сперва отрабатывает certbot
(Let’s Encrypt), который запрашивает и получает валидные SSL сертификаты, и только затем стартует контейнер с VPN сервером:
Подробную документацию по использованию certbot смотрите тут.
В случае успеха вывод будет, как на скрине выше. Просмотр вывода можно прервать комбинацией Ctrl+C
.
Чтобы убедится, что контейнер запущен выполняем:
docker compose ps
ls -l ./data
ls -l ./data/ssl/live/vpn.r4ven.me/
Должна создаться папка ./data
с файлами сервера + сертификаты, полученные контейнером cerbot.
data
, которое создаёт скрипт ocserv.sh
при первом стартеcerts
– директория с SSL файлами нашего “центра сертификации”;connect.sh
– bash скрипт, который выполняется для каждого клиента при подключении к нашему VPN серверу. Его можно расширять и кастоимизировать под любые ваши нужды. Главное не нарушьте инзначальную функциональность);
#!/bin/bash
set -Eeuo pipefail
echo "$(date) User ${USERNAME} Connected - Server: ${IP_REAL_LOCAL} VPN IP: ${IP_REMOTE} Remote IP: ${IP_REAL} Device:${DEVICE}"
echo "Running iptables MASQUERADE for User ${USERNAME} connected with VPN IP ${IP_REMOTE}"
iptables -t nat -A POSTROUTING -s ${IP_REMOTE}/32 -o eth0 -j MASQUERADE
disconnect.sh
– bash скрипт, который выполняется при отключении пользователя;
#!/bin/bash
set -Eeuo pipefail
echo "$(date) User ${USERNAME} Disconnected - Bytes In: ${STATS_BYTES_IN} Bytes Out: ${STATS_BYTES_OUT} Duration:${STATS_DURATION}"
ocserv.conf
– файл конфигурации нашего VPN сервера. Также можете модифицировать под свои нужды. К слову, полный конфиг ocserv с описанием доступных параметров можно изучить на официальном GitLab проекта по ссылке;
auth = "certificate"
#auth = "plain[passwd=/etc/ocserv/ocpasswd]"
#enable-auth = "certificate"
tcp-port = 443
socket-file = /run/ocserv-socket
server-cert = /etc/ocserv/ssl/live/"example.com"/fullchain.pem
server-key = /etc/ocserv/ssl/live/"example.com"/privkey.pem
ca-cert = /etc/ocserv/certs/ca-cert.pem
isolate-workers = true
max-clients = 20
max-same-clients = 2
rate-limit-ms = 200
server-stats-reset-time = 604800
keepalive = 10
dpd = 120
mobile-dpd = 1800
switch-to-tcp-timeout = 25
try-mtu-discovery = false
cert-user-oid = 0.9.2342.19200300.100.1.1
tls-priorities = "NORMAL:%SERVER_PRECEDENCE:%COMPAT:-VERS-SSL3.0:-VERS-TLS1.0:-VERS-TLS1.1:-VERS-TLS1.3"
auth-timeout = 1000
min-reauth-time = 300
max-ban-score = 100
ban-reset-time = 1200
cookie-timeout = 600
deny-roaming = false
rekey-time = 172800
rekey-method = ssl
connect-script = /etc/ocserv/connect.sh
disconnect-script = /etc/ocserv/disconnect.sh
use-occtl = true
pid-file = /run/ocserv.pid
log-level = 1
device = vpns
predictable-ips = true
default-domain = "example.com"
ipv4-network = 10.10.10.0
ipv4-netmask = 255.255.255.0
tunnel-all-dns = true
dns = 8.8.8.8
ping-leases = false
config-per-user = /etc/ocserv/config-per-user/
cisco-client-compat = true
dtls-legacy = true
client-bypass-protocol = false
crl = /etc/ocserv/certs/crl.pem
ocuser
– bash скрипт для создания новых пользователей и сертификатов подключения.p12
;
#!/bin/bash
set -Eeuo pipefail
# Check and set script params
if [[ $# -eq 2 ]]; then
USER_UID="$1"
USER_CN="$2"
elif [[ $# -eq 3 ]]; then
if [[ "$1" == "-A" ]]; then
USER_UID="$2"
USER_CN="$3"
else
echo "Use -A key as a first param to generate cert for IOS devices" >&2
exit 1
fi
else
echo "Please run script with two params: username and 'Common Username'" >&2
echo "Example: ocuser john 'John Doe'" >&2
echo "For IOS or HarmonyOS devices add -A key as first param in command" >&2
echo "Example: ocuser -A steve 'Steve Jobs'" >&2
exit 1
fi
# Modify user cert template and generate user key, cert and protected .p12 file
sed -i -e "s/^organization.*/organization = $SRV_CN/" -e "s/^cn.*/cn = $USER_CN/" -e "s/^uid.*/uid = $USER_UID/g" "${CERTS_DIR}"/users.cfg
echo "$(tr -cd "[:alnum:]" < /dev/urandom | head -c 60)" | ocpasswd -c "${OCSERV_DIR}"/ocpasswd "$USER_UID"
certtool --generate-privkey --outfile "${CERTS_DIR}"/"${USER_UID}"-privkey.pem
certtool --generate-certificate --load-privkey "${CERTS_DIR}"/"${USER_UID}"-privkey.pem --load-ca-certificate "${CERTS_DIR}"/ca-cert.pem --load-ca-privkey "${CERTS_DIR}"/ca-key.pem --template "${CERTS_DIR}"/users.cfg --outfile "${CERTS_DIR}"/"${USER_UID}"-cert.pem
if [[ "$1" == "-A" ]]; then
sleep 1 && certtool --to-p12 --load-certificate "${CERTS_DIR}"/"${USER_UID}"-cert.pem --load-privkey "${CERTS_DIR}"/"${USER_UID}"-privkey.pem --pkcs-cipher 3des-pkcs12 --hash SHA1 --outder --outfile "${SECRETS_DIR}"/"${USER_UID}".p12
else
sleep 1 && certtool --load-certificate "${CERTS_DIR}"/"${USER_UID}"-cert.pem --load-privkey "${CERTS_DIR}"/"${USER_UID}"-privkey.pem --pkcs-cipher aes-256 --to-p12 --outder --outfile "${SECRETS_DIR}"/"${USER_UID}".p12
fi
ocrevoke
– bash скрипт для отзыва сертификатов пользователей;
#!/bin/bash
set -Eeuo pipefail
if [[ ! -e "${CERTS_DIR}"/crl.tmpl ]]; then
cat << __EOF__ > "${CERTS_DIR}"/crl.tmpl
crl_next_update = 365
crl_number = 1
__EOF__
fi
if [[ $# -eq 1 ]]; then
if [[ "$1" == "HELP" ]]; then
echo "Usage:
CMD to revoke cert of some user: ocrevoke <exist_user>
CMD to apply current revoked.pem: ocrevoke RELOAD
CMD to reset all revokes: ocrevoke RESET
CMD to print this help: ocrevoke HELP"
elif [[ "$1" == "RESET" ]]; then
certtool --generate-crl --load-ca-privkey "${CERTS_DIR}"/ca-key.pem --load-ca-certificate "${CERTS_DIR}"/ca-cert.pem --template "${CERTS_DIR}"/crl.tmpl --outfile "${CERTS_DIR}"/crl.pem
occtl reload
elif [[ "$1" == "RELOAD" ]]; then
certtool --generate-crl --load-ca-privkey "${CERTS_DIR}"/ca-key.pem --load-ca-certificate "${CERTS_DIR}"/ca-cert.pem --load-certificate "${CERTS_DIR}"/revoked.pem --template "${CERTS_DIR}"/crl.tmpl --outfile "${CERTS_DIR}"/crl.pem
else
USER_UID="$1"
cat "${CERTS_DIR}"/"${USER_UID}"-cert.pem >> "${CERTS_DIR}"/revoked.pem
certtool --generate-crl --load-ca-privkey "${CERTS_DIR}"/ca-key.pem --load-ca-certificate "${CERTS_DIR}"/ca-cert.pem --load-certificate "${CERTS_DIR}"/revoked.pem --template "${CERTS_DIR}"/crl.tmpl --outfile "${CERTS_DIR}"/crl.pem
occtl reload
fi
else
echo "Usage:
CMD to revoke cert of some user: ocrevoke <exist_user>
CMD to apply current revoked.pem: ocrevoke RELOAD
CMD to reset all revokes: ocrevoke RESET
CMD to print this help: ocrevoke HELP"
fi
secrets
– директория, в которой хранятся сертификаты (.p12
) пользователей после их создания;ssl
– SSL сертификаты openconnect сервера, самоподписанные или полученные от проекта Let’s Encrypt.
Также проверяем прослушиваемый и проброшенный в хост контейнером TCP порт, такой командой:
ss -tnap | grep -E '43443'
Всё, сервер запущен и работает. Осталось создать пользователей и настроить клиентское подключение.
В таком случае правим файл описания сервисов docker-compose.yml
:
vim docker-compose.yml
В этом файле необходимо закоментировать сервис certbot
и директиву depends_on
у сервиса openconnect
, как показано на скриншоте:
После чего запускаем сборку и смотрим вывод:
docker compose up -d && docker compose logs -f
Если все отработало корректно, выходим из режима просмотра логов клавишей Ctrl+C
и проверяем запущенный сервис:
docker compose ps
ss -tnap | grep -E '43443'
Также проверяем наличие системных файлов ocserv:
ls -l ./data
Отлично, идём дальше.
Создание пользователей
Пару слов про способы авторизации на OpenConnect сервере.
Т.к.
ocserv
популярен в корпоративном мире, он поддерживает множество способов авторизации пользователей. От банального логина/пароля, до интеграции с централизованными инструментами, такими, как LDAP/Active Directory, PAM, Radius и пр. Из коробки даже имеется поддержка двухфакторной аутентификации на основе токенов. Для нас это уже излишне, но вас никто не ограничивает)В качестве надежного и в тоже время удобного способа авторизации я выбрал подключение на основе пользовательского сертификата, который упаковывается в шифрованный (при указании пароля) файл с расширением
.p12
. В результате со стороны клиента необходимо будет лишь указать путь до файла сертификата и пароль для его расшифровки. Обычно клиенты способны запоминать пароли и вводить их более не требуется.Стоит отметить, что для создания пользователя ему все таки необходимо задать пароль, хоть и такой способ авторизации отключен в конфиг файле сервера. Если вы проявили любопытство и заглянули в содержимое скрипта создания пользователей
ocuser
, то могли заметить команду генерации псевдослучайной строки: tr -cd “[:alnum:]” < /dev/urandom | head -c 60Вывод которой задается в качестве пароля для пользователя при его создании. Небольшая подстраховка)
Создание пользователей для Linux/Windows/Android
И так, создаём пользователя такой командой:
docker exec -it openconnect ocuser ivan 'Ivan Cherniy'
Где ivan – это имя и идентификатор пользователя в ocserv (старайтесь указывать идентификатор без пробелов, дабы избежать ошибок), а ‘Ivan Cherniy’ это полное имя пользователя, которое будет отображено в метаданных пользовательского сертификата.
После ввода команды необходимо будет в интерактивном режиме указать имя файла сертификата и пароль для его шифрования.
Если команда создания отработала успешно, для указанного пользователя будет создан файл сертификата .p12
в директории secrets
:
ls -l ./data/secrets
Список пользователей можно посмотреть в файле ./data/ocpasswd
.
Создание пользователей для HarmonyOS/iOS
При создании пользователей для мобильных ОС от Huawei и Apple команде ocuser
необходимо передать ключ -A
. В таком случае будет использован другой алгоритм шифрования сертификата.
Пример команды:
docker exec -it openconnect ocuser -A steve 'Steve Jobs'
Проверяем созданный сертификат:
ls -l ./data/secrets
Если для устройств под управлением HarmonyOS и iOS сгенерировать сертификаты без ключа -A
, то при настройке клиентского подключения, вы будете получать ошибку на этапе импорта сертификата.
Note that the Ciso AnyConnect app on iOS doesn’t support AES-256 cipher. It will refuse to import the client certificate. If the user is using iOS device, then you can choose the
linuxbabe.com3des-pkcs12
cipher.
Скачивание файла p12
Скачиваем созданный файл сертификата пользователя удобным для вас способом. Например:
cp ./data/secrets/ivan.p12 /tmp
А на клиентской машине:
scp vpn.r4ven.me:/tmp/ivan.p12 .
ls -l ivan.p12
На этом этапе уже можно переходить к настройке клиентов. Но если вам нужны персональные настройки каждого пользователя, такие как IP адрес клиента, маршруты, DNS, а также настройка автозапуска сервиса OpenConnect и автопродление SSL сертификатов, то идём последовательно.
Опционально: индивидуальные настройки ocserv для каждого пользователя
Создаём системную директорию config-per-user
и отдельный файл под каждого пользователя, с таким же именем, какое указывали при создании (если забыли, посмотрите в файле ./data/ocpasswd
):
mkdir ./data/config-per-user
vim ./data/config-per-user/ivan
Название параметров в файле конфига пользователя в большинстве своем идентичны основному конфигу сервера:
explicit-ipv4 = 10.10.10.5
route = 10.10.10.50/32
route = 64.233.164.139/32
dns = 1.1.1.1
В примере выше я задаю для пользователя ivan конкретный IP (из подсети, что указана в основном конфиге), отдельные маршруты и адрес DNS сервера.
Upd. Если вы предпочитаете повысить уровень конфиденциальности, то при тунелировании трафика желательно иметь собственный DNS сервер. Вот инструкция по его настройке и интеграции с OpenConnect:
Поднимаем свой DNS сервер Unbound и блокировщик рекламы Pihole в docker
При такой конфигурации данный пользователь будет обращаться к указанным ресурсам через VPN сервер, а остальной трафик будет идти через его шлюз по умолчанию (или нет, в зависимости от настроек сети).
После внесения изменений в конфиг необходимо выполнить перечитывание файла конфигурации процессом ocserv
внутри контейнера. Делается такой командой:
docker exec -it openconnect occtl reload
Автозапуск сервера OpenConnect с помощью systemd
Теперь настроим автоматический запуск/перезапуск нашего VPN сервера при помощи системы инициализации systemd.
Из директории с файлами проекта копируем файл openconnect.service
в системную директорию systemd, перезагружаем конфигурацию systemd и останавливаем наши запущенные контейнеры:
cp ./openconnect.service /etc/systemd/system/
systemctl daemon-reload
docker compose down
Теперь активируем автозапуск и пробуем выполнить старт контейнера с OpenConnect сервером как сервис systemd:
systemctl enable --now openconnect
systemctl status openconnect
docker compose ps
Все отлично.
Настройка автоматического обновления SSL сертификатов (если есть домен)
Особенностью сертификатов от центра сертификации Let’s Encrypt является то, что они выдаются на 3 месяца. В такой ситуации предусмотрительно будет настроить автоматическое продление. Для этого добавляем в системный планировщик заданий cron
запуск скрипта ssl_update.sh
раз в неделю, например, в воскресенье в 3 часа утра:
{ crontab -l; echo "0 3 * * 0 /opt/openconnect/ssl_update.sh"; } | crontab -
crontab -l
Синтаксис фигурных скобок в bash
{ command1; command2 }
позволяет объединить несколько команд в одну. Тут мы с его помощью первой командой выводим список заданий cron текущего пользователя, второй командой выводим строку с новым заданием и потом с помощью механизма перенаправления передаем весь вывод командеcrontab -
. Если командеcrontab -
передать только одну задачу, она затрет все предыдущие. Так делать не стоит)Хоть и в моём примере нет других заданий в планировщике, команда специально составлена с “защитой от дурака”)
Для проверки работоспособности скрипта продления сертификатов, можем выполнить его вручную:
UPD 16.05.2024
В команде обновления certbot заменил параметр
--force-renewal
на--keep-until-expiring
, который выполняет проверку срока действия сертификата, и если он не близок к завершению, то обновление сертификатов не происходит.
/opt/openconnect/ssl_update.sh
Скрипт также почистит дисковое пространство от неиспользуемых образов docker:
Проверяем время обновления SSL файлов:
ls -l ./data/ssl/live/vpn.r4ven.me/
Так, вроде все хорошо)
Обратите внимание, что если слишком часто обновлять сертификаты, то certbot (letsencrypt) будет выпадать в ошибку по этой причине.
Настройка подключения клиентов
Настройка OpenConnect клиента для Linux
Способ №1: менеджер сетевых соединений – NetworkManager
Если вы пользователь популярных дестопных дистрибутивов, то вероятнее всего вашей сетевой подсистемой управлет NetworkManager.
Сразу отмечу, что данный способ предпочтителен для десктопных систем, т.к. при использовании NM для подключения к ocserv не происходит конфликтов системы DNS.
В случае deb based систем, выполняем установку специального пакета-плагина NetworkManager для подключения к ocserv.
По традиции, продемонстрирую на примере Linux Mint 21 Cinnamon (Ubuntu 22.04).
Команда установки плагина:
sudo apt update && sudo apt install network-manager-openconnect-gnome
После установки:
- Заходим в “Сетевые соединения” через трей или любым удобным вам способом;
- Затем нажимаем кнопку добавления нового соединения, в списке выбираем “Cisco AntConnect or openconnect (OpenConnect)” и нажимаем “Создать“;
- В открвшемся окошке указываем
- “Имя соединения” – произвольное
- “Шлюз” – DNS имя нашего сервера или его IP адрес (если без домена) + порт подключения. Пример:
vpn.r4ven.me:43443
- В разделе “Аутентификации по сертификату” в параметре “Сертификат пользователя” выбираем наш
.p12
файл, который мы сгнерировали на этапе создания пользователя. В старых версиях nmapplet’а есть баг, когда при выборе сертификата он не видит файлы с расширенияем.p12
. В таком случае просто перетащите (drag-and-drop) файл с файлового менеджера в поле данного параметра, как показано на скриншоте ниже; - После нажимаем “Сохранить”.
Если все сделано корректно, пробуем подключиться к нашему серверу. Кликаем на апплет сетевых соединений, затем на переключатель “VPN подключения“. Должно появиться окошко, где нас любезно попросят ввести пароль от файла-сертификата (если вы его задавали), который мы устанавливали на этапе создания пользователей ocserv. Ставим галочку “Сохранить пароль” и затем “Подключиться“. При успешном соединении на рабочем столе появится соответствующее уведомление:
Также новое подключение для NetworkManager можно создать с помощью консольной утилиты nmcli. Вот пример:
nmcli connection add \
type vpn \
con-name "vpn.r4ven.me" \
ifname '*' \
vpn-type openconnect \
vpn.data "gateway=vpn.r4ven.me:43443, usercert=/home/ivan/ivan.p12"
Пробуем подключиться:
nmcli connection up vpn.r4ven.me
Успешно.
Посмотреть сетевые параметры нашего VPN подключения можно с помощью команд:
ip -c address
nmcli
Узнать внешний IP в консоли можно такой командой:
curl ifconfig.me
В выводе команды будет одна строка с вашим внешним IP адресом.
Способ №2: утилита командной строки – openconnect
Другой способ клиентского подключения в Linux, где отсутствует NetworkManager – это одноименная консольная утилита openconnect
. Такой способ подключения чаще всего используется для клиентов без GUI, т.е. на Linux серверах.
Пример подключения все также для deb based систем.
Выполняем установку клиентского пакета openconnect
:
sudo apt update && sudo apt install openconnect
Подключится к серверу OpenConnect можно такой командой:
# если с доменом
sudo openconnect -c /home/ivan/ivan.p12 vpn.r4ven.me:43443 <<< $(echo "password"$'\n')
# если без домена (с доп. подтверждением самоподписанного сертификата)
sudo openconnect -c /home/ivan/ivan.p12 12.345.67.89:43443 <<< $(echo "password"$'\n'yes$'\n')
Где ключу -c
передается путь до файла сертификата .p12
а с помощью механизма here string (<<<
) и подстановки передается вывод команды echo "password"$'\n'
, которая выводит текстовую строку и выполняет перевод строки, в данном случае, это имитация ввода Enter, чтобы не вводить вручную.
Замените password
на ваш пароль от сертификата.
Для автоматизации процесса подключения с помощью утилиты openconnect я написал небольшой скрипт. Изучить его можно в статье: Пишем bash скрипт для подключения к OpenConnect VPN серверу.
Результат команды подключения должен быть примерно таким:
Если вы используете подключение с помощью утилиты
openconnect
на Linux сервере, и планируете туннелировать весь его трафик, то необходимо добавить специальные правила маршрутизации, без которых вы потеряете доступ до своего сервера, в т.ч. и по SSH:ip rule add table 128 from <public-ip> ip route add table 128 to <public_ip_subnet> dev <ineteface_name> ip route add table 128 default via <gateway>
Где:
- 128 – имя новой таблицы маршрутизации;
- <piblic-ip> – основной IP вашего сервера;
- <public_ip_subnet> – подсеть основного IP сервера;
- <interface_name> – имя физического интерфейса, к которому подключен основной IP;
- <gateway> – шлюз сети, через который ходит основной IP.
Настройка OpenConnect клиента для Windows/MacOS
Для настройки клиента на Windows и MacOS необходимо скачать графическую программу-клиент с официального сайта https://gui.openconnect-vpn.net/download/ или на странице релизов в GitLab: https://gitlab.com/openconnect/openconnect-gui/-/releasesGitLab:
Далее устанавливаем программу на рабочий стол Windows обычными кликами “Далее, далее..”. А вот как установить клиента на MacOS я вам не подскажу. Тут уже вы сами изучите вопрос, ссылки на все материалы будут внизу.
После установки и запуска программы OpenConnectVPN выполняем настройку, как показано на скриншотах ниже. Отмечу лишь один момент: обязательно активируйте параметр “Disable UDP“, т.к. в нашей конфигурации сервера его использование отключено:
Проверяем сетевые параметры:
Все работает.
Настройка OpenConnect клиента для Android/HarmonyOS
Для мобильных ОС необходимо установить приложение Cisco Secure Client-AnyConnect.
Пожалуйста, обратите внимание, что данное мобильное приложение не имеет открытый исходный код. При его использовании придется довериться разработчику.
Существует open source мобильный клиент, но его разработка прекратилась n-е количество лет назад и у меня оно работало некорректно.
После открытия приложения необходимо создать подключение, указать адрес шлюза + порт и импортировать сертификат пользователя. Пошаговая инструкция показана на скриншотах ниже:
Настройка OpenConnect клиента для iOS
Инструкция любезно предоставлена подписчиком в Вороньем чате
Скачиваешь файл на телефон. Открываешь файлы и зажимаешь этот значок (просто при тычке он будет писать техническое сообщение о том, что профиль загружен). Выбираешь пункт поделиться – Еще – Листаешь в самый низ, там будет Any Connect. Открывается приложение и запрашивает пароль. Вводишь и тыкаешь импорт.
И дальше уже по твоей схеме в настройках туннеля выбираешь сертификат.
Ну вот, вроде бы всё.
Блокировка пользователей путём отзыва сертификата
При необходимости заблокировать какого-то пользователя, сделать это можно с помощью отзыва его SSL сертификата.
Если вы только что подняли ocserv по этой инструкции, просто воспользуйтесь командой ocrevoke
внутри контейнера:
# отозвать сертификат пользователя ivan
docker exec -it openconnect ocrevoke ivan
# перечитать черный список пользователей
docker exec -it openconnect ocrevoke RELOAD
# удалить всех из черного списка
docker exec -it openconnect ocrevoke RESET
Где вместо ivan
подставьте имя пользователя, доступ по сертификату которого нужно заблокировать.
При следующем подключении пользователь получит ошибку:
Если же ваш сервер был поднят по этой инструкции некоторое время назад, придется выполнить немного ручной работы.
Сперва в конец конфига ./data/ocserv.conf
добавляем параметр:
crl = /etc/ocserv/certs/crl.pem
Затем выполняем команды:
cat ./data/certs/ivan-cert.pem >> ./data/certs/revoked.pem
docker exec -it openconnect certtool --generate-crl --load-ca-privkey /etc/ocserv/certs/ca-key.pem --load-ca-certificate /etc/ocserv/certs/ca-cert.pem --load-certificate /etc/ocserv/certs/revoked.pem --template /etc/ocserv/certs/crl.tmpl --outfile /etc/ocserv/certs/crl.pem
docker exec -it openconnect occtl reload
Где вместо ivan
в первой команде подставьте имя пользователя, доступ по сертификату которого нужно заблокировать.
Как вы догадались, ключевым тут является файл ./data/certs/revoked.pem
, куда добавляются публичные сертификаты пользователей <user_name>-cert.pem
.
Для очистки всего черного списка выполните:
docker exec -it openconnect certtool --generate-crl --load-ca-privkey /etc/ocserv/certs/ca-key.pem --load-ca-certificate /etc/ocserv/certs/ca-cert.pem --template /etc/ocserv/certs/crl.tmpl --outfile /etc/ocserv/certs/crl.pem
docker exec -it openconnect occtl reload
При отсутствии необходимости данного функционала, просто закомментируйте строку crl = /etc/ocserv/certs/crl.pem
в конфиге ocserv.
Полезные команды docker/docker-compose/ocserv
Небольшой список команд, который может вам пригодится при настройке и обслуживании OpenConnect сервера.
# показывает список всех доступных образов docker
docker image ls
# показывает список всех контейнеров docker, включая работающие и остановленные
docker container ls -a
# удаляет все ненужные ресурсы docker, такие как неиспользуемые контейнеры, образы, сети и тома, без запроса подтверждения
docker system prune -af
# показывает статус и информацию о контейнерах, запущенных с помощью docker-compose
docker compose ps
# запускает контейнеры из файла docker-compose.yml в фоновом режиме
docker compose up -d
# останавливает и удаляет все контейнеры, сети, запущенные с помощью docker-compose
docker compose down
# отображает стандартный вывод контейнеров, запущенных с помощью docker-compose
docker compose logs -f
# перезапуск контейнера с именем "openconnect"
docker compose restart openconnect
# запускает интерактивную оболочку bash внутри контейнера "openconnect"
docker exec -it openconnect bash
# выводит справку по команде утилиты управления VPN сервером
docker exec -it openconnect occtl --help
# перечитывает файл конфигурации ocserv
docker exec -it openconnect occtl reload
# показывает статус ocserv
docker exec -it openconnect occtl show status
# показывает список пользователей ocserv
docker exec -it openconnect occtl show users
# показывает список активных сеансов ocserv
docker exec -it openconnect occtl show sessions all
# создает пользователя с id "ivan" и именем "Ivan Cherniy" для ocserv
docker exec -it openconnect ocuser ivan 'Ivan Cherniy'
Авторизация по логину/паролю
В некоторых случаях может понадобиться включение возможности авторизации по логину и паролю. Например при настройке openconnect на роутерах Keenetic, который не поддерживает подключение на основе сертификата.
В файле конфигурации ocserv – /opt/openconnect/data/ocserv.conf
разрешаем авторизацию по логину/паролю, путем добавления такого параметра:
enable-auth = "plain[passwd=/etc/ocserv/ocpasswd]"
Стоит отметить, что при такой конфигурации авторизация по сертификату останется.
Затем перезапускаем сервер:
docker compose restart openconnect
Далее создаём нового пользователя:
docker exec -it openconnect ocpasswd exampleuser
И задаем ему пароль.
После чего можно подключаться с помощью пары логин/пароль.
Обязательно указывайте только длинные и сложные пароли.
Заключение
Фух! Создание данного материала заняло приличное количество времени и сил, но они было потрачены не зря. В процессе подготовки я узнал много нового и про работу сети в Linux, и множество нюансов bash при написании скриптов проекта и многое другое.
В будущем буду писать статьи по развертыванию различных персональных сервисов, и для доступа к ним будет использоваться VPN подключения на базе OpenConnect.
Если у вас возникли трудности с настройкой или остались вопросы, то смело оставляйте комментарии к данной статье или в нашем чате телеги: @r4ven_me_chat.
Также не забудьте подписаться на наш телеграм канал:@r4ven_me. Ссылки на все новые статьи появляются там в момент публикации.
Спасибо, что вместе со мной осилили данную статью. Успехов вам!
Используемые материалы
- Файлы проекта из статьи на моём GitHub
- Официальный репозиторий GitLab проекта ocserv (EN)
- Официальный репозиторий GitLab gui клинетов (EN)
- Пример файла ocserv.conf с описанием параметров (EN)
- Инструкция: Set Up OpenConnect VPN Server (ocserv) on Ubuntu 20.04 with Let’s Encrypt (EN)
- Инструкция: Ocserv Advanced (Split Tunneling, IPv6, Static IP, Per User Configs, Virtual Hosting) (EN)
- Инструкция: Setting up my OpenConnect Server (EN)
- Официальный мануал: GitHub (EN)
- Документация по certbot: User Guide — Certbot (EN)
camouflage поддерживает эта версия ?
Добрый день.
В статье устанавливается стабильная версия ovserv – 1.1, из репозитория Debian. Она не поддерживает указанный вами параметр.
Но по просьбе подписчиков, я вручную собрал отдельный образ с последней версий ocserv – 1.3.
Вы можете использовать ее, отредактировав параметр:
Или же собрать его самостоятельно по отдельной статье:
Собираем OpenConnect (ocserv) версии 1.3 из исходников в Debian 12 + docker образ
В статье есть упоминание про подключение к серверу с Keenetic… Так вот в последней текущей бете (а может и во всех), Keenetic не умеет сохранять адрес с портом… он его просто удаляет.. Ну и не коннектится, соответственно.
Имеет смысл добавить комментарий, что если планируется подсоединяться через эти роутеры, то порт в .env нужно ставить дефолтный 443 (который не нужно указывать в строке подключения)
Привет!
Полезная информация. Добавил в статью. Благодарю👍
Добрый день.
А есть какие-то требования по настройке ufw и iptables?
Я второй день мучаюсь этими настройками.
Приветствую!
Пожалуйста, уточните, в чем именно проблема? ufw / iptables на хостовой системе или в контейнере?
Т.к. по моей инструкции контейнер с ocserv разворачивается с помощью docker, то в таком случае, при пробрасывании порта из контейнера в хостовую систему, docker самостоятельно добавляет нужные правила в iptables / ufw, в свою отдельную цепочку правил.
Вы можете в этом убедиться выполнив:
Или
Для более оперативной связи, рекомендую позадавать вопросы в нашем чате: @r4ven_me_chat. У нас там дружелюбное сообщество 🙂
У меня скорость на Download 0.70 а на Upload 27 мбит, как исправить скорость?
Зависит от большого числа факторов. Чаще всего дело не в ocserv, а в канале связи до вашего сервера.
Как можно проверить скорость Download|Upload в реальном времени с помощью ssh, чтобы определить проблема в VPN или нет.
Установите утилиту контроля пайплайна:
Затем отключите VPN и проверьте скорость download:
Затем upload:
Затем включите VPN и проверьте снова. Если без VPN скорость намного выше, значит дело в канале.
При коннекте получаю такую ошибку,хотя ссл рукопожатие таки проходит.
траффик проксирую в контейнер через нжинкс,но пробовал и хапрокси.результат аналогичный.Не могу понять в чем дело
Покажите, как настроили nginx для проксирования.
Если подключаться напрямую, без nginx, все работает корректно?
конфиг хапрокси
слушаю 8443 в контейнере хапрокси,отправляю на 9443 ocerv
напрямую без прокси
| 198c | Failed to open HTTPS connection to example.com
| 198c | Authentication error; cannot obtain cookie
| 2d98 | Disconnected
с хапрокси вот такое. Смущает signer not found,но похоже это пол беды
Сообщения из вывода указывают на проблемы с сертификатами локального центр сертификации (CA), которые генерируются скриптом в контейнере при первом запуске.
В директории
Должны быть файлы
Проверьте существуют ли эти файлы.
Если да, проверить файл сертификата можно такой командой:
Вывод должен начинаться примерно так:
Также стоит проверить сертификаты от cerbot, если вы используете подключение по доменному имени.
Рекомендую написать нам в чат телеги: https://t.me/r4ven_me_chat
А то тут можно долго перебирать варианты.
Решение пользователя из чата в телеграм:
Добрый день.
А если порты 80 и 443 уже заняты, то как быть?
Добрый день.
Сообщите пожалуйста подробности.
В статье демонстрируется установка ocserv с использованием 43443 порта, а не 443.
Предположу, что вы хотите поднять ocserv на 443 порту, но при этом он уже используется другими сервисами. В таком случае выходом будет настройка обратного прокси сервера, типа nginx или haproxy.
Тема обратных прокси выходит за рамки данной статьи. Но наши подсписчики в чате телеграм уже обсуждали решение данного вопроса. При желании можете поискать ответ в истории сообщений или задать вопрос😌.