Got the following error while connecting to the previously configured OpenConnect server (ocserv).
GnuTLS error: A packet with illegal or unsupported version was received.
Probably because I’ve upgraded a few libraries. After upgrading ocserv to the latest version (0.12.5), this problem got solved. Here, put down a short note on ocserv settings in case I have to reinstall it again…
After all these settings, I realise that the running ocserv is still the one installed by apt. The above issue is fixed by update/install some dependencies, but I'm not able to figure out which one is really working. Install these dependencies in the following section is enough to solve the above issue.
Install ocserv
At present, using apt install ocserv
will just give you an earlier version, but it’s fine to use this version.
sudo apt update
sudo apt install build-essential pkg-config libghc-nettle-dev libghc-gnutls-dev libprotobuf-c-dev libev-dev
sudo apt install ocserv openconnect
You can also install the latest version from the source:
Download latest ocserv
Check the recent release at ftp://ftp.infradead.org/pub/ocserv/.
wget ftp://ftp.infradead.org/pub/ocserv/ocserv-0.12.5.tar.xz
tar -xf ocserv-0.12.5.tar.xz
cd ocserv-0.12.5
Compile & install ocserv
Make sure you have installed all the packages in any notice from configure. I passed the configure with the following installations:
sudo apt update
sudo apt install build-essential pkg-config libghc-nettle-dev libghc-gnutls-dev libprotobuf-c-dev libev-dev
./configure
sudo make
sudo make install
ocserv --version
Obtain TLS certificate with Let’s Encrypt
Install Let’s Encrypt client
sudo apt install software-properties-common
sudo add-apt-repository ppa:certbot/certbot
sudo apt update
sudo apt install certbot
Obtain cert with Nginx
If your Ubuntu server already has a web server listening on port 80
and 443
, and you want ocserv to use a different port, 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.
Since I’m running Nginx, let’s go ahead to obtain a cert with it. First, we need to create a virtual host, domain.example.com
for example.
sudo nano /etc/nginx/conf.d/domain.example.com.conf
Paste the following lines into the file.
server {
listen 80;
server_name domain.example.com;
root /var/www/domain.example.com/;
location ~ /.well-known/acme-challenge {
allow all;
}
}
Save and close the file. Then create the web root directory.
sudo mkdir -p /var/www/domain.example.com
Set www-data
(Nginx user) as the owner of the web root.
sudo chown www-data:www-data /var/www/domain.example.com -R
Reload Nginx to make changes take effect.
sudo systemctl reload nginx
Then, run the following command to get a Let’s Encrypt certificate using webroot plugin.
sudo certbot certonly --webroot --agree-tos --email your-email-address \
-d domain.example.com -w /var/www/domain.example.com
Edit ocserv configuration file
sudo nano /etc/ocserv/ocserv.conf
In the configuration file, find and modify the following lines:
# use separate accounts instead of system accounts to login
auth = "plain[passwd=/etc/ocserv/ocpasswd]"
# change the port number
tcp-port = 443
udp-port = 443
# locate the Let's Encrypt server certificate and server key file
server-cert = /etc/letsencrypt/live/domain.example.com/fullchain.pem
server-key = /etc/letsencrypt/live/domain.example.com/privkey.pem
# change clients limits
max-clients = 16
max-same-clients = 2
# enable MTU discovery
try-mtu-discovery = true
# set default domain
default-domain = domain.example.com
# config ipv4-network (optional)
ipv4-network = 192.168.1.0
ipv4-netmask = 255.255.255.0
# tunnel all DNS queries
tunnel-all-dns = true
# set DNS server
dns = 9.9.9.9
dns = 8.8.8.8
dns = 1.1.1.1
# comment out all the route
#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
Save and close the file, then restart ocserv.
# check the configuration
ocserv -c /etc/ocserv/ocserv.conf -f -d 1
sudo systemctl restart ocserv
Fix DTLS handshake failure
If you’re going to use ports other than 443
, it’s better to disable the ocserv.socket
.
sudo cp /lib/systemd/system/ocserv.service /etc/systemd/system/ocserv.service
sudo nano /etc/systemd/system/ocserv.service
Delete the following two lines:
Requires=ocserv.socket
Also=ocserv.socket
Save and close the file, then reload systemd.
sudo systemctl daemon-reload
sudo systemctl stop ocserv.socket
sudo systemctl disable ocserv.socket
sudo systemctl restart ocserv.service
Create ocserv accout
sudo ocpasswd -c /etc/ocserv/ocpasswd username
# you'll prompt to set password
Enable IP forwarding
sudo nano /etc/sysctl.conf
# comment out the following line
net.ipv4.ip_forward = 1
Save and close the file, then apply the changes with following command to preserve the changes across system reboots.
Firewall for IP masquerading
ip addr
# find the name of the server's main network interface, something like ens3, eth0
sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
sudo iptables -t nat -L POSTROUTING
I’ve cleared the iptables, and manage the firewall through the service provider’s web interface. Read this post may help you manage your firewall through iptables (ip6tables).
You can preserve the iptables rules using a systemd service.
# switch to root user
iptables-save > /etc/iptables.rules
Then create a systemd service.
nano /etc/systemd/system/iptables-restore.service
Paste the following content.
[Unit]
Description=Restore iptables
Before=network-pre.target
Wants=network-pre.target
[Service]
Type=oneshot
ExecStart=/sbin/iptables-restore /etc/iptables.rules
ExecReload=/sbin/iptables-restore /etc/iptables.rules
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
Then, enable this service.
sudo systemctl daemon-reload
sudo systemctl enable iptables-restore
Remember that you have to save iptables rules to the file after making any changes.
And, if you’re also use IPV6, do the same processes again with ip6tables
.
HAproxy settings (optional)
If you have configured ocserv to listen on ports other than 443
and you need to connect to ocserv with specify the port number, you can use HAproxy to help.
Install the latest HAproxy
Find the latest HAproxy stable release at: http://www.haproxy.org/. Currently, it’s 2.0.
sudo add-apt-repository ppa:vbernat/haproxy-2.0
sudo apt update
sudo apt install haproxy
Before that, enable listen-proxy-proto
in ocserv.
sudo nano /etc/ocserv/ocserv.conf
Find the change the following item.
# comment out udp port since it's bypassed by HAproxy
# udp-port = 443
listen-proxy-proto = true
Close and save the file, then go to set HAproxy.
sudo nano /etc/haproxy/haproxy.cfg
Setting SNI request as follows:
frontend env_ssl_frontend
bind *: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 domain.example.com }
backend ocserv
mode tcp
option ssl-hello-chk
server ocserv localhost:6666 send-proxy-v2
In the example, the port number is setting to 6666
, change to your assigned port number in ocserv.conf
.
Note that the send-proxy-v2
is required for ocserv.
Then restart programs.
sudo systemctl restart ocserv
sudo systemctl restart haproxy
Now, you can connect to your ocserv without specify the port number.