OpenBSD natively supports the following types of VPN server, which will allow your gadgets to connect to, if you configure them properly, they are:
- IPSec + L2TP - Very old fashioned, works with almost all devices, extremely difficult to setup and maintain. Low throughput due to complexity.
- Wireguard - Wireguard is a new open-source VPN / secure networking implementation which is very popular in Cloud-native environments due to its simplicity. It also is highly regarded by Linus Torvalds. As soon as Jason completed the implementation in Linux kernel, he started work on the OpenBSD kernel implementation as well, which is also well received by the OpenBSD community. However, it does not support dynamic address assignment and requires a separate App in order to work with Apple iOS or Android.
- IPSec IKEv2 - A newer generation of IPSec which has a higher throughput than Wireguard as long as you have a CPU that supports AES encryption instructions (basically almost all Intel / AMD CPUs in the last couple of years, and all high-end ARM CPUs, meaning all high-end gadgets). Also, it is supported natively in Mac OS, iOS and can be supported easily in Android through StrongSwan App, a popular Linux based IPSec IKEv2 implementation. We will be using this as our choice of VPN solution.
In OpenBSD, the base iked server is a native implementation of IPSec IKEv2.
The following assumptions, following my previous article on how to setup OpenBSD, applies.
- Internet on em0
- Internal network on em1, with the IP address range of 192.168.0.0/24
- VPN address is 192.168.1.0/24
Setting up a Dynamic DNS account
You will need to setup a dynamic DNS account so that your devices can connect to your home router, which probably is using a dynamic IP address from your ISP.
I strongly recommend duckdns.org, which is free and is open. Simply register via their website and obtain a update token from them.
In the examples below, we will use example.duckdns.org as our server name.
I have a very simple script to update DDNS record for DuckDNS below:
Setting up LetsEncrypt to obtain a trusted SSL certificate
Fortunately, LetsEncrypt, a wonderful organisation which issues free SSL certificates, can work with dynamic hostname such as DuckDNS.
We will use the LetsEncrypt SSL certificate for server authentication, thus avoiding installing a third party root certificate to the devices which want to connect to the VPN server.
- Configure /etc/httpd.conf to respond to LetsEncrypt's HTTP challenge.
- Create the file /etc/acme-client.conf which contains the following:
Finally, we do not want the webserver to be running all the time just responding to the LetsEncrypt challenge if you do not have a website. Hence, we will only start the httpd web server and open the firewall port only if we need to renew the certificate. I have created a handy shell script simply for this purpose:
In order for this to work, put a simple line in the front part of the firewall configuration, /etc/pf.conf just behind the match rules:
You can run this shell script manually or simply setup another cron job to run it every day at midnight. It will:
- Open up firewall port for the httpd web server.
- Start httpd web server.
- Trigger a SSL certificate renew process using acme-client
- If a renewal is successful, copy the new certificates to the right location in iked configuration, and restart iked.
- Stop the webserver.
- Close the firewall port.
I use the following /etc/iked.conf configuration file in its default location:
set fragmentation set passive set mobike set enforcesingleikesa set cert_partial_chain set dpd_check_interval 0 ikev2 "Home" passive esp \ from 0.0.0.0/0 to 0.0.0.0/0 \ peer 0.0.0.0/0 \ ikesa enc aes-256-gcm \ prf hmac-sha2-256 \ group modp2048 \ childsa enc aes-256-gcm \ group modp2048 \ srcid example.duckdns.org \ ikelifetime 0 \ lifetime 0 bytes 0 \ config address 192.168.1.0/24 \ config name-server 192.168.0.1
Changing firewall rules accordingly
We also need to change the firewall rules to allow traffics going in and out of VPN. The following 2 rules allow VPN traffic coming from Internet and allow the established VPN connection to access both internal and external servers.
Also, a match and scrub rule will adjust TCP-MSS to a smaller value, this is just an example and you can fine tune accordingly.
This above is recommended because PMTU (Path MTU discovery) does not work well in IPSec VPN. I welcome any suggestions on this.
The resulting /etc/pf.conf looks like this:
Setting up PKI certificates for iked
IKEv2 support multiple different kinds of authentications (ways to identify yourelf and allow you in). We will be using PKI certificates. OpenBSD provides ikectl command which allows you to easily create a certificate.
You will need the following certificates in order for iked to work:
- A server certificate, which is already created, and
- 1 client certificate per device using the following instructions.
The server certificate can be easily created by the following command:
Then install the server certificate:
ikectl ca vpn certificate example.duckdns.org install
Then, for each client, create a certificate using email address (doesn't matter if the email address is working or not), such as firstname.lastname@example.org
Then, copy the user certificate from the SSL certificate directory (/etc/ssl/vpn/) to the IKED certificate directory of /etc/iked/pubkeys/ufqdn/ with an example like below:
Do note that the filename is changed from <email address>.crt to <email address>. iked will identify the certificate that is used for authentication using the filename that is the same as the common name. Since the common name is in a email address format, it should be put into the ufqdn folder under /etc/iked/pubkeys/.
Installing certificates - Mac OS and iOS
After generating the device certificates, we will need to export the certificate into .pfx format so that Mac OS and iOS can use them.
Since we have adjusted the authentication and encryption protocols in /etc/iked.conf, we will use a customized mobile configuration which can work with both Mac OS and iOS. I have created a template mobile configuration as well as some shell scripts to generate Mac and iOS compatible mobileconfig files which can be imported into those devices effortlessly.
The mobileconfig profiles, once applied (by double clicking them on Macs, and downloading them through iCloud in iOS), will:
- Install a client certificate into your device, and
- Setup VPN connection accordingly with the right encryptions. I am using AES-256-GCM with DH group 14.
Generating .pfx certificate bundle using ikectl
You can generate a .pfx certificate bundle using the following command from ikectl:
It will ask you for a export passphrase and repeat it. Enter a secret that you will only share with the device that you intend to have the VPN profile installed.
A compressed archive with the file extension of .tgz will be created. Extract the email@example.com from the archive.
Normally, you can use the Apple Configurator to generate a mobileconfig file. However, I have written a quick script with some template files to make this easier. Simply copy the .pfx file into the same location of the script and the template file and execute the following command to generate the mobileconfig file:
Simply open the mobileconfig using a Mac or iOS device and the profile will be installed after following the instructions. You will also need to enter the export passphrase that you have defined previously.
The shell script and the template files can be found in my Github.
Wrap it up
Once everything is setup, you will have:
- A working IPSec IKEv2 VPN server at home
- Your devices can use the certificates generated to connect to your home VPN. If you use the examples above to generate mobile configuration for Mac OS and iOS, they will direct all network traffic through your home network before going out. In the next article, I will share a very useful case in this scenario, which I will direct all Internet traffic to a VPN provider instead of using my ISP.