OpenConnect VPN Server on CentOS 7

Setting up OCServ on CentOS 7 as pseudo bridge using proxy ARP

2018-12-14 centos7 openconnect vpn

This post explains how to set up an OpenConnect VPN server (OCServ) as a pseudo bridge, which makes VPN clients appear like local ones to the network.

The following is based on the CentOS 7 Proxmox Template but can of course be applied to different CentOS installations.

Network topology

In this example a home network with a local address range of 192.0.2.0/24 is assumed. The router also functions as a DNS resolver and DHCP server at 192.0.2.1. Local devices are assigned an IP address via DHCP in the range of 192.0.2.100-200. The router acts as a DynDNS client and can be accessed via home.example.com. The router forwards port 443 to the OpenConnect VM. If port 80 was also forwarded, a valid Let’s Encrypt certificate could be acquired and used. To use that one or any other valid certificate the parameters server-cert and server-key need to be set.

The VPN clients are supposed to use an IP from the network 192.0.2.80/28 which allows a range of 192.0.2.81-94 (including the server) and doesn’t conflict with any previous IPs or IP ranges.

Installation

At first ocserv needs to be installed and set up to start automatically on system boot.

yum install ocserv -y
systemctl enable ocserv

Configuration

The following configuration changes prepare the system for file-based password-authentication plus timed one-time passwords (TOTP). Other authentication methods include RADIUS, PAM, Kerberos and more. Any option not listed stays with it’s default value.

# /etc/ocserv/ocserv.conf
auth = "plain[passwd=/etc/ocserv/passwd,otp=/etc/ocserv/passwd.oath]" # authentication using password-file and otp
listen-host-is-dyndns = true # client should resolve hostname on reconnect
default-domain = home.example.com
ipv4-network = 192.0.2.80/28 # network for the clients, first IP (192.0.2.81) will be used by the server
tunnel-all-dns = true # resolve using internal DNS resolver
dns = 192.0.2.1 # set router as DNS resolver
ping-leases = true # ping IPs before assigning an IP to a client
route = default # route all traffic through VPN, equals 0.0.0.0/0
# optional:
max-clients = 13 # limit clients to the number of IPs available, 13 in this case

To allow packets to be forwarded by the server and to let the server answer requests to other systems on the same network, IP forwarding and proxy ARP need to be activated.

echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf
echo "net.ipv4.conf.all.proxy_arp = 1" >> /etc/sysctl.conf
sysctl -p # apply wihout rebooting

Authentication

The tool ocpasswd can be used to generate new login credentials. If OTP is enabled, an empty password can be used.

ocpasswd -c /etc/ocserv/passwd <username>

To generate the TOTP secret, some pseudo-random data needs to be generated and written to the OTP file in a special format.

KEY=$(head -10 /dev/urandom | md5sum | cut -b 1-8) # use 1-30 for more complex keys
echo "HOTP/T30 <username> - $KEY" >> /etc/ocserv/passwd.oath

Finally the base32 representation of the key can be found in the verbose output of oathtool. Using -w 5 prints the next five one time passwords to match with the Google Authenticator (or similar) generated passwords.

yum install oathtool -y
oathtool --totp --verbose -w 5 $KEY
# Base32 secret: ...

An easy way to get this key to a mobile device is to generate the corresponding QR code inside the terminal. Depending on the terminal configuration ANSIUTF8 might not work. In that case the following types can also be used to display the code: ANSI, ANSI256, ASCII, ASCIIi, UTF8.

yum install qrencode -y
qrencode -t ANSIUTF8 "otpauth://totp/<some identifier>?secret=<Base32 Secret>"

If all else fails, the base32 secret can still be entered manually. This secret hast to be handeled like a password.

Ready to go

What is left is to start the service and test a successful connection using the OpenConnect client or Cisco’s AnyConnect. An invalid (self-signed) certificate will be presented to the clients (which can be trusted manually) as no certificate has been installed yet.

systemctl start ocserv