0xCABBA9E

22 May 2024

OpenIKED based IPsec VPN tunnel on FreeBSD

This post will show how to set up an IPsec based VPN tunnel using OpenIKED. Road warrior clients will be placed within their own subnet of 10.0.5.0/24. Authentication between road warriors and the VPN server will be based on certificates. A word of advice for those attempting this journey; IPsec based VPNs with certificate based authentication is a messy endeavour. The first messy part is the Internet Key Exchange (IKE), both client and server have their own set of supported algorithms. During key negotiation a common ground needs to be found between client and server regarding cipher suites. Second messy part are the certificates, as a random guy on the internet, has so nicely phrased: public key infrastructure (PKI) and X.509 certificates are a wild, wild, west.

Config

The configuration file for the IPsec tunnel is very simple. For a full set of options see iked.conf(5).

# /usr/local/etc/iked.conf
ikev2 "bram" esp \
        from any to 10.0.5.0/24 \
        local vpn.cbbg.nl peer any \
        srcid vpn.cbbg.nl \
        config address 10.0.5.0/24 \
        config name-server 192.168.0.1

I suspect the srcid is used to find the matching server certificate.

packet filter (PF) config

Below is an excerpt from my pf config file with the relevant pieces related to creating an IPsec VPN tunnel.

# /etc/pf.conf
udp_services = "{isakmp, ipsec-nat-t}"
nat on $ext_if from 10.0.5.0/24 to any -> ($ext_if)
pass in proto udp to any port $udp_services

IPsec requires port 500 (isakmp) and if one of the devices is behind a NAT, port 4500 (ipsec-nat-t) is required as well. Service names and the related ports are specified in /etc/services. After making changed to the configuration file, make sure to reload it again using pfctl -f /etc/pf.conf.

Certificates

For authentication purposes, at least three certificates are required. These are:

  • Root certificate (Certificate Authority)
  • Server certificate to authenticate the server
  • Client certificate(s) to authenticate client(s)

This section will describe how these certificates are generated using the openssl command. Note that these certificates can also be generated by using the ikectl(8) command, but for educational purposes it is fun to do manually.

Root certificate

This is the mother of all certificates, and is most commonly referred to as the Certificate Authority (CA). This certificate is used for signing the other certificates, thereby creating a chain of trust. The commands to create the CA certificate are listed below.

openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:4096 -out ca.pem
openssl req -x509 -days 1001 -key ca.pem -sha1 -addext basicConstraints=CA:TRUE -subj "/C=NL/O=Jabberwocky/CN=Jabberwocky Root CA" -out ca.crt

First the private and public key pair is generated and stored in ca.pem. After this the root certificate is generated.

Server certificate

This certificate is used by the server to authenticate itself to the clients. It is generated in similar fashion as the root certificate, with the major difference that this time CA is set to FALSE.

openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out server.pem
openssl req -x509 -CA ca.crt -CAkey ca.pem -key server.pem -sha1  -addext basicConstraints=CA:FALSE -addext "subjectAltName=DNS:vpn.cbbg.nl" -subj "/C=NL/O=Mad Hatter Inc./CN=vpn.cbbg.nl" -days 365 -out server.crt

Client certificate(s)

openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out client.pem
openssl req -x509 -CA ca.crt -CAkey ca.pem -key client.pem -sha1  -addext basicConstraints=CA:FALSE -subj "/C=NL/CN=bram@cbbg.nl" -days 365 -out client.crt
openssl pkcs12 -export -legacy -in client.crt -inkey client.pem -out client.p12 -name "VPN Client key" -CAfile ca.crt -chain

The last command creates a PKCS#12 file which bundles the client certificate, the client private key, and the CA certificate neatly into one file. The first time trying to import the client certificate on Android, I had the cryptic error “type the correct password”. This had nothing to do with password, but instead with the algorithm used for certificate encryption. Adding the -legacy option resolved this error.

Installing keys and certificates

Finally, copy all certificates and keys to the right places.

cp ca.crt /usr/local/etc/iked/ca
cp server.crt client.crt /usr/local/etc/iked/certs
cp server.pem /usr/local/etc/iked/private/local.key

On Android the PKCS#12 file can be imported from Settings->Biometrics and security->Other security settings->Install from device storage.

Debugging

Hardly ever will something work the first time, so here are some tips and tricks for debugging.

Server side

For printing loads of debug messages about the connection process, start the OpenIKED deamon running in the foreground.

iked -d -vv -f /usr/local/etc/iked.conf

Dumping policies and security associations:

setkey -PD
setkey -D

Print statistics on the Encapsulating Security Payload (ESP) packages and IPsec protocol:

netstat -ss -p esp
netstat -ss -p ipsec

The -ss option omits counters which are zero. To set them anyway, use a single -s.

Note that when using tcpdump, you will only see the encapsulated packages. The packages before and after encapsulation will not show up on an interface. If you would like to see these packages or apply filtering on them, have a look at the enc(4) interface.

Client side

On the road warrior side, the strongSwan application has a convenient log viewer to analyse what is happing on the client side.

Certificates

Dump certificate information:

openssl x509 -in client.crt -text -noout

On Windows platforms, the built in certutil can be used:

certutil -dump client.p12

Comments