This tutorial is going to show you how to run your own VPN server by installing OpenConnect VPN server on CentOS 8/RHEL 8. OpenConnect VPN server, aka ocserv, is an open-source implementation of the Cisco AnyConnnect VPN protocol, which is widely-used in businesses and universities. AnyConnect is a SSL-based VPN protocol that allows individual users to connect to a remote network.
Why Set Up Your Own VPN Server?
- Maybe you are a VPN service provider or a system administator, which behooves you to set up your 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 login 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.
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 simply run the following command to connect to my OpenConnect VPN server.
sudo openconnect -b vpn.mydomain.com
The openconnect
client software is available for Debian, Ubuntu, Fedora, RHEL, CentOS, Arch Linux and OpenSUSE. You can easily install it with your package manager.
sudo apt install openconnect sudo dnf 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 RAM high performance KVM VPS for just $2.5 per month, which is perfect for your private VPN server. Once you have a VPS, install CentOS 8 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.
Note: 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 address.
Step 1: Install OpenConnect VPN Server (ocserv) on CentOS 8
Log into your CentOS 8 server via SSH. Then run the following commands to install the ocserv
package from the EPEL repository.
sudo dnf install epel-release sudo dnf install ocserv
Step 2: Open Ports in Firewall
Firewall on CentOS is enabled by default. It’s customary to configure ocserv to listen on port 443, so run the following commands to open TCP and UDP port 443.
sudo firewall-cmd --zone=public --permanent --add-port=443/tcp sudo firewall-cmd --zone=public --permanent --add-port=443/udp
We also need to open TCP port 80 to obtain TLS certificate from Let’s Encrypt.
sudo firewall-cmd --zone=public --permanent --add-port=80/tcp
Reload firewall for the changes to take effect.
sudo systemctl reload firewalld
Step 3: Install Let’s Encrypt Client (Certbot) on CentOS 8 Server
The gnutls-utils
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 command to install the Let’s Encrypt client (certbot) on CentOS 8.
sudo dnf install certbot
To check version number, run
certbot --version
Sample output:
certbot 1.0.0
Step 4: Obtain a Trusted TLS Certificate from Let’s Encrypt
I recommend using the standalone
or webroot
plugin to obtain TLS certificate.
Standalone Plugin
If there’s no web server running on your CentOS 8 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. Set DNS A record for vpn.example.com
at your domain registrar’s website, then run the following command to obtain certificate.
sudo certbot certonly --standalone --preferred-challenges http --agree-tos --email [email protected] -d vpn.example.com
Explanation:
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 from the following screenshot, I successfully obtained the certificate.
Using webroot Plugin
If your CentOS 8 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 web server, then create virtual host under /etc/httpd/conf.d/
directory.
sudo nano /etc/httpd/conf.d/vpn.example.com.conf
And paste the following lines into the file.
ServerName vpn.example.com DocumentRoot /var/www/ocserv
Save and close the file. Then create the web root directory.
sudo mkdir /var/www/ocserv
Set user apache
as the owner of this web root.
sudo chown apache:apache /var/www/ocserv -R
Reload Apache for the changes to take effect.
sudo systemctl reload httpd
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 web server, then create virtual host under /etc/nginx/conf.d/
.
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 user nginx
as the owner of this web root.
sudo chown nginx:nginx /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 5: Edit OpenConnect VPN Server Configuration File
Edit ocserv configuration file.
sudo nano /etc/ocserv/ocserv.conf
First, configure password authentication. By default, password authentication through PAM (Pluggable Authentication Modules) is enabled, which allows you to use CentOS system accounts to login from VPN clients. This behavior can be disabled by commenting out the following line.
auth = "pam"
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. If you want to enable certificate authentication, 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 (there’s a web server using 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/pki/ocserv/public/server.crt server-key = /etc/pki/ocserv/private/server.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
It’s recommended to enable LZ4 compression, so uncomment the following line.
compression = true
Then, set the maximal number of clients. Default is 16. Set to zero for unlimited.
max-clients = 0
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 = 0
Next, find the following line. Change false
to true
to enable MTU discovery, which can optimize VPN performance.
try-mtu-discovery = true
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=2400
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 most 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 (such as 10.10.10.0/24) to avoid IP address collision, so uncomment the above two lines and 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
Change DNS resolver address. You can use Google’s public DNS server.
dns = 8.8.8.8 dns = 8.8.4.4
or Cloudflare’s public DNS server.
dns = 1.1.1.1 dns = 1.0.0.1
Note: If you are a VPN service provider, it’s a good practice to run your own DNS resolver. 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 # character at the beginning of the following lines), which will set the server as the default gateway for the clients.
#route = 10.10.10.0/255.255.255.0 #route = 192.168.0.0/255.255.0.0 #route = fef4:db8:1000:1001::/64 #no-route = 192.168.5.0/255.255.255.0
Finally, scroll down to the end of the file (In Nano text editor, you can go to the end of the file by pressing Ctrl W
, then pressing Ctrl V
), and comment out the user-profile
parameter, because the user profile XML file is not needed by OpenConnect clients or the Cisco AnyConnect client on iOS and Android.
#user-profile = profile.xml
Note: If user-profile is enabled, the Cisco AnyConnect client on iOS will produce the following error when connecting to the OpenConnect VPN server.
Failed to download the AnyConnect profile. Please try again.
Save and close the file.
Step 6: Creating 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.
Now we can start the ocserv
serice.
sudo systemctl start ocserv
And enable auto-start at boot time.
sudo systemctl enable ocserv
You can check its status with:
systemctl status ocserv
Sample output:
● ocserv.service - OpenConnect SSL VPN server Loaded: loaded (/usr/lib/systemd/system/ocserv.service; enabled; vendor preset: disabled) Active: active (running) since Fri 2020-01-03 19:04:53 CST; 15s ago Docs: man:ocserv(8) Main PID: 19851 (ocserv-main) Tasks: 2 (limit: 5061) Memory: 3.2M CGroup: /system.slice/ocserv.service ├─19851 ocserv-main └─19853 ocserv-sm
Hint: If the above command doesn’t quit immediately, you can press the Q key to gain back control of the terminal.
By default OpenConnect VPN server listens on TCP and UDP port 443. If it’s being used by a web server, then the VPN server can’t be started. We will see how to make OpenConnect VPN server and web server use the same port later.
Step 7: Enable IP Forwarding in Linux Kernel
In order for the VPN server to route packets between VPN client 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 8: Configure IP Masquerading in Firewall
Run the following command to enable IP masquerading in the server firewall.
sudo firewall-cmd --zone=public --permanent --add-masquerade sudo systemctl reload firewalld
This will hide your VPN 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.
Now OpenConnect VPN server is ready to accept client connections.
Install and Use OpenConnect VPN client on CentOS 8/Fedora Desktop
Run the following command to install OpenConnect VPN command-line client on Fedora desktop.
sudo dnf install openconnect
On CentOS 8, you need to enable the EPEL repository in order to install the client.
sudo dnf install epel-release sudo dnf install openconnect
Then you can connect to VPN server from the command line like below. -b
flag will make it run in the background after the connection is established.
sudo openconnect -b vpn.example.com
By default, openconnect client sends request to port 443 of the server. If you configured a different port for the server, then you can add the port number.
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 as 10.10.50.139, using SSL lz4 Continuing in background; pid 2137 Established DTLS connection (using GnuTLS). Ciphersuite (DTLS1.2)-(PSK)-(AES-256-GCM).
If the connection failed, you can check the ocserv log to find out why. (Perhaps you didn’t enter the password correctly.)
sudo journaltcl -eu ocserv
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 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.
Auto-Connect on System Startup
To make 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 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
andWants=network-online.target
make this service run after network is up.- In reality, this service can still run before network is up. We add
Restart=always
andRestartSec=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 command 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 theopenconnect
command. - The
KillSignal
directive tells Systemd to send theSIGINT
signal when thesystemctl stop openconnect
command is issued. This will perform 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
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.
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 to run the Cron job daily. If the certificate is going to expire in 30 days, certbot will try to renew the certificate. It’s necessary to restart ocserv service for the VPN server to pick up new certificate and key file.
@daily certbot renew --quiet && systemctl restart 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.
You may also want to disable DTLS to bypass firewall restrictions because DTLS uses UDP port 443. Standard TLS uses TCP port 443.
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. It’s written for Ubuntu, but also works for CentOS.
In my test, standard TLS with TCP BBR enabled is two times faster than DTLS.
Troubleshooting
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.
Make OpenConnect VPN server and web server use port 443 at the same time
Normally a port can only be used by one process. However, we can use HAproxy (High Availability Proxy) and SNI (Server Name Indication) to make ocserv and Apache/Nginx use port 443 at the same time.
First, edit ocserv configuration file.
sudo nano /etc/ocserv/ocserv.conf
Find the following line.
#listen-host = [IP|HOSTNAME]
Change it to
listen-host = 127.0.0.1
This will make ocserv listen on 127.0.0.1 because later HAproxy will need to listen on the public IP address.
Then add the following line. This will allow ocserv to obtain the client IP address instead of HAproxy IP address.
listen-proxy-proto = true
Save and close the file. Restart ocserv for the changes to take effect.
sudo systemctl restart ocserv
Next, we also need to make the web server listen on localhost only, instead of listening on public IP address. If you use Nginx, edit the server block file.
sudo nano /etc/nginx/conf.d/www.example.com.conf
In the SSL server block, find the following directive.
listen 443 ssl;
Change it to
listen 127.0.0.2:443 ssl;
This time we make it listen on 127.0.0.2:443
because 127.0.0.1:443
is already taken by ocserv. Save and close the file. Then restart Nginx.
sudo systemctl restart nginx
Now install HAproxy.
sudo dnf install haproxy
Edit configuration file.
sudo nano /etc/haproxy/haproxy.cfg
Copy and paste the following lines to the end of the file. Replace 12.34.56.78
with the public IP address of your server. Replace vpn.example.com
with the domain name used by ocserv and www.example.com
with the domain name used by your web server.
frontend https bind 12.34.56.78:443 mode tcp tcp-request inspect-delay 5s tcp-request content accept if { req_ssl_hello_type 1 } use_backend ocserv if { req_ssl_sni -i vpn.example.com } use_backend nginx if { req_ssl_sni -i www.example.com } use_backend nginx if { req_ssl_sni -i example.com } default_backend ocserv backend ocserv mode tcp option ssl-hello-chk server ocserv 127.0.0.1:443 send-proxy-v2 backend nginx mode tcp option ssl-hello-chk server nginx 127.0.0.2:443 check
Save and close the file. Then restart HAproxy.
sudo systemctl restart haproxy
In the configuration above, we utilized the SNI (Server Name Indication) feature in TLS to differentiate VPN traffic and normal HTTPS traffic.
- When
vpn.example.com
is in the TLS Client Hello, HAProxy redirect traffic to the ocserv backend. - When
www.example.com
is in the TLS Client Hello, HAProxy redirect traffic to the nginx backend. - If the client doesn’t specify the server name in TLS Client Hello, then HAproxy will use the default backend (ocserv).
You can test this setup with the openssl
tool. First, run the following command multiple times.
echo | openssl s_client -connect your-server-IP:443 | grep subject
We didn’t specify server name in the above command, so HAproxy will always pass the request to the default backend (ocserv), and its certificate will be sent to the client. Next, run the following two commands.
echo | openssl s_client -servername www.example.com -connect your-server-IP:443 | grep subject echo | openssl s_client -servername vpn.example.com -connect your-server-IP:443 | grep subject
Now we specified the server name in the commands, so HAproxy will pass request accordingly. Note that the Cisco AnyConnect App doesn’t support TLS SNI, so it’s better to set ocserv
as the default backend in HAProxy configuration file.
When renewing Let’s Encrypt certificate for your website, it’s recommended that you use the http-01
challenge instead of tls-alpn-01
challenge, because HAproxy is listening on port 443 of the public IP address, so it can interfere with the renew process.
sudo certbot renew --preferred-challenges http-01
Fixing HAproxy Error
If your Nginx web server doesn’t show up in your browser and you see the following messages in haproxy log (/var/log/haproxy.log
)
Server nginx/nginx is DOWN, reason: Socket error, info: "Connection reset by peer backend nginx has no server available! Layer6 invalid response
It maybe because your backend Nginx web server is using a TLS certificate with OCSP must staple extension. Nginx doesn’t send the OCSP staple information on the first HTTP request. To make it work, be sure to add a resolver in your Nginx virtual host configuration like below.
{ .... ssl_trusted_certificate /etc/letsencrypt/live/www.example/chain.pem; ssl_stapling on; ssl_stapling_verify on; resolver 8.8.8.8; .... }
Save and close the file. Then restart Nginx.
sudo systemctl restart nginx
Also consider removing health check for the backend server in HAproxy. So change
server nginx 127.0.0.2:443 check
To
server nginx 127.0.0.2:443
Save and close the file. Then restart HAproxy.
sudo systemctl restart haproxy
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:-VERS-SSL3.0"
To disable TLS 1.0 and TLS 1.1 in OpenConnect VPN server, replace it with:
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.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.
route = 10.10.10.0/255.255.255.0
This means that after user1
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. I use this to allow my another VPS (virtual private server) to connect to this VPN server.
Save and close the file. Restart ocserv for the changes to take effect.
Wrapping Up
That’s it! I hope this tutorial helped you install and configure OpenConnect VPN on CentOS 8/RHEL 8 server. As always, if you found this post useful, then subscribe to our free newsletter to get more tips and tricks. Take care 🙂
Rate this tutorial
[Total: 0 Average: 0]