Tech 11 minutes read

Setting up a VPN server with StrongSwan on Ubuntu 20.04

Having a Virtual Private Network (VPN) server enables you to encrypt traffic between your client devices (laptop, cell phone, or tablet) and a VPN server, and thus add another layer of security and privacy to your day to day online activities. This tutorial outlines the steps for setting up a dedicated VPN instance using StrongSwan on an Ubuntu 20.04 server instance.

The protocol that’s used for securely routing the traffic through VPN is IKEv2, which stands for Internet Key Exchange version 2. In a nutshell, it’s a fairly modern protocol that’s part of the IPSec protocol suite which enables secure session initiation with a VPN server and is supported by a wide range of devices and operating systems.

Preparing an Ubuntu 20.04 instance

Before we proceed with installing all necessary VPN-related software packages, we’ll make sure our Ubuntu 20.04 server is up to date and has a dedicated VPN user.

Provision an Ubuntu 20.04 server at your provider of choice and log into it as root. Then, make sure the instance is updated and runs the latest kernel version:

apt update && sudo apt full-upgrade -y

Although it’s not currently active, set the UFW firewall so that it will explicitly allows incoming SSH connections once it becomes active later on:

ufw allow OpenSSH

It’s often recommended to reboot the server once the upgrade is done so that the new kernel (if there is one) is used from the very beginning:

reboot now

Installing the necessary packages

After a reboot, log into the server again as root. We can now install the necessary packages, including StrongSwan:

apt install -y strongswan strongswan-pki libcharon-extauth-plugins libcharon-extra-plugins

Set up the server - side PKI infrastructure

In addition to the usual username and password credentials clients use to connect to the VPN server, the VPN instance employing IKEv2 uses certificates in the usual PKI (Public Key Infrastructure) fashion for identifying itself to the clients connecting to it. Therefore, a basic PKI infrastructure needs to be established on the server in order for the clients to be able to securely connect to the server.

The first step is to generate the server root private key file:

pki --gen --type rsa --size 4096 --outform pem > /etc/ipsec.d/private/ca-key.pem

Now we can create the root CA certificate key file using the private key we created in the previous step:

pki --self --ca --lifetime 3650 --in /etc/ipsec.d/private/ca-key.pem --type rsa --dn "CN=BlueGrid VPN CA" --outform pem > /etc/ipsec.d/cacerts/ca-cert.pem

That is going to be a self signed root CA certificate file with a 10 year validity period. If you want, you can substitute “VPN CA” with something more descriptive, preferably with the name of your organization + VPN at the end so that the certificate can be easily distinguished from other certificates you may have elsewhere.

The only thing left to do certificate - wise is to generate a server certificate that will allow the clients to verify the VPN server. First, the private key is created:

pki --gen --type rsa --size 4096 --outform pem > /etc/ipsec.d/private/server-key.pem

Now we can create the server certificate file and sign it:

pki --pub --in /etc/ipsec.d/private/server-key.pem --type rsa | pki --issue --lifetime 1095 --cacert /etc/ipsec.d/cacerts/ca-cert.pem --cakey /etc/ipsec.d/private/ca-key.pem --dn "CN=12.13.14.15" --san 12.13.1415 --san @12.13.14.15 > /etc/ipsec.d/certs/server-cert.pem

Be sure to replace 12.13.14.15 with your server’s actual IP address and make sure you have 2 SAN parameters as the example above (one with just an IP address and another one with the IP address prefixed by a @ character - some VPN clients really care about those nitpicky details!).

Configuring StrongSwan

First, back up the existing configuration file, just in case:

mv /etc/ipsec.conf /etc/ipsec.conf_bak

And then create a new one:

nano /etc/ipsec.conf

with the following contents in it:

config setup
    charondebug="ike 1, knl 1, cfg 0"
    uniqueids=no

conn ikev2-vpn
    auto=add
    compress=no
    type=tunnel
    keyexchange=ikev2
    fragmentation=yes
    forceencaps=yes
    dpdaction=clear
    dpddelay=300s
    rekey=no
    left=%any
    leftid=@<public_server_ip>
    leftcert=server-cert.pem
    leftsendcert=always
    leftsubnet=0.0.0.0/0
    right=%any
    rightid=%any
    rightauth=eap-mschapv2
    rightsourceip=10.10.10.0/24
    rightdns=1.1.1.1,8.8.8.8
    rightsendcert=never
    eap_identity=%identity
    ike=chacha20poly1305-sha512-curve25519-prfsha512,aes256gcm16-sha384-prfsha384-ecp384,aes256-sha1-modp1024,aes128-sha1-modp1024,3des-sha1-modp1024!
    esp=chacha20poly1305-sha512,aes256gcm16-ecp384,aes256-sha256,aes256-sha1,3des-sha1!

Be sure to put the correct public IP of the server in question for the leftid parameter. Also, feel free to adjust the rightdns parameter with the IPs of the DNS resolvers of choice. The above example uses Cloudflare and Google public DNS services for domain name resolution.

Configuring client side authentication

At this point, we have a functional VPN server. However, there are still a few things to do. One of them is setting up the actual credentials for the clients. They will use the credentials along with the server certificate file to securely authenticate and connect to the VPN server.

The credentials are stored in the following file:

nano /etc/ipsec.secrets

add the following lines to the file:

: RSA "server-key.pem" 
bluegrid : EAP "bLu3gr!d"

Be sure to replace the “bluegrid” and “bLu3gr!d” with the actual username and password you intend to use. Of course, multiple usernames and password pairs can be entered on each line. Of course depending on your organization's needs (per user or per device). Once done, save the changes to the file with the usual CTRL + X sequence and exit the nano editor.

