Skip to content
UKENRU
Kyiv, Ukrainemail@dvornichenko.com
Михаил Дворниченко Архитектор решений · SK.AI
Home Blog Все заметки

Настройка VPN-сервера с обфускацией трафика

Краткое руководство по настройке VPN-сервера OpenConnect на AlmaLinux в связке с SNI-прокси на NGINX для обфускации трафика.

SNI-прокси (Server Name Indication) даёт дополнительный слой обфускации: трафик выглядит как обычный HTTPS. Это особенно выручает в странах и сетях, где VPN-трафик активно блокируют и анализируют.

Важно понимать, что это решение — не панацея. С большинством сервисов, которыми я пользуюсь сам, обфускация работает, но некоторые её всё же распознают. Например, OpenAI довольно неплохо вычисляет VPN-трафик, а вот банковские сервисы его не видят, так что с ними всё работает гладко.

Для VPN я взял самый дешёвый VPS: 1 ядро, 2 ГБ оперативной памяти, 10 ГБ диска и канал 500 Мбит/с.

Шаг 1: установка необходимых пакетов

Сначала поставим пакеты, на которых мы соберём VPN и SNI-прокси. Нам понадобятся OpenConnect Server (ocserv), NGINX и OpenSSL.

sudo dnf install epel-release -y
sudo dnf install ocserv nginx openssl -y

Команда поставит на AlmaLinux свежие версии OpenConnect, NGINX и OpenSSL.

Шаг 2: создание SSL-сертификатов

И OpenConnect, и NGINX шифруют соединение, поэтому им нужны SSL-сертификаты. Можно обойтись самоподписанным, а если у вас есть домен — взять настоящий TLS-сертификат у центра сертификации.

Выполните команду — она создаст сертификат и приватный ключ:

sudo mkdir -p /etc/ocserv/ssl
sudo openssl req -newkey rsa:4096 -nodes -keyout /etc/ocserv/ssl/server.key -x509 -days 365 -out /etc/ocserv/ssl/server.crt -subj "/C=UA/ST=Kyiv/L=Kyiv/O=MyVPN/OU=IT/CN=$(hostname -I)"
Code language: JavaScript (javascript)

Сертификат ляжет в /etc/ocserv/ssl/server.crt, приватный ключ — в /etc/ocserv/ssl/server.key. Срок действия — год.

Шаг 3: настройка сервера OpenConnect

Теперь настроим сам OpenConnect — для этого нужен конфигурационный файл. По умолчанию он лежит в /etc/ocserv/ocserv.conf.

sudo tee /etc/ocserv/ocserv.conf > /dev/null <<EOF
auth = "plain[passwd=/etc/ocserv/ocpasswd]"
tcp-port = 443
udp-port = 443
server-cert = /etc/ocserv/ssl/server.crt
server-key = /etc/ocserv/ssl/server.key
device = vpns
ipv4-network = 192.168.100.0
ipv4-netmask = 255.255.255.0
run-as-user = nobody
run-as-group = nobody
max-clients = 128
max-same-clients = 4
keepalive = 32400
dpd = 90
cisco-client-compat = true
tls-priorities = "normal"
socket-file = /var/run/ocserv-socket
dns = 8.8.8.8
dns = 8.8.4.4
EOF
Code language: JavaScript (javascript)

С такой конфигурацией OpenConnect принимает трафик на порту 443, держит до 128 клиентов и ходит за DNS к серверам Google.

Шаг 4: настройка NGINX в роли SNI-прокси

SNI-прокси на NGINX разводит трафик по имени сервера (SNI), которое клиент указал в запросе. VPN-запросы он отдаст OpenConnect, а обычный HTTP/HTTPS при необходимости уведёт на другой сервер.

sudo tee /etc/nginx/nginx.conf > /dev/null <<EOF
worker_processes  1;

events {
    worker_connections  1024;
}

stream {
    map $ssl_preread_server_name $backend {
        vpn.example.com ocserv_backend;
        default web_backend;
    }

    upstream ocserv_backend {
        server 127.0.0.1:443;
    }

    upstream web_backend {
        server 127.0.0.1:8443;
    }

    server {
        listen 443;
        proxy_pass $backend;
        ssl_preread on;
    }
}
EOF
Code language: PHP (php)

Вместо vpn.example.com подставьте свой домен или публичный IP сервера.

Шаг 5: запуск сервисов и добавление в автозагрузку

Осталось запустить оба сервиса и добавить их в автозагрузку.

