Set Up OpenConnect VPN Server (ocserv) on Ubuntu 20.04 with Let’s Encrypt

This tutorial is going to show you how to run your own VPN server by installing OpenConnect VPN server on Ubuntu 20.04. OpenConnect VPN server, aka ocserv, is an open-source implementation of Cisco AnyConnnect VPN protocol, which is widely used in businesses and universities. AnyConnect is an SSL-based VPN protocol that allows individual users to connect to a remote network.

Note: This tutorial also works on Ubuntu 20.10 and Ubuntu 21.04.

Why Set Up Your Own VPN Server?

  • Maybe you are a VPN service provider or a system administrator, which behooves you to set up our own VPN server.
  • You don’t trust the no-logging policy of VPN service providers, so you go the self-host route.
  • You can use VPN to implement network security policy. For example, if you run your own email server, you can require users to log in only from the IP address of the VPN server by creating an IP address whitelist in the firewall. Thus, your email server is hardened to prevent hacking activities.
  • Perhaps you are just curious to know how VPN server works.

Set Up OpenConnect VPN Server (ocserv) on Ubuntu 20.04

Features of OpenConnect VPN Server

  • Lightweight and fast. In my test, I can watch YouTube 4K videos with OpenConnect VPN. YouTube is blocked in my country (China).
  • Runs on Linux and most BSD servers.
  • Compatible with Cisco AnyConnect client
  • There are OpenConnect client software for Linux, MacOS, Windows and OpenWRT. For Android and iOS, you can use the Cisco AnyConnect Client.
  • Supports password authentication and certificate authentication
  • Supports RADIUS accounting.
  • Supports virtual hosting (multiple domains).
  • Easy to set up

I particularly like the fact that compared to other VPN technologies, it is very easy and convenient for the end-user to use OpenConnect VPN. Whenever I install a Linux distro on my computer and want to quickly unblock websites or hide my IP address, I install OpenConnect client and connect to the server with just two lines of commands:

sudo apt install openconnect

sudo openconnect -b vpn.mydomain.com

There is also OpenConnect VPN client for Fedora, RHEL, CentOS, Arch Linux and OpenSUSE. You can easily install it with your package manager.

sudo dnf install openconnect
sudo yum install openconnect
sudo pacman -S openconnect

Prerequisites

To follow this tutorial, you will need a VPS (Virtual Private Server) that can access blocked websites freely (Outside of your country or Internet filtering system). I recommend Vultr VPS (This is my referral link. You can get $50 free credit by creating an account at Vultr via my referral link). They offer 512M memory high-performance KVM VPS for just $2.5 per month, which is perfect for your private VPN server. Once you have a VPS, install Ubuntu 20.04 on it and follow the instructions below.

You also need a domain name. I registered my domain name from NameCheap because the price is low and they give whois privacy protection free for life.

Update: The new Vultr $2.5/month plan includes IPv6 address only. You can select the $3.5/month plan at the New York (NJ) data center to have both IPv4 and IPv6 addresses.

Step 1: Install OpenConnect VPN Server on Ubuntu 20.04

Log into your Ubuntu 20.04 server. Then use apt to install the ocserv package from the default Ubuntu repository.

sudo apt update
sudo apt install ocserv

Once installed, the OpenConnect VPN server is automatically started. You can check its status with:

systemctl status ocserv

Sample output:

 ocserv.service - OpenConnect SSL VPN server
     Loaded: loaded (/lib/systemd/system/ocserv.service; enabled; vendor preset: enabled)
     Active: active (running) since Sun 2020-04-12 19:57:08 HKT; 12s ago
       Docs: man:ocserv(8)
   Main PID: 216409 (ocserv-main)
      Tasks: 2 (limit: 9451)
     Memory: 1.6M
     CGroup: /system.slice/ocserv.service
             ├─216409 ocserv-main
             └─216429 ocserv-sm

Hint: If the above command doesn’t quit immediately, you can press the Q key to gain back control of the terminal.

If it’s not running, then you can start it with:

sudo systemctl start ocserv

By default OpenConnect VPN server listens on TCP and UDP port 443. If it’s being used by web server, then the VPN server would probably fail to start. We will see how to change the port in OpenConnect VPN configuration file later.

If there’s a firewall running on your server, then you will need to open port 80 and 443. For example, if you use UFW, then run the following command.

sudo ufw allow 80,443/tcp

Step 2: Install Let’s Encrypt Client (Certbot) on Ubuntu 20.04 Server

The gnutls-bin package installed along with ocserv provides tools to create your own CA and server certificate, but we will obtain and install Let’s Encrypt certificate. The advantage of using Let’s Encrypt certificate is that it’s free, easier to set up and trusted by VPN client software.

Run the following commands to install Let’s Encrypt client (certbot) from the default Ubuntu repository.

sudo apt install certbot

To check the version number, run

certbot --version

Sample output:

certbot 0.40.0

Step 3: Obtain a Trusted TLS Certificate from Let’s Encrypt

I recommend using the standalone or webroot plugin to obtain TLS certificate for ocserv.

Standalone Plugin

If there’s no web server running on your Ubuntu 20.04 server and you want OpenConnect VPN server to use port 443, then you can use the standalone plugin to obtain TLS certificate from Let’s Encrypt. Run the following command. Don’t forget to set A record for your domain name.

sudo certbot certonly --standalone --preferred-challenges http --agree-tos --email [email protected] -d vpn.example.com

