Поднимаем OpenConnect SSL VPN сервер (ocserv) в docker для внутренних проектов

Поднимаем 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). Настройка клиентов и то занимает больше времени.

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 у пользователя, если будете выполнять установку по своему.

Если с какой-то из пунктов отсутствует, возможно вам будут полезны следующие статьи:

В статье про установку 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 3des-pkcs12cipher.

linuxbabe.com

Скачивание файла 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

После установки:

  1. Заходим в “Сетевые соединения” через трей или любым удобным вам способом;
  2. Затем нажимаем кнопку добавления нового соединения, в списке выбираем “Cisco AntConnect or openconnect (OpenConnect)” и нажимаем “Создать“;
  3. В открвшемся окошке указываем
    • Имя соединения” – произвольное
    • Шлюз” – DNS имя нашего сервера или его IP адрес (если без домена) + порт подключения. Пример: vpn.r4ven.me:43443
  4. В разделе “Аутентификации по сертификату” в параметре “Сертификат пользователя” выбираем наш .p12 файл, который мы сгнерировали на этапе создания пользователя. В старых версиях nmapplet’а есть баг, когда при выборе сертификата он не видит файлы с расширенияем .p12. В таком случае просто перетащите (drag-and-drop) файл с файлового менеджера в поле данного параметра, как показано на скриншоте ниже;
  5. После нажимаем “Сохранить”.

Если все сделано корректно, пробуем подключиться к нашему серверу. Кликаем на апплет сетевых соединений, затем на переключатель “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. Ссылки на все новые статьи появляются там в момент публикации.

Спасибо, что вместе со мной осилили данную статью. Успехов вам!

Используемые материалы

Подписаться
Уведомить о
16 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии
Valentin
Valentin
09.09.2024 07:43

camouflage поддерживает эта версия ?

Kirill Tarashev
Kirill Tarashev
11.09.2024 21:23

В статье есть упоминание про подключение к серверу с Keenetic… Так вот в последней текущей бете (а может и во всех), Keenetic не умеет сохранять адрес с портом… он его просто удаляет.. Ну и не коннектится, соответственно.
Имеет смысл добавить комментарий, что если планируется подсоединяться через эти роутеры, то порт в .env нужно ставить дефолтный 443 (который не нужно указывать в строке подключения)

Vladimir
Vladimir
07.10.2024 10:49

Добрый день.
А есть какие-то требования по настройке ufw и iptables?
Я второй день мучаюсь этими настройками.

survland
survland
26.10.2024 15:07

У меня скорость на Download 0.70 а на Upload 27 мбит, как исправить скорость?

Mashrooms Peter
Mashrooms Peter
01.11.2024 13:27

При коннекте получаю такую ошибку,хотя ссл рукопожатие таки проходит.

Got inappropriate HTTP CONNECT response: HTTP/1.1 401 Cookie is not acceptable

Error establishing the CSTP channel

disconnect

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

Mashrooms Peter
Mashrooms Peter
01.11.2024 17:21
Ответ на  Иван Чёрный

конфиг хапрокси

frontend https_frontend
  bind haproxy:8443
  mode tcp
	tcp-request inspect-delay 5s
  tcp-request content accept if { req_ssl_hello_type 1 }
	
    acl host_vpn hdr(host) -i example.com
    use_backend ocserv if { req_ssl_sni -i example.com }

backend ocserv
  mode tcp
  option ssl-hello-chk
  server ocserv openconnect:9443 send-proxy-v2

слушаю 8443 в контейнере хапрокси,отправляю на 9443 ocerv

напрямую без прокси
| 198c | Failed to open HTTPS connection to example.com
| 198c | Authentication error; cannot obtain cookie
| 2d98 | Disconnected

с хапрокси вот такое. Смущает signer not found,но похоже это пол беды

Server certificate verify failed: signer not found
| 4088 | Connected to HTTPS on example.com with ciphersuite (TLS1.3)-(ECDHE-SECP256R1)-(RSA-PSS-RSAE-SHA256)-(AES-256-GCM)
| 4088 | Got HTTP response: HTTP/1.1 200 OK
| 4088 | Connection: Keep-Alive
| 4088 | Content-Type: text/xml
| 4088 | Content-Length: 189
| 4088 | X-Transcend-Version: 1
| 4088 | Set-Cookie: webvpncontext=AEG244K5UOM9lu9jQ4qa69GXypuR9ybC0YNRdqif4EI=; Secure; HttpOnly
| 4088 | Set-Cookie: webvpn=<elided>; Secure; HttpOnly
| 4088 | Set-Cookie: webvpnc=; expires=Thu, 01 Jan 1970 22:00:00 GMT; path=/; Secure; HttpOnly
| 4088 | Set-Cookie: webvpnc=bu:/&p:t&iu:1/&sh:F050A2A4D159D3086B9A5353CC5EDCD6C62F5515; path=/; Secure; HttpOnly
| 4088 | HTTP body length:  (189)
| 4088 | XML POST enabled
| 4088 | SSL negotiation with example.com
| 4088 | Server certificate verify failed: signer not found
| 4088 | Connected to HTTPS on example.com with ciphersuite (TLS1.3)-(ECDHE-SECP256R1)-(RSA-PSS-RSAE-SHA256)-(AES-256-GCM)
| 4088 | TCP_MAXSEG 1440
| 4088 | Got inappropriate HTTP CONNECT response: HTTP/1.1 401 Cookie is not acceptable
| 4088 | Error establishing the CSTP channel
| 2d98 | Disconnected
Татьяна Германова
Татьяна Германова
19.11.2024 23:16

Добрый день.
А если порты 80 и 443 уже заняты, то как быть?