Apply the changes by restarting StrongSwan:

systemctl restart strongswan-starter

Of course, you can always come back to this file and adjust the existing credentials or add new ones. Just be sure to restart StrongSwan every time you make any changes to the IPSec secrets file so the changes take effect.

Configuring the firewall & IP forwarding

The only thing left to do is configure the firewall and IP forwarding so that VPN traffic can pass through the server.

At the beginning of the article, we’ve configured UFW to allow SSH traffic so that we can log into the server remotely, so we should be safe to actually turn the firewall on:

ufw enable

You may get a warning that existing SSH connections may be disrupted. Just proceed with enabling UFW, you should stay connected.

Next, we need to allow the ordinary UDP traffic for the usual IPSec ports:

ufw allow 500,4500/udp

We need to know which network interface serves the public network traffic. We can find that out with the following command:

ip r s default

The output should be similar to this:

default via 12.13.14.15 dev eth0 proto static

in this case, the network interface we need is named eth0 but it may vary on your server.

Make a note of the interface name, we’ll need it shortly.

All that’s left to do is configure the firewall for some low-level packet filtering so that VPN network packets can flow in both directions properly.

We’ll start with editing the following file:

nano /etc/ufw/before.rules

Near the top of the file, before the section beginning with a *filter line, add the following lines:

*nat
-A POSTROUTING -s 10.10.10.0/24 -o eth0 -m policy --pol ipsec --dir out -j ACCEPT
-A POSTROUTING -s 10.10.10.0/24 -o eth0 -j MASQUERADE
COMMIT
*mangle
-A FORWARD --match policy --pol ipsec --dir in -s 10.10.10.0/24 -o eth0 -p tcp -m tcp --tcp-flags SYN,RST SYN -m tcpmss --mss 1361:1536 -j TCPMSS --set-mss 1360
COMMIT

Make sure you’ve replaced eth0 with the actual network interface name on your server! After the *filter section, add the following 2 lines:

-A ufw-before-forward --match policy --pol ipsec --dir in --proto esp -s 10.10.10.0/24 -j ACCEPT
-A ufw-before-forward --match policy --pol ipsec --dir out --proto esp -d 10.10.10.0/24 -j ACCEPT

Those 2 lines should be placed between the section beginning with *filter line and the section beginning with -A ufw-before-input -i lo -j ACCEPT line.

Save the changes to the file in question and then open the following file for editing:

nano /etc/ufw/sysctl.conf

Uncomment the following line (by removing the # character at the beginning of the line):

net/ipv4/ip_forward=1

and add the following 2 lines at the end of the file:

net/ipv4/conf/all/send_redirects=0
net/ipv4/ip_no_pmtu_disc=1

and then save the changes.

All that’s left is to restart the firewall and we should be good to go:

ufw disable
ufw enable

That’s it. We now have a fully functional VPN server running on our Ubuntu 20.04 instance!

Connecting from a macOS client machine

Now that the VPN server has been set up, we can connect our first client to it. As noted at the beginning of this tutorial, IKEv2 is widely supported natively across a wide variety of operating systems, including recent versions of Apple macOS.

First, we need to get the server’s CA certificate and copy it to our client macOS machine. The easiest way to do so is to simply cat the contents of the file to the terminal, copy the contents of the file and use a text editor on a client machine to paste it so that the certificate can be saved locally and installed to the local macOS keychain.

First, get the CA certificate file’s contents printed out:

cat /etc/ipsec.d/cacerts/ca-cert.pem

The output will begin with -----BEGIN CERTIFICATE----- and end with -----END CERTIFICATE----- lines. Copy the complete output, including those 2 lines, and paste them to the text editor of choice on the target macOS machine, such as the built-in TextEdit app. Save the file as a ca-cert.pem (make sure to get the .pem extension right) and then double click on the file in a Finder window.

By default, the certificate will be added to the local user’s keychain only. In case you want to add it to the System keychain, you’ll be prompted for your macOS system password. In that case, you need to type it and then click on the Modify Keychain button.

Once the certificate has been added to either keychain, you need to open the Keychain Access app on your macOS machine and type “BlueGrid” in the search field in the upper right corner of the window. In the search results, the CA certificate file should be visible. Right-click on it and click on Get Info.

A new window will pop up with all the info about the CA certificate file. Expand the Trust section and select “Always Trust” for IP Security (IPsec) option. You will likely be

prompted for a system password again since you’re trying to change the trust settings. Enter the password and click on Modify Keychain.

Next, go to System Preferences -> Network. On the left side, all network interfaces currently present on your machine will be listed (Wi-Fi, Bluetooth PAN, and others). Click on the + sign in the lower end of the list. Select VPN for the Interface and make sure the VPN Type is set to IKEv2. You may enter “BlueGrid VPN” or similar for Service Name. Once you’re done, click on Create.

In the Server Address and Remote ID fields, make sure the server’s public IP address is entered (12.13.14.15 in this case). The Local ID field can remain blank.

Click on Authentication Settings and fill in the Username and Password fields with the credentials you entered in the /etc/ipsec.secrets file earlier in the tutorial and click OK. Once done, click on the Apply button in the lower right end of the window and you’re done. Your macOS machine is all set!

If you click on the Connect button, a new VPN connection should be successfully established within a few seconds. Depending on your Internet connection speed and the “BlueGrid VPN” connection should now have a green icon next to it in the network interfaces list on the left.

You can verify your local Internet traffic is now routed through the VPN by observing your computer’s public IP address using tools such as whatismyip.com - you should see the public IP address of your VPN server.

Check out our tech blog section for similar content!