Where:

  • certonly: Obtain a certificate but don’t install it.
  • --standalone: Use the standalone plugin to obtain a certificate
  • --preferred-challenges http: Perform http-01 challenge to validate our domain, which will use port 80.
  • --agree-tos: Agree to Let’s Encrypt terms of service.
  • --email: Email address is used for account registration and recovery.
  • -d: Specify your domain name.

As you can see the from the following screenshot, I successfully obtained the certificate.

ocserv ubuntu 20.04 letsencrypt certbot

Using webroot Plugin

If your Ubuntu 20.04 server has a web server listening on port 80 and 443, then it’s a good idea to use the webroot plugin to obtain a certificate because the webroot plugin works with pretty much every web server and we don’t need to install the certificate in the web server.

First, you need to create a virtual host for vpn.example.com.

Apache

If you are using Apache, then

sudo nano /etc/apache2/sites-available/vpn.example.com.conf

And paste the following lines into the file.

<VirtualHost *:80>        
        ServerName vpn.example.com

        DocumentRoot /var/www/ocserv
</VirtualHost>

Save and close the file. Then create the web root directory.

sudo mkdir /var/www/ocserv

Set www-data (Apache user) as the owner of the web root.

sudo chown www-data:www-data /var/www/ocserv -R

Enable this virtual host.

sudo a2ensite vpn.example.com

Reload Apache for the changes to take effect.

sudo systemctl reload apache2

Once virtual host is created and enabled, run the following command to obtain Let’s Encrypt certificate using webroot plugin.

sudo certbot certonly --webroot --agree-tos --email [email protected] -d vpn.example.com -w /var/www/ocserv

Nginx

If you are using Nginx, then

sudo nano /etc/nginx/conf.d/vpn.example.com.conf

Paste the following lines into the file.

server {
      listen 80;
      server_name vpn.example.com;

      root /var/www/ocserv/;

      location ~ /.well-known/acme-challenge {
         allow all;
      }
}

Save and close the file. Then create the web root directory.

sudo mkdir -p /var/www/ocserv

Set www-data (Nginx user) as the owner of the web root.

sudo chown www-data:www-data /var/www/ocserv -R

Reload Nginx for the changes to take effect.

sudo systemctl reload nginx

Once virtual host is created and enabled, run the following command to obtain Let’s Encrypt certificate using webroot plugin.

sudo certbot certonly --webroot --agree-tos --email [email protected] -d vpn.example.com -w /var/www/ocserv

Step 4: Edit OpenConnect VPN Server Configuration File

Edit ocserv main configuration file.

sudo nano /etc/ocserv/ocserv.conf

First, we need to configure password authentication. By default, password authentication through PAM (Pluggable Authentication Modules) is enabled, which allows you to use Ubuntu system accounts to login from VPN clients. This behavior can be disabled by commenting out the following line.

auth = "pam[gid-min=1000]"

If we want users to use separate VPN accounts instead of system accounts to login, we need to add the following line to enable password authentication with a password file.

auth = "plain[passwd=/etc/ocserv/ocpasswd]"

After finishing editing this config file, we will see how to use ocpasswd tool to generate the /etc/ocserv/ocpasswd file, which contains a list of usernames and encoded passwords.

Note: Ocserv supports client certificate authentication, but Let’s Encrypt does not issue client certificate. You need to set up your own CA to issue client certificate.

Next, if you don’t want ocserv to use TCP and UDP port 443, then find the following two lines and change the port number. Otherwise leave them alone.

tcp-port = 443
udp-port = 443

Then find the following two lines. We need to change them.

server-cert = /etc/ssl/certs/ssl-cert-snakeoil.pem
server-key = /etc/ssl/private/ssl-cert-snakeoil.key

Replace the default setting with the path of Let’s Encrypt server certificate and server key file.

server-cert = /etc/letsencrypt/live/vpn.example.com/fullchain.pem
server-key = /etc/letsencrypt/live/vpn.example.com/privkey.pem

Then, set the maximal number of clients. Default is 128. Set to zero for unlimited.

max-clients = 128

Set the number of devices a user is able to login from at the same time. Default is 2. Set to zero for unlimited.

max-same-clients = 2

Next, find the following line. Change false to true to enable MTU discovery, which can optimize VPN performance.

try-mtu-discovery = false

You can set the time that a client is allowed to stay idle before being disconnected via the following two parameters. If you prefer the client to stay connected indefinitely, then comment out these two parameters.

idle-timeout=1200
mobile-idle-timeout=1800

After that, set the default domain to vpn.example.com.

default-domain = vpn.example.com

The IPv4 network configuration is as follows by default. This will cause problems because many home routers also set the IPv4 network range to 192.168.1.0/24.

ipv4-network = 192.168.1.0
ipv4-netmask = 255.255.255.0

We can use another private IP address range (10.10.10.0/24) to avoid IP address collision, so change the value of ipv4-network to

ipv4-network = 10.10.10.0

Now uncomment the following line to tunnel all DNS queries via the VPN.

tunnel-all-dns = true

The default DNS resolver addresses are as follows, which is fine.

dns = 8.8.8.8
dns = 1.1.1.1

Note: If you are a VPN service provider, then it’s a good practice to run your own DNS resolver on the same server. If there’s a DNS resolver running on the same server, then specify the DNS as

dns = 10.10.10.1