sudo systemctl enable nginx
sudo systemctl enable ocserv
sudo systemctl restart nginx
sudo systemctl restart ocserv

Шаг 6: проверка состояния сервисов

Проверьте, что OpenConnect и NGINX запущены и работают:

systemctl status ocserv
systemctl status nginx

Шаг 7: добавление пользователей VPN

В OpenConnect заводятся свои пользователи VPN. Логины и пароли он держит в обычном текстовом файле (/etc/ocserv/ocpasswd). Кроме того, каждому пользователю нужен клиентский сертификат.

Добавляем пользователя и генерируем ему сертификат:

read -p "Enter VPN username: " vpn_user
sudo ocpasswd -c /etc/ocserv/ocpasswd $vpn_user
Code language: PHP (php)

Создание клиентских сертификатов и файлов PKCS#12

Каждому пользователю нужен клиентский сертификат и файл PKCS#12: в нём лежат и сертификат, и ключ, поэтому настроить клиента заметно проще:

sudo openssl req -newkey rsa:2048 -nodes -keyout /etc/ocserv/ssl/$vpn_user.key -x509 -days 365 -out /etc/ocserv/ssl/$vpn_user.crt -subj "/C=UA/ST=Kyiv/L=Kyiv/O=MyVPN/OU=IT/CN=$vpn_user"
sudo openssl pkcs12 -export -out /etc/ocserv/ssl/$vpn_user.p12 -inkey /etc/ocserv/ssl/$vpn_user.key -in /etc/ocserv/ssl/$vpn_user.crt -certfile /etc/ocserv/ssl/server.crt -password pass:$vpn_pass
Code language: PHP (php)

Файл .p12 передайте клиенту — он понадобится при настройке VPN.

Шаг 8: проверка VPN и SNI-прокси

Когда VPN-сервер настроен, проверьте, слушает ли он нужные порты:

sudo lsof -i :443 | grep LISTEN

Достучаться до VPN снаружи проще всего через curl:

curl -I https://$(hostname -I | awk '{print $1}') --insecure --max-time 10
Code language: JavaScript (javascript)

Если всё собрано верно, VPN ответит на HTTPS-запрос по порту 443.

Шаг 9: проверка шифрования VPN

Убедиться, что трафик действительно шифруется, можно перехватом через tcpdump:

sudo tcpdump -i any port 443 

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

Скрипт автоматической установки

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

Перед публикацией я прогнал этот скрипт на свежей виртуалке в Azure — вроде бы всё отработало нормально. Если наткнётесь на ошибки, напишите мне.

#!/bin/bash

# Automated OpenConnect Server and NGINX SNI Proxy Setup Script on AlmaLinux with Client Certificates (.crt, .p12)

echo "Starting the OpenConnect VPN Server and NGINX SNI Proxy installation on AlmaLinux..."

# Step 1: Install necessary packages
echo "Installing necessary packages: ocserv, nginx, and openssl..."
sudo dnf install epel-release -y
sudo dnf install ocserv nginx openssl -y

# Step 2: Check if required packages are installed
echo "Verifying package installation..."
if ! command -v ocserv &> /dev/null; then
    echo "Error: OpenConnect Server (ocserv) is not installed."
    exit 1
fi
if ! command -v nginx &> /dev/null; then
    echo "Error: NGINX is not installed."
    exit 1
fi
if ! command -v openssl &> /dev/null; then
    echo "Error: OpenSSL is not installed."
    exit 1
fi

echo "All required packages are installed."

# Step 3: Generate SSL certificate for OpenConnect Server and NGINX
echo "Generating SSL certificates..."
sudo mkdir -p /etc/ocserv/ssl
sudo openssl req -newkey rsa:4096 -nodes -keyout /etc/ocserv/ssl/server.key -x509 -days 365 -out /etc/ocserv/ssl/server.crt -subj "/C=US/ST=California/L=SanFrancisco/O=MyVPN/OU=IT/CN=$(hostname -I)"

# Step 4: Configure OpenConnect Server
echo "Configuring OpenConnect Server (ocserv)..."
sudo tee /etc/ocserv/ocserv.conf > /dev/null <<EOF
auth = "plain[passwd=/etc/ocserv/ocpasswd]"
tcp-port = 443
udp-port = 443
server-cert = /etc/ocserv/ssl/server.crt
server-key = /etc/ocserv/ssl/server.key
device = vpns
ipv4-network = 192.168.100.0
ipv4-netmask = 255.255.255.0
run-as-user = nobody
run-as-group = nobody
max-clients = 128
max-same-clients = 4
keepalive = 32400
dpd = 90
cisco-client-compat = true
tls-priorities = "normal"
socket-file = /var/run/ocserv-socket
dns = 8.8.8.8
dns = 8.8.4.4
EOF

