Install Cisco anyconnect VPN on Debian 9 with SSL ready

Updated Jun 09, 2019

Installation step

// Step 1 - Install openconnect VPN package
apt update
apt install ocserv

// Step 2 - Install certbot to get free Let's encrypt SSL
apt install certbot

// Step 3 - generate SSL 
// recommend
certbot certonly --rsa-key-size 4096 --standalone --agree-tos --no-eff-email --email [email protected] -d vpn.xxx.io
// alternative
certbot certonly --standalone -d vpn.example.com

// Step 4 - Replace generated PEM and Privkey in ocserv.conf
nano /etc/ocserv.conf

** Comment out all route and no-route 
# 注释掉所有的 route 和 no-route,让服务器成为gateway
#route = 192.168.1.0/255.255.255.0
#no-route = 192.168.5.0/255.255.255.0

Recommended configuration

auth = "plain[/etc/ocserv/ocpasswd]"
# TCP and UDP port number
tcp-port = 443
udp-port = 443
run-as-user = nobody
run-as-group = daemon
socket-file = /var/run/ocserv-socket
server-cert = /etc/letsencrypt/live/xxx.com/fullchain.pem
server-key = /etc/letsencrypt/live/xxx.com/privkey.pem
ca-cert = /etc/ssl/certs/ssl-cert-snakeoil.pem
isolate-workers = true
max-clients = 16
max-same-clients = 2
keepalive = 32400
dpd = 90
mobile-dpd = 1800
try-mtu-discovery = true
cert-user-oid = 0.9.2342.19200300.100.1.1
compression = true
tls-priorities = "NORMAL:%SERVER_PRECEDENCE:%COMPAT:-VERS-SSL3.0:-VERS-TLS1.1:-VERS-TLS1.2"
#tls-priorities = "NORMAL:%SERVER_PRECEDENCE:%COMPAT:-VERS-SSL3.0"
auth-timeout = 240
min-reauth-time = 3
max-ban-score = 50
ban-reset-time = 300
cookie-timeout = 300
deny-roaming = false
rekey-time = 172800
rekey-method = ssl
use-utmp = true
use-occtl = true
pid-file = /var/run/ocserv.pid
device = vpns
predictable-ips = false
default-domain = vpn.xxx.com
ipv4-network = 192.168.3.0/24
# The IPv6 subnet that leases will be given from.
ipv6-network = fef4:db8:1000:1001::/64 
dns = 108.61.201.119
dns = 9.9.9.9
ping-leases = false
cisco-client-compat = true
dtls-legacy = true
// Step 5 - Modify ocserv.socket port number too
nano /lib/systemd/system/ocserv.socket

// Replace port number and save 
[Socket]
ListenStream=443 // change this to your desire port number
ListenDatagram=443 // change this to your desire port number

// Step 6 - Generate user password
ocpasswd -c /etc/ocserv/ocpasswd <username>

// Step 7 - Start your server
service ocserv restart

// Step 8 - Ensure ocserv was successful fireup
netstat -tulpn | grep 443 

Config your iptables

// ens3 is the outgoing port on Vultr, usually is eth0
// IPv4
*nat
-A POSTROUTING -s 192.168.0.0/24 -o ens3 -j MASQUERADE
-A POSTROUTING -o ens3 -j MASQUERADE
COMMIT

*filter
-A INPUT -p tcp -m tcp --dport 443 -j ACCEPT
-A INPUT -p udp -m udp --dport 443 -j ACCEPT
-A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -s 192.168.0.0/24 -m conntrack --ctstate NEW -j ACCEPT
COMMIT

// IPv6
*filter
-A INPUT -p tcp -m tcp --dport 443 -j ACCEPT
-A INPUT -p udp -m udp --dport 443 -j ACCEPT
-A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -s fef4:db8:1000:1001::/64 -m conntrack --ctstate NEW -j ACCEPT
COMMIT

*nat
-A POSTROUTING -s fef4:db8:1000:1001::/64 -o ens3 -j MASQUERADE
-A POSTROUTING -o ens3 -j MASQUERADE
COMMIT

Fixing DTLS Handshake Failure

On Debian9 or Ubuntu 18.04, ocserv daemon ocserv.socket does not respect “listen-host” value from configuration file, which will cause the following error when clients connect to VPN server.

DTLS handshake failed: Resource temporarily unavailable, try again.

To fix this error, we need to edit the ocserv.service file. We first copy the original file in /lib/systemd/system/ directory to /etc/systemd/system/ directory, then edit it, because we don’t want new version of ocserv package to override our modifications. (To learn more about systemd unit files, run man systemd.unit.)

sudo cp /lib/systemd/system/ocserv.service /etc/systemd/system/ocserv.service
sudo nano /etc/systemd/system/ocserv.service

Comment out the following two lines.

Requires=ocserv.socket

Also=ocserv.socket

Save and close the file. Then reload systemd

sudo systemctl daemon-reload

Stop ocserv.socket and disable it.

sudo systemctl stop ocserv.socket

sudo systemctl disable ocserv.socket

Restart ocserv service.

sudo systemctl restart ocserv.service

The ocserv systemd service won’t output any message if it fails to restart, so we need to check the status to make sure it’s actually running.

systemctl status ocserv

References:
1. https://lowendbox.com/blog/install-openconnect-server-on-ubuntu-16-04/
2. https://nova.moe/deploy-openconnect-ocserv-with-letsencrypt/
3. https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=837944