10.10.10.1 is the IP address of OpenConnect VPN server in the VPN LAN. This will speed up DNS lookups a little bit for clients because the network latency between the VPN server and the DNS resolver is eliminated.

Then comment out all the route parameters (add # symbol at the beginning of the following four lines), which will set the server as the default gateway for the clients.

route = 10.0.0.0/8
route = 172.16.0.0/12
route = 192.168.0.0/16

no-route = 192.168.5.0/255.255.255.0

Save and close the file  Then restart the VPN server for the changes to take effect.

sudo systemctl restart ocserv

Step 5: Create VPN Accounts

Now use the ocpasswd tool to generate VPN accounts.

sudo ocpasswd -c /etc/ocserv/ocpasswd username

You will be asked to set a password for the user and the information will be saved to /etc/ocserv/ocpasswd file. To reset password, simply run the above command again.

Step 6: Enable IP Forwarding

In order for the VPN server to route packets between VPN clients and the Internet, we need to enable IP forwarding. Edit sysctl.conf file.

sudo nano /etc/sysctl.conf

Add the following line at the end of this file.

net.ipv4.ip_forward = 1

Save and close the file. Then apply the changes with the below command. The -p option will load sysctl settings from /etc/sysctl.conf file. This command will preserve our changes across system reboots.

sudo sysctl -p

Step 7: Configure IP Masquerading in Firewall

We need to set up IP masquerading in the server firewall, so that the server becomes a virtual router for VPN clients. I will use UFW, which is a front end to the iptables firewall. Install UFW on Ubuntu with:

sudo apt install ufw

First, you need to allow SSH traffic.

sudo ufw allow 22/tcp

Then find the name of your server’s main network interface.

ip addr

As you can see, it’s named ens3 on my Ubuntu server.

openconnect-ubuntu 20.04-command-line

To configure IP masquerading, we have to add iptables command in a UFW configuration file.

sudo nano /etc/ufw/before.rules

By default, there are some rules for the filter table. Add the following lines at the end of this file. Replace ens3 with your own network interface name.

# NAT table rules
*nat
:POSTROUTING ACCEPT [0:0]
-A POSTROUTING -s 10.10.10.0/24 -o ens3 -j MASQUERADE

# End each table with the 'COMMIT' line or these rules won't be processed
COMMIT

In Nano text editor, you can go to the end of the file by pressing Ctrl+W, then pressing Ctrl+V.

ufw masquerade rule ocserv ubuntu

The above lines will append (-A) a rule to the end of of POSTROUTING chain of nat table. It will link your virtual private network with the Internet. And also hide your network from the outside world. So the Internet can only see your VPN server’s IP, but can’t see your VPN client’s IP, just like your home router hides your private home network.

By default, UFW forbids packet forwarding. We can allow forwarding for our private network. Find the ufw-before-forward chain in this file and add the following 3 lines, which will accept packet forwarding if the source IP or destination IP is in the 10.10.10.0/24 range.

# allow forwarding for trusted network
-A ufw-before-forward -s 10.10.10.0/24 -j ACCEPT
-A ufw-before-forward -d 10.10.10.0/24 -j ACCEPT

ufw allow packet fowarding

Save and close the file. Then enable UFW.

sudo ufw enable

If you have enabled UFW before, then you can use systemctl to restart UFW.

sudo systemctl restart ufw

Now if you list the rules in the POSTROUTING chain of the NAT table by using the following command:

sudo iptables -t nat -L POSTROUTING

You can see the Masquerade rule.

ocserv-IP-Masquerading-ufw-ubuntu-20.04

It can take some time for UFW to process the firewall rules. If the masquerade rule doesn’t show up, then restart UFW again.

Step 8: Open Port 443 in Firewall

Run the following command to open TCP and UDP port 443. If you configured a different port for ocserv, then change 443 to your configured port.

sudo ufw allow 443/tcp
sudo ufw allow 443/udp

Now OpenConnect VPN server is ready to accept client connections.

If you run a local DNS Resolver

For those of you who run a local DNS resolver, if you specified 10.10.10.1 as the DNS server for VPN clients, then you must allow VPN clients to connect to port 53 with the following UFW rule.

sudo ufw insert 1 allow in from 10.10.10.0/24

You also need to edit the BIND DNS server’s configuration file (/etc/bind/named.conf.options) to allow VPN clients to send recursive DNS queries like below.

allow-recursion { 127.0.0.1; 10.10.10.0/24; };

Then restart BIND.

sudo systemctl restart named

How to Install and Use OpenConnect VPN client on Ubuntu 20.04 Desktop

Run the following command to install OpenConnect VPN command line client on Ubuntu desktop.

sudo apt install openconnect

You can Connect to VPN from the command line like below. -b flag will make it run in the background after connection is established.

sudo openconnect -b vpn.example.com:port-number

You will be asked to enter VPN username and password. If connection is successfully established, you will see the following message.

Got CONNECT response: HTTP/1.1 200 CONNECTED
CSTP connected. DPD 90, Keepalive 32400
Connected tun0 as 192.168.1.139, using SSL
Established DTLS connection (using GnuTLS). Ciphersuite (DTLS1.2)-(RSA)-(AES-256-GCM).

To stop the connection, run:

sudo pkill openconnect

To run the client non-interactively, use the following syntax.

echo -n password | sudo openconnect -b vpn.example.com -u username --passwd-on-stdin

If you want to use Network Manager to manage VPN connection, then you also need to install these packages.

sudo apt install network-manager-openconnect network-manager-openconnect-gnome

If you are successfully connected to the VPN server, but your public IP address doesn’t change, that’s because IP forwarding or IP masquerading is not working. I once had a typo in my iptables command (using a wrong IP address range), which caused my computer not being able to browse the Internet.

Auto-Connect on System Startup

To let OpenConnect VPN client automatically connect to the server at boot time, we can create a systemd service unit.

sudo nano /etc/systemd/system/openconnect.service

Put the following lines to the file. Replace the red text.

[Unit]
  Description=OpenConnect VPN Client
  After=network-online.target systemd-resolved.service
  Wants=network-online.target

[Service]
  Type=simple
  ExecStart=/bin/bash -c '/bin/echo -n password | /usr/sbin/openconnect vpn.example.com -u username --passwd-on-stdin'
  KillSignal=SIGINT
  Restart=always
  RestartSec=2

[Install]
  WantedBy=multi-user.target

Save and close the file. Then enable this service so that it will start at boot time.

sudo systemctl enable openconnect.service

Explanation of the file content:

  • After=network-online.target systemd-resolved.service and Wants=network-online.target make this service run after network is up. We want the openconnect.service start after the systemd-resolved.service because that will ensure the DNS server address set by OpenConnect won’t be overridden by systemd-resolved.service.
  • In reality, this service can still run before network is up. We add Restart=always and RestartSec=2 to restart this service after 2 seconds if this service fails.
  • Systemd doesn’t recognise pipe redirection, so in the ExecStart directive, we wrap the comand in single quotes and run it with the Bash shell.
  • Since OpenConnect VPN client will run as a systemd service, which runs in the background, there’s no need to add -b flag to the openconnect command.
  • The KillSignal directive tells Systemd to send the SIGINT signal when the systemctl stop openconnect command is issued. This will performs a clean shutdown by logging the session off, and restoring DNS server settings and the Linux kernel routing table.

To start this Systemd service immediately, run

sudo systemctl start openconnect

To stop this Systemd service, run

sudo systemctl stop openconnect

How to Automatically Restart OpenConnect Client When Resuming from Suspend

If your Ubuntu desktop goes into suspend state, the OpenConnect client would lose connection to the VPN server. To make it automatically restart when resuming from suspend, we need to create another systemd service unit.

sudo nano /etc/systemd/system/openconnect-restart.service

Add the following lines in the file.

[Unit]
Description=Restart OpenConnect client when resuming from suspend
After=suspend.target

[Service]
Type=simple
ExecStart=/bin/systemctl --no-block restart ocnyc.service

[Install]
WantedBy=suspend.target

Save and close the file. Then enable this service.

sudo systemctl enable openconnect-restart.service

Automatic-Restart When VPN Connection Drops

Sometimes the VPN connection would drop due to other reasons. You can run the following command to check if the VPN client can ping the VPN server’s private IP address (10.10.10.1). If the ping is unsuccessful, then the command on the right will be executed to restart the VPN client. || is the OR operator in Bash. It executes the command on the right only if the command on the left returned an error.

ping -c9 10.10.10.1 || systemctl restart openconnect

The ping will be done 9 times, i.e 9 seconds. You can use an infinite loop in the Bash shell to make the whole command run forever. Press Ctrl+C to stop it.

for ((; ; )) do (ping -c9 10.10.10.1 || systemctl restart openconnect) done

Now we can create a systemd service for this task.

sudo nano /etc/systemd/system/openconnect-check.service

Add the following lines to this file. We specify that this service should run after the openconnect.service.

[Unit]
Description=OpenConnect VPN Connectivity Checker
After=openconnect.service

[Service]
Type=simple
ExecStart=/bin/bash -c 'for ((; ; )) do (ping -c9 10.10.10.1 || systemctl restart openconnect) done'

[Install]
WantedBy=multi-user.target

Save and close the file. Then start this service.

sudo systemctl start openconnect-check

Enable auto-start at boot time.

sudo systemctl enable openconnect-check

Once this service is started, the ping command will run forever. If the VPN connection drops, it will automatically restart openconnect.service.

OpenConnect GUI Client for Windows and macOS

They can be downloaded from OpenConnect GUI Github Page.

Speed

OpenConnect VPN is pretty fast. I can use it to watch 4k videos on YouTube.

ocserv letsencrypt

Auto-Renew Let’s Encrypt Certificate

Edit root user’s crontab file.

sudo crontab -e

Add the following line at the end of the file. It’s necessary to reload ocserv service for the VPN server to pick up new certificate and key file.

@daily certbot renew --quiet && systemctl reload ocserv

Optimization

OpenConnect by default uses TLS over UDP protocol (DTLS) to achieve faster speed, but UDP can’t provide reliable transmission. TCP is slower than UDP but can provide reliable transmission. One optimization tip I can give you is to disable DTLS, use standard TLS (over TCP), then enable TCP BBR to boost TCP speed.

To disable DTLS, comment out (add # symbol at the beginning) the following line in ocserv configuration file.

udp-port = 443

Save and close the file. Then restart ocserv service.

sudo systemctl restart ocserv.service

To enable TCP BBR, please check out the following tutorial.

In my test, standard TLS with TCP BBR enabled is two times faster than DTLS.

Troubleshooting Tips

Note that if you are using OpenVZ VPS, make sure you enable the TUN virtual networking device in VPS control panel. (If you use Vultr VPS, then you have KVM-based VPS, so you don’t have to worry about this.)

If you encounter any problem, then check OpenConnect VPN server log.

sudo journalctl -eu ocserv.service

I found that if I change port 443 to a different port, the great firewall of China will block this VPN connection.

If ocserv tells you that it can’t load the /etc/ocserv/ocserv.conf file, you can stop ocserv.

sudo systemctl stop ocserv

Then run it in the foreground with debugging enabled.

sudo /usr/sbin/ocserv --foreground --pid-file /run/ocserv.pid --config /etc/ocserv/ocserv.conf --debug=10

Then output might give you some clues why ocserv isn’t working.

If you are successfully connected to the VPN server, but you can’t browse the Internet, that’s because IP forwarding or IP masquerading is not working. I remember my VPS provider once did a platform upgrade, which changed the name of the main network interface from ens3 to enp3s0, so I had to update the name in the UFW file (/etc/ufw/before.rules).

Make OpenConnect VPN server and web server use port 443 at the same time

Please read the following article:

How to Disable TLS 1.0 and TLS 1.1 in ocserv

The PCI council deprecated TLS 1.0 in June 30, 2018 and main stream web browsers are going to disable TLS 1.0 and TLS 1.1 in 2020. We should do the same with VPN server. Edit the main configuration file.

sudo nano /etc/ocserv/ocserv.conf

Find the following line:

tls-priorities = "NORMAL:%SERVER_PRECEDENCE:%COMPAT:-RSA:-VERS-SSL3.0:-ARCFOUR-128"

To disable TLS 1.0 and TLS 1.1 in OpenConnect VPN server, just add -VERS-TLS1.0 and -VERS-TLS1.1 in the line.

tls-priorities = "NORMAL:%SERVER_PRECEDENCE:%COMPAT:-RSA:-VERS-SSL3.0:-ARCFOUR-128:-VERS-TLS1.0:-VERS-TLS1.1"

Save and close the file. Then restart ocserv.

sudo systemctl restart ocserv

Now ocserv will only accept TLS 1.3 and TLS 1.2. For further information on configuring the TLS parameter in ocserv, please see GnuTLS priority strings.

To check if TLS 1.0 is supported in your OpenConnect VPN server, run the following command.

openssl s_client -connect vpn.your-domain.com:443 -tls1

And check TLS 1.1

openssl s_client -connect vpn.your-domain.com:443 -tls1_1

If you see the following message in the output, that means the TLS version is not supported.

New, (NONE), Cipher is (NONE)
Secure Renegotiation IS NOT supported

Per-User or Per Group Configuration

Ocserv allows per user and per group configurations. To enable this feature, uncomment the following two lines in /etc/ocserv/ocserv.conf file.

config-per-user = /etc/ocserv/config-per-user/
config-per-group = /etc/ocserv/config-per-group/

Save and close the file. Then create the per user and per-group config directory.

sudo mkdir /etc/ocserv/config-per-user/
sudo mkdir /etc/ocserv/config-per-group/

Next, you can create a file under these two directories. For example, create the user1 file to allow custom configuration for user1.

sudo nano /etc/ocserv/config-per-user/user1

You can also create the group1 file to allow custom configuration for the group named group1.

sudo nano /etc/ocserv/config-per-group/group1

You can add something like below in the file to enable split tunneling.

route = 10.10.10.0/255.255.255.0
tunnel-all-dns = false
dns = 8.8.8.8
dns = 1.1.1.1

Where:

  • The first line means that after user1 or users in group1 connect to this VPN server, only traffic to the 10.10.10.0/24 network will be routed via VPN server. Traffic to other IP addresses are routed via the original gateway.
  • The second line disables tunneling DNS queries.
  • The third and fourth line set DNS servers for VPN clients.

I use this trick to allow my another VPS (virtual private server) to connect to this VPN server without disrupting normal traffic, so the TUN device (vpns0) of my VPN server is always turned on, which means my VPN server will always have the private IP address 10.10.10.1.

Save and close the file. Restart ocserv for the changes to take effect.

sudo systemctl restart ocserv

Note that if you enable IPv6 in ocserv, then you also need to add the IPv6 network range in order to use split tunneling.

route = 10.10.10.0/255.255.255.0
route = fda9:4efe:7e3b:03ea::/48
tunnel-all-dns = false
dns = 8.8.8.8
dns = 1.1.1.1

If you want to exclude an IP address from the default route, then use no-route.

no-route = 12.34.56.78/32

This means all other traffic will go through the VPN tunnel except traffic to IP 12.34.56.78.  route and no-route paramater accept both netmask notation (10.10.10.0/255.255.255.0) and CIDR notation (12.34.56.78/32)

If you see the following error after enabling split tunneling, it’s probably because you didn’t use the netmask or CIDR notation.

could not parse proxy protocol header; discarding connection

Split Tunneling by Country

Let’s say you only want traffic to foreign counties to be tunneled by the VPN. Traffic to your own country should use the normal route. You can use the no-route directive in ocserv configuration file to achieve this.

First, you need to download the IP range for your country by going to this web page: https://www.ip2location.com/free/visitor-blocker, which allows you to download the IP address range in CIDR format.

Save the IP range in a text file ip2location.txt, and run the following command in your Linux terminal to add the no-route = directive to the beginning of each line.

sed 's/^/no-route = /' -i ip2localtion.txt

Now open the file in a text editor and copy all the lines in it. We need to add these lines in the ocserv configuration file. If there are too many lines, you can and add these lines to the the per-user configuration file.

sudo nano /etc/ocserv/config-per-user/user1

You can also add them to the per-group configuration file, then add users to the group.

Restart ocserv for the changes to take effect.

sudo systemctl restart ocserv

How to Enable IPv6 in OpenConnect VPN

If your VPN server has a public IPv6 address, you can enable IPv6 in OpenConnect VPN. Edit ocserv configuariton file.

sudo nano /etc/ocserv/ocserv.conf

Find the following two lines and uncomment them.

ipv6-network = fda9:4efe:7e3b:03ea::/48
ipv6-subnet-prefix = 64

Save and close the file. Restart ocserv for the change to take effect.

sudo systemctl restart ocserv

Then we need to enable IP forwarding for IPv6. Edit sysctl.conf file.

sudo nano /etc/sysctl.conf

Add the following line at the end of this file.

net.ipv6.conf.all.forwarding=1

Save and close the file. Then apply the changes with the below command.

sudo sysctl -p

Next, we need to set up IPv6 masquerading in the server firewall, so that the server becomes a virtual router for VPN clients.

sudo nano /etc/ufw/before6.rules

By default, there are some rules for the filter table. Add the following lines at the end of this file. Replace ens3 with your own network interface name.

# NAT table rules
*nat
:POSTROUTING ACCEPT [0:0]
-A POSTROUTING -o ens3 -j MASQUERADE

# End each table with the 'COMMIT' line or these rules won't be processed
COMMIT

In Nano text editor, you can go to the end of the file by pressing Ctrl+W, then pressing Ctrl+V.

IPv6 masquerading in the UFW firewall

By default, UFW forbids packet forwarding. We can allow forwarding for our private IPv6 network. Find the ufw6-before-forward chain in this file and add the following 3 lines, which will accept packet forwarding if the source IP or destination IP is in the fda9:4efe:7e3b:03ea::/48 range.

ufw allow packet forwarding for ipv6 network

Save and close the file. Restart UFW for the change to take effect.

sudo systemctl restart ufw

Now if you list the rules in the POSTROUTING chain of the NAT table by using the following command:

sudo ip6tables -t nat -L POSTROUTING

You can see the Masquerade rule.

enable ipv6 in ocserv openconnect vpn

Disconnect the current VPN connection, add an AAAA record for vpn.example.com and re-establish VPN connection. Then go to https://test-ipv6.com/ to check your IPv6 connectivity.

If you run your own BIND DNS resolver on the VPN server, you can add the following line in /etc/ocserv/ocserv.conf file to set the VPN server as the DNS resolver for VPN clients.

dns = fda9:4efe:7e3b::1

Save and close the file. To query DNS names in IPv6, we need to configure BIND to allow IPv6 VPN clients.

sudo nano /etc/bind/named.conf.options

Find the allow-recursion parameter and change it to:

allow-recursion { 127.0.0.1; 10.10.10.0/24; fda9:4efe:7e3b:03ea::/48; };

Save and close the file. Restart BIND9.

sudo systemctl restart named

We also need to allow IPv6 VPN client in the firewall.

sudo ufw allow in from fda9:4efe:7e3b:03ea::/48

Virtual Hosting

To add a new virtual host in ocserv, first you need to use the method in step 3 to obtain a TLS certificate for the new virtual host. Then edit ocserv configuration file.

sudo nano /etc/ocserv/ocserv.conf

Go to the bottom of this file. In Nano text editor, you can press Ctrl+W, then Ctrl+V to jump to the bottom of a file. Add the following lines. Replace vpn2.example.com with the hostname of the second virtual host.

[vhost:vpn2.example.com]
#Allow password authentication and certificate authentication
enable-auth = "plain[passwd=/etc/ocserv/ocpasswd]"
auth = "certificate"

tcp-port = 443

#uncomment the two lines if ocserv runs behind HAProxy.
#listen-host = 127.0.0.1
#listen-proxy-proto = true

# SSL/TLS configuration
ca-cert = /etc/ocserv/ssl/ca-cert.pem
server-cert = /etc/letsencrypt/live/vpn2.example.com/fullchain.pem
server-key = /etc/letsencrypt/live/vpn2.example.com/privkey.pem
cert-user-oid = 0.9.2342.19200300.100.1.1

#Networking configuration. Use a different network range for this virtual host. 
device = vpns
ipv4-network = 10.10.20.0
ipv4-netmask = 255.255.255.0
route = default
dns = 8.8.8.8
tunnel-all-dns = true

compression = true
max-clients = 0
max-same-clients = 0
try-mtu-discovery = true
idle-timeout=1200
mobile-idle-timeout=2400

config-per-user = /etc/ocserv/config-per-user/
config-per-group = /etc/ocserv/config-per-group/

Save and close the file. Then restart ocserv.

sudo systemctl restart ocserv

Edit the UFW configuration file.

sudo nano /etc/ufw/before.rules

Find the ufw-before-forward chain in this file and add the following 2 lines, which will accept packet forwarding if the source IP or destination IP is in the 10.10.20.0/24 range.

-A ufw-before-forward -s 10.10.20.0/24 -j ACCEPT
-A ufw-before-forward -d 10.10.20.0/24 -j ACCEPT

Save and close the file. Then restart UFW.

sudo systemctl restart ufw

Note that the ocserv daemon might tell you some parameters will be ignored for virtual host. However, I found that some of the ignored parameters are actually needed. For example, if you delete the device = vpns line from the virtual host, you might encounter the following error when establishing VPN connection to the virtual host.

VPN service unavailable; reason: Server configuration error

And the VPN server would produce the following error message in the log.

no networks are configured; rejecting client

Also Note that the AnyConnect VPN client on iOS doesn’t support TLS SNI, so iOS users will connect to the default virtual host.

How to Run Multiple Instances of ocserv

One ocserv process can bind to only one TCP or UDP port on your server. If you want to allow ocserv to bind to multiple TCP or UDP ports, then you need to run multiple ocserv processes. It’s very simple. Copy the /lib/systemd/system/ocserv.service to a new file.

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

Then edit the new file.

sudo nano /etc/systemd/system/ocserv2.service

Change

/etc/ocserv/ocserv.conf

to

/etc/ocserv/ocserv2.conf

Save and close the file. Next, you can edit the /etc/ocserv/ocserv2.conf file and add your custom configurations. Once you are done, start the second ocserv service.

sudo systemctl start ocserv2

Wrapping Up

That’s it! I hope this tutorial helped you install and configure OpenConnect VPN on Ubuntu 20.04. As always, if you found this post useful, then subscribe to our free newsletter to get more tips and tricks 🙂

Rate this tutorial
[Total: 6 Average: 5]

33 Responses to “Set Up OpenConnect VPN Server (ocserv) on Ubuntu 20.04 with Let’s Encrypt

  • papino
    1 year ago

    Hi, I have successfully connected but got the error message as below:

    Established DTLS connection (using GnuTLS). Ciphersuite (DTLS1.2)-(PSK)-(AES-256-GCM).
    Failed to read from SSL socket: The transmitted packet is too large (EMSGSIZE).
    Failed to recv DPD request (1434)
    Command line is not complete. Try option “help”

    How to fix this problem? please help!!

  • Xiao Guoan
    Hello
    My problem is that after connecting to OpenConnect the speed is lost or does not exceed 0.78mb. How to fix the problem

    • OpenConnect VPN protocol itself is very fast. However, there are other factors that can impact speed, such as the network condition between the VPN client and the VPN server. Always choose a server close to the client as the VPN server.

      You can enable the TCP BBR algorithm to speed up TCP connection speed.

  • The user is constantly banned, for example, between the transition from 3g to a wi fi network.
    Can you help me find a solution?

  • hello,
    did you faced an error like:
    main.c:868: bind UDP to [::]:443: Invalid argument
    … Network is unreachable

    client works fine and can access internet but still see this errors in my logs 🙁

    • it seems issue was with udp-port, tried to comment it in config file.

      • Mostafa Ghadimi
        3 months ago

        Hi Marat,

        Would you please give more information of how your issue was resolved?
        I have the same issue but I don’t know how to fix it?
        https://askubuntu.com/questions/1325690/ufw-block-error-seen-in-journalctl-xe-command

        Thanks

        • Marat
          3 months ago

          Hi Mostafa,
          To be honest, I don’t remember, try to comment related line in config file 🙂

  • Hello Xiao!

    Great tutorial!
    I have a question. If I have the nginx running in parallel with the vpn and use the haproxy as you explained in the linked tutorial is there a way to make a site from nginx available only to the vpn? What would be needed in the haproxy, nginx and vpn configuration?
    From a newbie perspective I expect something like an client IP forward through haproxy to nginx where nginx decides to deny/accept the connection. And the vpn needs to do something like a split tunnel. But I need to search some more documentation for all of these.
    Any terms that would be helpful in this when I look into the manuals?

    Many thanks!

    • Ok, after working some days on this issue this is what I did. Let me know if there are other things that need to be taken care of besides what I did here.

      I first did the in the /etc/sysctl.conf file

       net.ipv4.ip_nonlocal_bind=1

      In /etc/haproxy/haproxy.cfg
      I added the

      bind 10.10.10.1:443

      to bind the vpn internal ip to the frontend
      In the nginx backaned I added server

      send-proxy-v2

      I created the rpz dns resolver and added in it all the dns entries from the sites that I wanted to be in the “intranet” as stated in this tutorial (/etc/bind/db.rpz.local):
      https://www.linuxbabe.com/ubuntu/set-up-response-policy-zone-rpz-in-bind-resolver-on-debian-ubuntu

      In nginx I added the proxy_protocol directive to the listen ssl directive, this is required to get the real ip from haproxy (that is why I modified that configuration file as well):

      listen 127.0.0.1:443 ssl proxy_protocol;

      And to allow and deny ip connections the known lines:

          allow 10.10.10.0/24;
          deny all;
    • If you allow access to all your Nginx virtual hosts to VPN clients only, you can simply make Nginx listen on the VPN interface,

      listen  10.10.10.1:443  ssl

      Check the /etc/nginx/nginx.conf file and the default Nginx virtual host to see the there are listen 443 ssl directives, change them to listen 10.10.10.1:443 ssl. So your Nginx virtual host won’t accept connections from the public Internet. Then use RPZ to make an “intranet” DNS record for the domain.

      You don’t need to change the HAProxy configurations or add allow, deny directives in Nginx.

      • Yeah!

        You’re right. That is simpler than the way I did it. 🙂

  • Hello, how can I know exactly if my client is connected to my ocserv?
    And see it online.

  • Help please view the client connection history in ocserv through specific commands

  • Thank you so much.
    Please tell me how you can make sure AnyConnect does not turn off on mobile.

  • Mysterion
    6 months ago

    Great tutorial. Thanks!

  • Ubuntu 20.10 gives below error , When i checked the file is present there.

    error connecting to sec-mod socket ‘/run/ocserv.socket.83a664e5’: No such file or directory

    ● ocserv.service - OpenConnect SSL VPN server
         Loaded: loaded (/lib/systemd/system/ocserv.service; enabled; vendor preset: enabled)
         Active: active (running) since Fri 2020-12-04 11:22:49 UTC; 6s ago
           Docs: man:ocserv(8)
       Main PID: 3513 (ocserv-main)
          Tasks: 2 (limit: 8100)
         Memory: 1.6M
         CGroup: /system.slice/ocserv.service
                 ├─3513 ocserv-main
                 └─3514 ocserv-sm
    
    Dec 04 11:22:49 b2-7-de1-2 ocserv[3513]: note: setting 'pam' as primary authentication method
    Dec 04 11:22:49 b2-7-de1-2 ocserv[3513]: error connecting to sec-mod socket '/run/ocserv.socket.83a664e5': No such file or directory
    Dec 04 11:22:49 b2-7-de1-2 ocserv[3513]: note: setting 'file' as supplemental config option
    Dec 04 11:22:49 b2-7-de1-2 ocserv[3513]: listening (TCP) on 0.0.0.0:443...
    Dec 04 11:22:49 b2-7-de1-2 ocserv[3513]: listening (TCP) on [::]:443...
    Dec 04 11:22:49 b2-7-de1-2 ocserv[3513]: listening (UDP) on 0.0.0.0:443...
    Dec 04 11:22:49 b2-7-de1-2 ocserv[3513]: listening (UDP) on [::]:443...
    Dec 04 11:22:49 b2-7-de1-2 ocserv[3513]: main: initialized ocserv 0.12.6
    Dec 04 11:22:49 b2-7-de1-2 ocserv[3514]: sec-mod: reading supplemental config from files
    Dec 04 11:22:49 b2-7-de1-2 ocserv[3514]: sec-mod: sec-mod initialized (socket: /run/ocserv.socket.83a664e5)
    
    • If you look carefully at the log, it said it can’t find the socket, and next, it initialized this socket. So problem solved, right? 🙂

  • how to fix the: transmitted packet is too large (emsgsize) ?

  • Hi,

    You said to edit /etc/bind/named.conf.options to allow VPN clients to send recursive DNS queries
    I didn’t find this file on my server, what should I do exactly?

    thanks for your article

  • So everything is running fine with IPv4. When enabling ipv6 on the dns. After I add

    allow-recursion { 127.0.0.1; 10.10.10.0/24; fda9:4efe:7e3b:03ea::/48; };

    The DNS for the IPv6 and the IPv4 stop working.

    If I remove fda9:4efe:7e3b:03ea::/48; from the bind9 config the ipv4 dns will start working again. any ideas?

  • Hi, I’ve used this guide a few times for ipv4 successfully, but just now found that the ipv6 section seems to be incomplete. In the “Set Up IPv6 in Firewall (Debian, Ubuntu)” section of your other guide here https://www.linuxbabe.com/linux-server/ocserv-vpn-server-apache-nginx-haproxy and there is the additional step of adding the two

    -A ufw6-before-forward...

    , which is missing from this guide. Once I added those lines, ipv6 forwarding works properly.

  • Khazhbikar Tsarnaev
    2 weeks ago

    First of all, thank you very much for this valuable article and your beautiful expression. I did everything successfully. However, there is one point that I do not understand or cannot do. Cisco AnyConnect uses TLS 1.2 for some reason. Can’t we force this to use TLS 1.3? All packages on my system have the latest version. Looking forward to your valuable feedback. Good work.

    • Cisco Annyconnect client has some problems when using TLS 1.3. You can force the server to use TLS 1.3, but if the client has a compatibility problem, the VPN would stop working.

      • Khazhbikar Tsarnaev
        1 week ago

        Thank you for your valuable reply. Thank you for your valuable reply. OpenConnect’s own client also uses the TLS 1.2 protocol. Either there is a different situation in these applications or I am doing something wrong. I want to understand the issue better. How can I force the system to use TLS 1.3? Even though I edit this on Apache, it doesn’t work. It persistently uses TLS 1.2. By the way, I’m running with TCP only, DTLS is turned off. I wonder if TLS over TCP currently only supports TLS 1.2? What is your opinion on this? I’m curious about your comments.

    • TLS1.3 will be disabled when cisco client compatibility is enabled. See here: ocserv changelog

Leave a Comment

  • Comments with links are moderated by admin before published.
  • Your email address will not be published.
  • Use <pre> ... </pre> HTML tag to quote the output from your terminal/console.
  • Please use the community (https://community.linuxbabe.com) for questions unrelated to this article.
  • I don't have time to answer every question. Making a donation would incentivize me to spend more time answering questions.

The maximum upload file size: 2 MB. You can upload: image. Links to YouTube, Facebook, Twitter and other services inserted in the comment text will be automatically embedded.