# Step 5: Configure NGINX as SNI Proxy
echo "Configuring NGINX as SNI Proxy..."
sudo tee /etc/nginx/nginx.conf > /dev/null <<EOF
worker_processes  1;

events {
    worker_connections  1024;
}

stream {
    map $ssl_preread_server_name $backend {
        vpn.example.com ocserv_backend;
        default web_backend;
    }

    upstream ocserv_backend {
        server 127.0.0.1:443;
    }

    upstream web_backend {
        server 127.0.0.1:8443;
    }

    server {
        listen 443;
        proxy_pass $backend;
        ssl_preread on;
    }
}
EOF

# Step 6: Enable and restart services
echo "Enabling and restarting services (NGINX and OCserv)..."
sudo systemctl enable nginx
sudo systemctl enable ocserv
sudo systemctl restart nginx
sudo systemctl restart ocserv

# Step 7: Verify services
echo "Verifying the status of OCserv and NGINX..."
if systemctl is-active --quiet ocserv; then
    echo "OCserv is running."
else
    echo "Error: OCserv is not running."
    exit 1
fi

if systemctl is-active --quiet nginx; then
    echo "NGINX is running."
else
    echo "Error: NGINX is not running."
    exit 1
fi

# Step 8: Check if port 443 is being listened on
echo "Checking if port 443 is being listened on..."
if sudo lsof -i :443 | grep LISTEN; then
    echo "Port 443 is listening."
else
    echo "Error: Port 443 is not listening."
    exit 1
fi

# Step 9: Adding VPN users and creating certificates
echo "Adding VPN users..."
while true; do
    read -p "Enter a username for VPN (or type 'exit' to quit): " vpn_user
    if [[ "$vpn_user" == "exit" ]]; then
        echo "Exiting user creation."
        break
    fi
    read -sp "Enter password for $vpn_user: " vpn_pass
    echo
    sudo ocpasswd -c /etc/ocserv/ocpasswd $vpn_user <<EOF
$vpn_pass
$vpn_pass
EOF
    echo "User $vpn_user has been added."

    # Step 10: Create client certificate
    echo "Generating client certificate for $vpn_user..."
    sudo openssl req -newkey rsa:2048 -nodes -keyout /etc/ocserv/ssl/$vpn_user.key -x509 -days 365 -out /etc/ocserv/ssl/$vpn_user.crt -subj "/C=US/ST=California/L=SanFrancisco/O=MyVPN/OU=IT/CN=$vpn_user"

    # Step 11: Create PKCS#12 (.p12) file for client
    echo "Creating .p12 certificate bundle for $vpn_user..."
    sudo openssl pkcs12 -export -out /etc/ocserv/ssl/$vpn_user.p12 -inkey /etc/ocserv/ssl/$vpn_user.key -in /etc/ocserv/ssl/$vpn_user.crt -certfile /etc/ocserv/ssl/server.crt -password pass:$vpn_pass

    echo "Client certificate (.crt), private key (.key), and PKCS#12 (.p12) bundle have been created for user $vpn_user."
    echo "Files for $vpn_user are located at /etc/ocserv/ssl/"

done

# Step 12: Verify VPN connection availability via curl
echo "Verifying VPN connection availability via curl..."
curl -I https://$(hostname -I | awk '{print $1}') --insecure --max-time 10 >/dev/null 2>&1
if [ $? -eq 0 ]; then
    echo "VPN is accessible via HTTPS."
else
    echo "Error: Unable to access VPN via HTTPS."
    exit 1
fi

echo "Setup complete. Client certificates and .p12 bundles are located in /etc/ocserv/ssl/"
Code language: PHP (php)

Не забудьте выдать права на запуск:

chmod +x vpn-setup.sh Code language: CSS (css)

Теперь установите клиент Cisco AnyConnect

Для Android: https://play.google.com/store/apps/details?id=com.cisco.anyconnect.vpn.android.avf

Для iOS: https://apps.apple.com/us/app/cisco-secure-client/id1135064690

Для Windows: https://apps.microsoft.com/detail/9wzdncrdj8lh

Не забудьте поставить на клиент сертификат, который вы сгенерировали на шаге 7.

Надеюсь, пригодится.

Michael Dvornichenko Архитектор решений · SK.AI

Leave a comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *