Computer Laboratory

Malcolm Scott

strongSwan IKEv2 server configuration

Following substantial trial-and-error, I've configured a strongSwan VPN server to serve primarily Windows clients. This page explains my configuration and some of the reasons that led to various choices. Hopefully it is useful to someone!

My configuration was initially based upon the strongSwan example EAP configuration for multiple Windows 7 clients, with several modifications.

I'm using strongSwan 5.1.2 on Ubuntu 14.04. IKE was changed substantially in strongSwan 5 and I do not expect this configuration to work at all on versions earlier than that.

Aim

To provide an end-user-facing VPN service with at least the following properties:

  • Is at least "moderately" secure
  • Supports clients running Windows 7 and above, and as many other OSes as possible
  • Provides IPv4 and IPv6 addresses to VPN clients
  • The server uses a SSL certificate chain signed by a public CA
  • Authenticates users and passwords from a flat-file "database" (ipsec.secrets)
    • Unfortunately it is necessary to store passwords in plaintext for most EAP authentication protocols—in particular, for the common subset of EAP protocols supported by the majority of clients, which pretty much just means EAP-MSCHAPv2. See below, and also this handy table of protocol and password compatibility.
    • In future I may switch to RADIUS or LDAP authentication, but that is a challenge for another day…

IKEv2 in brief

IKEv2 is my VPN protocol of choice (or more strictly, the key exchange protocol which configures IPsec tunnels for me). It establishes a connection as follows:

  1. Authentication phase 1: bring up a SSL connection over UDP (port 500)
  2. Authentication phase 2: authenticate the user via various EAP (Extensible Authentication Protocol) mechanisms, within that SSL connection for protection
  3. Encrypted ESP (Encapsulating Security Payload) tunnel, using either of the following:
    • Pure native ESP: encapsulating encrypted IP packets directly within another IP packet (using IP protocol 50, not UDP or TCP!)
    • NAT Traversal: ESP within UDP (port 4500), to avoid problems forwarding IP protocol 50 which can't easily pass through NAT routers

IKEv2 improves upon IKEv1 in several ways; in particular it's simpler to configure on the client (believe it or not) as it doesn't require clients to present a SSL certificate. IKEv2 is relatively new, but gaining support on modern OSes.

Packages to install

You'll probably need some or all of the following packages installed (using Ubuntu package names here):

  • strongswan-ike — strongSwan IKEv2 (and IKEv1) daemon, charon
  • strongswan-starter — utilities to configure and wrap charon (because interacting directly with charon is unpleasant)
  • strongswan-plugin-eap-mschapv2 — EAP-MSCHAPv2 authentication plugin (our authentication protocol of "choice")
  • (strongswan-plugin-openssl — a SSL implementation will be pulled in by strongswan-ike, but there are several to choose from; I have only tested the OpenSSL one)

If you're using Debian, you probably want:

  • strongswan-charon
  • libstrongswan-standard-plugins
  • libcharon-extra-plugins

But note that on Debian, libcharon-extra-plugins includes and will automatically enable many plugins that you don't need. Consider disabling unwanted plugins in /etc/strongswan.d/charon/*.conf.

The main configuration file: /etc/ipsec.conf

The main configuration file is /etc/ipsec.conf which I'll give in full here, and then work through line-by-line. The syntax of ipsec.conf is restrictive on the placement of whitespace and comments, unfortunately...

There are a few auxiliary configuration files and scripts too, which I'll give later on.

Raw configuration

config setup charondebug="cfg 2" # log proposals uniqueids=no # allow multiple connections from a given user conn myvpn keyexchange=ikev2 auto=add left=%any leftsubnet=::/0,0.0.0.0/0 leftauth=pubkey leftcert=/etc/ssl/private/vpn-server.crt leftsendcert=always right=%any rightsourceip=192.0.2.0/25,2001:db8::/96 rightauth=eap-mschapv2 rightsendcert=never rightdns=192.0.2.200,192.0.2.201,2001:db8:8000::53:1,2001:db8:8000::53:2 eap_identity=%any leftupdown=/etc/strongswan.d/proxyndp.updown dpdaction=clear dpddelay=2400s rekey=no fragmentation=yes ike=aes256-aes192-aes128-sha384-sha256-sha1-modp3072-modp2048-modp1536-modp1024! esp=aes256-aes192-aes128-sha384-sha256-sha1!

Line-by-line explanation

config setup

Start a global configuration section. Most of the VPN configuration is attached to a specific connection block, below, but a couple of things are set globally.

charondebug="cfg 2"

For ease of debugging broken connection setup I log the sets of encryption and authentication algorithms proposed by the clients, and what the client and server end up agreeing upon.

uniqueids=no

