IKEv2
Date: 2018-08-24 · Word Count: 887 · Reading Time: 5 minutes
Given how much more efficient IPSec is than OpenVPN, I decided to setup an IKEv2 endpoint using FreeBSD on my network to allow for accessing content which is restricted to the USA and doing administration on my network when traveling.
FreeBSD
The DEFAULT kernel contains everything you need to run IPSec, just add the following to /boot/loader.conf:
if_enc_load="YES"
If you’re building a custom kernel, you’ll want:
options IPSEC
device crypto
device enc
OpenIKED
As OpenIKED has a relateively simple configuration and applications written by the OpenBSD team tend to be relatively secure, I started with it. Unfortunately, while I could get transport sessions to work, tunnel mode just wasn’t happening. Even worse, enc0 doesn’t show traffic, as noted by the port developer
StrongSwan
The StrongSwan configuration is rather verbose as it provides an endless supply of options to configure, mostly due to providing more than just IKEv2. It’s worth starting with the StrongSwan security recommendations.
The first step is to generate RSA keys. If you don’t already have a CA, you can easily set one up and generate keys with the following:
#!/bin/sh
readonly KEYDIR=/usr/local/etc/ipsec.d
readonly DN="C=AU, O=OTOH.ORG, CN=VPN"
ipsec pki --gen -s 4096 --outform pem > "${KEYDIR}/private/ca.key"
ipsec pki --self --in "${KEYDIR}/private/ca.key" \
--dn "${DN}" \
--ca --outform pem \
> "${KEYDIR}/cacerts/ca.pem"
ipsec pki --gen -s 4096 --outform pem > "${KEYDIR}/private/server.key"
ipsec pki --pub --in "${KEYDIR}/private/server.key" \
| ipsec pki --issue \
--cacert "${KEYDIR}/cacerts/ca.pem" \
--cakey "${KEYDIR}/private/ca.key" \
--dn "CN=${external_ip}" \
--san="${external_ip}" \
--flag serverAuth \
--flag ikeIntermediate \
--outform pem \
> "${KEYDIR}/certs/server.pem"
You can then generate client certs with:
#!/bin/sh
if [ $# != 1 ]; then
echo "USAGE: $0 client_name" >&2
exit 1
fi
client=$1
readonly KEYDIR=/usr/local/etc/ipsec.d
readonly DN="C=AU, O=OTOH, CN=${client}"
if [ -f "${KEYDIR}/private/client.pem" ]; then
echo "Client ${client} already exists" >&2
exit 1
fi
ipsec pki --gen -s 4096 --outform pem > "${KEYDIR}/private/${client}.pem"
ipsec pki --pub --in "${KEYDIR}/private/${client}.pem" \
| ipsec pki --issue \
--cacert "${KEYDIR}/cacerts/ca.pem" \
--cakey "${KEYDIR}/private/ca.key" \
--dn "${DN}" \
--san="${client}" \
--outform pem > "${KEYDIR}/certs/${client}.pem"
You’ll then need to make the CA cert available to all clients and distribute client keys as well.
Next is to setup ipsec.conf
. StrongSwan confusingly refers to the local side
of the connection as left and the remote side as right, which took a bit of
getting used to. Next is working out which ciphers to use for the
connection. As with all other aspects, there are a lot of options and it took a
while to work out whether things like MODP or ECP were better. Fortunately,
RFC5114 and CSNA provide some insights into selecting strong ciphers (turns
out ECP is generally what you want).
This resulted in an initial configuration of:
# NB: The ! at the end is important, or weak ciphers will be appended
ike=aes256gcm16-prfsha512-ecp521!
esp=aes256gcm16-ecp521!
Unfortunately, this didn’t connect and, after running StrongSwan in debug mode, I was able to see that the strongest I was going to get was:
ike=aes256-sha256-ecp256!
esp=aes256-sha256-ecp256!
Later, I found out about the configuration profile reference and that stronger configurations were available, which is what I implemented below.
Here’s my resulting ipsec.conf
:
config setup
# Increase debug level
# charondebug = ike 3, cfg 3
conn %default
# NB: MacOS only does the weaker ciphers
ike=aes256gcm16-prfsha512-ecp521,aes256gcm16-prfsha384-ecp384,aes256gcm16-prfsha256-ecp521,aes256-sha256-ecp256!
esp=aes256gcm16-ecp521,aes256gcm16-ecp384,aes256-sha256-ecp384,aes256-sha256-ecp256!
# Dead peer detection will ping clients and terminate sessions after
# timeout
dpdaction=clear
dpddelay=35s
dpdtimeout=2000s
keyexchange=ikev2
auto=add
rekey=yes
reauth=yes
fragmentation=no
compress=yes
mobike=yes
type=tunnel
# Default is 3h, rekey more often
lifetime=1h
# Rekey when this many bytes have been transferred
lifebytes=536870912
leftauth=pubkey
left=${external_ip}
leftid=${external_ip}
leftcert=${path_to_server_cert}
leftsendcert=always
# Routes pushed to clients. If you don't have ipv6 then add ::/0
# This route sends all traffic via the VPN
leftsubnet=0.0.0.0/0
# right - remote (client) side
right=%any
rightid=%any
# ipv4 and ipv6 subnets that assigns to clients. If you don't have ipv6
# then remove it
rightsourceip=${internal_netblock}/24
rightdns=${dns_server}
rightauth=secret
# MacOS will not connect without this
conn ikev2-mschapv2-apple
rightauth=eap-mschapv2
If you have windows clients, you’ll want to add
conn ikev2-mschapv2
rightauth=eap-mschapv2
You’ll also need an ipsec.secrets
:
: RSA ${path_to_server_cert}
user1-laptop : EAP "${user_laptop_passphrase}"
user1-phone : EAP "${user_phone_passphrase}"
If you want to use pre-shared-keys (not recommended), then replace : RSA ...
with : PSK "${pre_shared_key}"
. As you can see, I use separate keys for my
laptop and my phone, to allow both to connect at once. An alternative is to
turn off uniqueids, which will allow any given user to connect more than once
simultaneously, but this is not recommended.
MacOS
After getting a tunnel going, the next surprise was that my laptop disconnected every 8 minutes. Turns out MacOS doesn’t allow for perfect forward secrecy if you use the GUI to setup your VPN. My first thought was that it may be an expiry rekey issue, but no amount of fiddling with these settings made a difference.
For those also trying to debug IKEv2 issues in MacOS, you can increase the log level via:
sudo defaults write \
/Library/Preferences/com.apple.networkextension.control.plist LogLevel 6
After some poking around, I came across Apple Configurator, which will allow for generating configuration profiles which you can share between clients. Using configurator, you can generate a configuration with the following important properties:
- PFS: Enabled
- DiffieHellmanGroup: 21
- EncryptionAlgorithm: AES-256-GCM
- IntegrityAlgorithm: SHA2-256
- DisableMOBIKE: 0
After saving, you can then distribute this file to all your Mac clients to automatically setup a properly configured VPN.