Setting up a VPN server with traffic obfuscation
A brief guide on how to set up an OpenConnect VPN server on AlmaLinux with SNI proxy integration on NGINX for traffic obfuscation.
An SNI proxy (Server Name Indication) provides an additional layer of obfuscation, making the traffic appear like regular HTTPS traffic. This is especially useful in countries or networks where VPN traffic is actively blocked or inspected.
However, it’s important to note that this solution is not a panacea. Obfuscation works for most services I personally use, but there are some that can detect it. For example, OpenAI is quite good at recognizing VPN traffic, but banking services do not see it, allowing for smooth operation.
For the VPN setup, I purchased the cheapest VPS: 1 core, 2GB of RAM, 10GB of disk space, and a 500Mbps internet connection.
Step 1: Installing the necessary packages
First, you need to install the packages that will allow us to configure the VPN and SNI proxy. We need to install OpenConnect Server (ocserv), NGINX, and OpenSSL.
sudo dnf install epel-release -y
sudo dnf install ocserv nginx openssl -y
This installs the latest available versions of OpenConnect, NGINX, and OpenSSL on AlmaLinux.
Step 2: Creating SSL Certificates
Both OpenConnect and NGINX require SSL certificates for secure communication. You can create a self-signed certificate or use a real TLS certificate from a certificate authority if you have a domain.
Run the following command to create a certificate and a private key:
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)
This creates an SSL certificate at /etc/ocserv/ssl/server.crt
and a private key at /etc/ocserv/ssl/server.key
, valid for one year.
Step 3: Configuring the OpenConnect server
The next step is to configure the OpenConnect server by creating a configuration file. By default, the file is located at /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)
This configuration sets up the OpenConnect server to handle traffic on port 443, supports up to 128 clients, and uses Google DNS servers.
Step 4: Configuring NGINX as an SNI Proxy
The NGINX SNI proxy will forward traffic depending on the server name (SNI) used in the request. It will forward VPN requests to OpenConnect and HTTP/HTTPS traffic to another server if necessary.
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)
Replace vpn.example.com
with your server’s domain or public IP address.
Step 5: Starting and Enabling the Services
After configuring OpenConnect and NGINX, you need to start and enable both services.
sudo systemctl enable nginx
sudo systemctl enable ocserv
sudo systemctl restart nginx
sudo systemctl restart ocserv
Step 6: Verifying Service Status
Ensure that both the OpenConnect and NGINX services are active and running:
systemctl status ocserv
systemctl status nginx
Step 7: Adding VPN Users
With OpenConnect, you can create VPN users. OpenConnect uses a simple password file (/etc/ocserv/ocpasswd
) to store usernames and passwords. You’ll also need to create user certificates.
Here’s how to add a user and generate a client certificate:
read -p "Enter VPN username: " vpn_user
sudo ocpasswd -c /etc/ocserv/ocpasswd $vpn_user
Code language: PHP (php)
Creating Client Certificates and PKCS#12 Files
For each user, you need to generate a client certificate and a PKCS#12 file, which includes the certificate and key for easy client setup:
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)
The .p12
file needs to be sent to the client for VPN setup.
Step 8: Verifying VPN and SNI Proxy
After setting up the VPN server, you can check if it’s listening on the correct ports:
sudo lsof -i :443 | grep LISTEN
To ensure the VPN is accessible, use curl
to check its availability:
curl -I https://$(hostname -I | awk '{print $1}') --insecure --max-time 10
Code language: JavaScript (javascript)
If everything is set up correctly, the VPN will respond to HTTPS requests on port 443.
Step 9: Verifying VPN Encryption
To confirm that the VPN encrypts traffic, you can use the tcpdump
tool to capture traffic:
sudo tcpdump -i any port 443
You should see encrypted traffic between the client and server. Any VPN client connecting via the .p12
certificate will have encrypted traffic. Instead of plain text, the packets will only contain headers and packet length, without revealing the content of the data.
Automatic Setup Script
If you don’t want to set it up manually, you can use the automatic script provided below.
I tested this script on a new Azure virtual machine before publishing this article, and everything seemed to work fine. If you find any errors, please let me know.
#!/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)
Don’t forget to change permissions:
chmod +x vpn-setup.sh
Code language: CSS (css)
Now, install the Cisco AnyConnect client
For Android: https://play.google.com/store/apps/details?id=com.cisco.anyconnect.vpn.android.avf
For iOS: https://apps.apple.com/us/app/cisco-secure-client/id1135064690
For Windows: https://apps.microsoft.com/detail/9wzdncrdj8lh
Don’t forget to install the generated client certificate on the client, obtained during Step 7.
I hope you found this information useful.