Allow more than one connection from a given user (the default will break a user's earlier connections if they bring up a later connection—in general, strongSwan's defaults are optimised for fixed point-to-point links, rather than client-to-server.

conn myvpn keyexchange=ikev2 auto=add

Start to define a specific VPN connection type using IKEv2. On strongSwan startup, load this connection and then wait for clients to connect to it (auto=add).

left=%any

strongSwan defines the VPN tunnel based on the "left" and "right" sides (one of which is probably the local network, and one is probably remote, but it's defined in terms of left and right so that an identical configuration can be used on both ends of a point-to-point link; that feature isn't so useful for a client-server relationship).

In this case, the "left" side is the server side, and the "left" network is the entire internet.

left=%any means that we should listen for connections on all network interfaces.

leftsubnet=::/0,0.0.0.0/0

Define default IPv4 and IPv6 routes which will be offered to clients on the "right" side.

Unfortunately, Windows (at least 7 and 10) ignores the offered routes. It uses local configuration, rather than the offer from the server, to configure a default route. It also usually fails to set a default IPv6 route even if configured to do so (workaround below).

leftauth=pubkey leftcert=/etc/ssl/private/vpn-server.crt

We authenticate ourselves using a public key stored in a certificate.

The file /etc/ssl/private/vpn-server.crt must only contain our public key, not the complete certificate chain.

You should put intermediate certificates in /etc/ipsec.d/cacerts; strongSwan appears to fill in the intermediate certificates from this directory automatically, provided you do not alter the leftid setting—SSL certificate chain generation relies on leftid being the DN of your certificate, which is the default if you don't specify something else. You will probably find leftid specified in most example configurations; that leads to incorrect behaviour on this setup!

You may notice that we don't configure the private key in this file. A quirk of strongSwan is that you must configure the private key in ipsec.secrets.

NB: some popular clients don't support wildcard certificates.

leftsendcert=always

We always send our certificate to the client, even if the client does not explicitly ask for it. The client should ask for it, but I've observed a case where a client fails to ask the server for a certificate and then gets upset that the server provided no certificate.

right=%any

Define the other (remote) end of the connection. Allow clients to connect from any IP address.

rightsourceip=192.0.2.0/25,2001:db8::/96

Assign each client dynamic addresses from an IPv4 and an IPv6 pool. The first and last addresses in each subnet will not be used (i.e. clients here will be assigned addresses starting with 192.0.2.1 and 2001:db8::1).

Note that despite the name, rightsourceip does not define the source IP address of the VPN connection—it defines addresses to be assigned within the VPN tunnel.

If you have strongSwan 5.2.2 or newer, you can probably use IP address ranges rather than subnets (untested).

rightauth=eap-mschapv2

Require clients to authenticate themselves using EAP-MSCHAPv2.

There are several possible EAP (authentication) protocols, many arguably better than EAP-MSCHAPv2, but our hands are tied and we must use EAP-MSCHAPv2 if we wish to support a wide variety of clients. In particular, the strongSwan native Mac OS X client only supports EAP-MSCHAPv2 and EAP-MD5. Microsoft has deprecated EAP-MD5 in Windows.

Don't worry, though: whilst MSCHAPv2 is in itself a highly dubious authentication protocol vulnerable to several attacks including fairly easy offline bruteforce, it is secure in this setup as IKEv2 runs it within a SSL tunnel.

EAP-MSCHAPv2 does, unfortunately, require passwords to be stored either as plaintext or as NTLM hashes (not much better than plaintext). Protect your ipsec.secrets file carefully.

rightsendcert=never

Don't require clients to send a SSL certificate.

rightdns=192.0.2.200,192.0.2.201,2001:db8:8000::53:1,2001:db8:8000::53:2

Provide recursive DNS servers to clients.

eap_identity=%any

Allow any defined user to connect (provided they're present in ipsec.secrets).

leftupdown=/etc/strongswan.d/proxyndp.updown

When the connection comes up, run the defined script (see below), which sets up proxy ARP and proxy NDP to allow IPv4 and IPv6 traffic in and out of the VPN without setting up explicit routes upstream.

dpdaction=clear dpddelay=2400s

Enable Dead Peer Detection (DPD), which periodically checks that the client is still responding and if it's not then the IKEv2 session and the IPsec tunnel are cleared.

I deliberately set a long DPD delay (2400 seconds = 40 minutes) so that MOBIKE (IKEv2 Mobility and Multihoming) has a chance to work. MOBIKE lets clients change their (outer) IP address whilst maintaining their VPN connection—a very handy feature for mobile clients.

rekey=no

Don't re-key the connection after it's set up (rather than periodically re-keying for additional security), due to client bugs. As the strongSwan wiki puts it: "Windows 7 does not like a VPN gateway to take the initiative."

fragmentation=yes

Make IKEv2 send smaller packets (doing its own application-layer fragmentation)—otherwise it is prone to sending very large UDP datagrams (e.g. containing the SSL certificate chain in a single packet) and relying on IP fragmentation to deliver these. IP fragmentation often doesn't work, and doesn't exist at all in IPv6.

ike=aes256-aes192-aes128-sha384-sha256-sha1-modp3072-modp2048-modp1536-modp1024!

List our acceptable encryption and message-integrity algorithms, for the authentication and key exchange process.

The client and server must agree on a mutually-acceptable set of algorithms according to an ordered list of proposals from the client and the server. Ultimately, the algorithms used are the first proposal from the client that matches one of our proposals, i.e. the client's order of preference matters and the server's does not.

Unfortunately, though, Windows (7 and 10 at least) lists its proposals in increasing order of security, starting with the least secure in the highest-preference position.

The exact order depends on the choice of encryption strength in the client configuration, as follows (Windows 10):

  • "Optional encryption" means that Windows will propose (in decreasing order of priority):
    3des-sha1, 3des-sha256, 3des-sha384, aes128-sha1, aes128-sha256, aes128-sha384, aes192-sha1, aes192-sha256, aes192-sha384, aes256-sha1, aes256-sha256, aes256-sha384
  • "Require encryption" or "Maximum-strength encryption" means that Windows will propose:
    3des-sha1, aes256-sha1, 3des-sha256, aes256-sha256, 3des-sha384, aes256-sha384

Windows 7 offers a smaller subset of these (notably, no aes256).

In other words, if the server allows triple-DES at all, the connection will use it in preference to AES.

My IKE proposal line is a compromise between allowing connections from older clients and forcing Windows not to choose poor protocols (i.e. 3DES). It results in Windows 10 clients using aes256-sha1, and Windows 7 clients using aes128-sha1 (regardless of whether "Optional" / "Require" / "Maximum-strength" encryption is specified on the client).

Allegedly, Mac OS X in some configurations might only support 3DES. You might have to add this to the ike= line—but doing so will cause Windows clients to use 3DES, too.

The modp stuff refers to the length of the Diffie-Hellman (DH) parameters. Unfortunately, few clients support better than 1024-bit DH, which is known to be poor (Logjam attack). For now, this is probably the biggest crypto vulnerability in this setup, but is unavoidable until clients are fixed.

Some clients, such as strongSwan on Android, will make use of the longest offered DH parameter. However, Android devices appear to be very slow to connect using longer parameters. The offered maximum of 3072-bit DH is a compromise which allows for reasonable security and reasonably fast connections.

esp=aes256-aes192-aes128-sha384-sha256-sha1!

Encryption and message-integrity algorithms must be configured separately for ESP (the encrypted tunnel set up after authentication and key exchange has completed), because they're implemented in a different place: strongSwan handles IKE, whereas the Linux kernel itself handles ESP crypto.

I use a similar set of protocols for ESP as for IKE.

For ESP, unlike for IKE, Windows proposes algorithms in decreasing order of security, preferring the most-secure algorithm, as follows (Windows 10):

  • "Optional encryption": aes256-sha1, aes128-sha1, 3des-sha1, followed by various entirely-insecure proposals (single DES, NULL cipher)
  • "Require encryption" or "Maximum-strength encryption": aes256-sha1, 3des-sha1

(Again, Windows 7 doesn't support aes256.)

Option I want to enable but can't: forceencaps

IP protocol 50 (native ESP tunnelled traffic) can't pass through most NAT routers. Ordinarily, IKEv2 will attempt to detect the presence of a NAT and will switch to UDP-encapsulated ESP. However, IP protocol 50 might not pass through some non-NAT firewalls, too, which won't be detected by this means.

On the face of it, it would seem a good idea to enable forceencaps=yes which would use UDP encapsulation for all tunnelled traffic, regardless of the outcome of NAT detection.

However, the Linux kernel does not (currently) implement UDP encapsulation for IPv6 ESP, so if your VPN server publishes an IPv6 address, IPv6 clients will fail to connect if this option is set.

We are therefore forced to rely on NAT detection to enable UDP encapsulation where appropriate. IPv4 clients behind NAT will automatically use UDP; IPv4 clients without NAT will use IP protocol 50. IPv6 clients can only use IP protocol 50 (but IPv6 NAT is, thankfully, not a thing we are likely to encounter).

(If you are not using IPv6, consider enabling this option.)

Auxiliary configuration files and scripts

strongSwan installs a raft of configuration files in /etc/strongswan.d and /etc/ipsec.d, most of which can be left at their defaults.

However, it's useful to change a few things:

/etc/strongswan.d/proxyndp.updown

This is a custom script to set up proxy ARP and proxy NDP so that we capture traffic from the local network for our VPN clients' IP addresses and route it into the VPN. The alternative would be to configure a route on your upstream router to direct the VPN client address pool at the VPN server.

#!/bin/bash IFACE=eth0 # configure me ADDR=${PLUTO_PEER_CLIENT%/*} case $PLUTO_VERB in up-client-v6) echo "Adding proxy NDP for $ADDR via $IFACE" ip -6 neigh add proxy $ADDR dev $IFACE ;; down-client-v6) echo "Removing proxy NDP for $ADDR via $IFACE" ip -6 neigh delete proxy $ADDR dev $IFACE ;; up-client) echo "Adding proxy ARP for $ADDR via $IFACE" ip neigh add proxy $ADDR dev $IFACE ;; down-client) echo "Removing proxy ARP for $ADDR via $IFACE" ip neigh delete proxy $ADDR dev $IFACE ;; esac 2>&1 | logger -t proxyndp.updown

Replace "eth0" on the second line of the script with your external network interface.

(Remember also to chmod +x this file.)

/etc/ipsec.secrets

This is the repository of authentication secrets—primarily clients' passwords, but also the secret (private key) for your SSL certificate.

The file should look something like this:

### SSL private key : RSA /etc/ssl/private/vpn-server.key ### Clients' credentials (plaintext) user1 : EAP "password1" user2 : EAP "password2" # ...etc. Note that the presence of whitespace either side of the ':' is significant. # You could also tell strongSwan to include secrets from elsewhere, # for example an auto-generated file: include ipsec.d/secrets/myvpn

/etc/ipsec.d/cacerts

Remember to put any intermediate certificates required by your Certification Authority in the directory /etc/ipsec.d/cacerts (one intermediate certificate per file).

Don't include the root certificate.

Client configuration

I believe that the following clients should be able to connect, with a few caveats as listed:

  • Built-in VPN support in Windows 7 or above
  • Built-in VPN support in OS X 10.11 "El Capitan" or above (untested)
  • strongSwan app on OS X 10.7 or above (untested)
  • Built-in VPN support in iOS 9 or above (untested)
  • strongSwan VPN Client app on Android
  • Network Manager on Ubuntu and other Linux distributions
    • Ensure that you have installed network-manager-strongswan and strongswan-plugin-eap-mschapv2
    • Ensure that you tick Request an inner IP address when creating the connection
    • There appears to be no IPv6 support in network-manager-strongswan at present!
  • strongSwan 5.x suite on various Unices
    • No GUI except for the Android and OS X apps, linked above
    • Client configuration left as an exercise for the reader

Generally, except where indicated, the default options for these clients should work—just specify the server name, username and password.

Workaround for Windows IPv6 bugs

Windows has a tendency not to set up VPN routes correctly. In particular most versions at the time of writing will not set up an IPv6 default route (even if that option is explicitly selected). I know of two possible workarounds:

IPv6 workaround option 1: make a persistent addition to the routing table

This should work in most Windows versions (certainly Windows 7, perhaps older), but requires administrative privileges.

  1. Set up the VPN connection from the Network and Sharing Center, making a note of the name you specified for the VPN connection (e.g. "My VPN").
  2. Open an Administrator command prompt:
    1. Click Start;
    2. Type "cmd" but don't press Enter (or find a shortcut to the Command Prompt but don't click it);
    3. Right-click the displayed shortcut to "cmd" or "Command Prompt";
    4. Click "Run as administrator".
  3. Enter netsh interface ipv6 add route ::/0 "My VPN" (replacing "My VPN" with the name of your VPN connection, as configured on the client).
  4. This new routing table entry will show up in the output of route print at all times, but will only take effect whilst the VPN connection is active.

IPv6 workaround option 2: add two static routes to the VPN properties via PowerShell

This option requires Windows 8.1 or 2012 R2 or later, with PowerShell — but works even if you don't have administrative access.

  1. Set up the VPN connection from either the (new, Metro) Network Settings app, or the (older, Windows-7-like) Network and Sharing Center, making a note of the name you specified for the VPN connection (e.g. "My VPN").
  2. Open PowerShell.
  3. Enter the following two commands:
    • Add-VpnConnectionRoute "My VPN" ::/1
    • Add-VpnConnectionRoute "My VPN" 8000::/1
    replacing "My VPN" with the name of your VPN connection, as configured on the client.

    (Two commands are required because the PowerShell cmdlet refuses to allow the addition of a default route, even though IPv6 default routes are not handled correctly elsewhere. Together the two added routes are equivalent to a default route.)

Acknowledgements

Thanks to Daniel Thomas and Chris Share for their feedback and suggestions.