Tips for IKEv2 VPN (strongswan) with Certificate Authentication

This post does NOT provide full tutorial of setting-up IKEv2 VPN. Please refer to Vultr’s Guide for step-by-step tutorial.

Pure certificate authentication means certificates are used for both server & client authentication. No PSK (pre-shared key) is involved.
Pros: safer than weak password; can’t be easily shared by verbal; no need to share the server PSK; difficult to spoof the server.
Cons: more complicated to configure.

Most configuration tutorials generally use server PSK + client PSK (usually IKEv1) or server cert + client PSK (usually IKEv2). This is enough for own use or sharing with a few friends. However, for sharing with many people or within an organization, cert auth will be more convenient. Based on general settings, the following paragraphs elaborates how to setup cert auth and tips.

Strongswan configuration

Sample conf:
ipsec.conf

config setup
         uniqueids = never

#Default profile
conn %default
    dpdaction=clear
    dpddelay=3600s
    dpdtimeout=300s
    fragmentation=yes
    rekey=no

    ike=aes256gcm16-aes256gcm12-aes128gcm16-aes128gcm12-aesxcbc-sha256-sha1-modp4096-modp2048-modp1024,aes256-aes128-sha256-sha1-modp4096-modp2048-modp1024,3des-sha1-modp1024!
    esp=aes128gcm12-aes128gcm16-aes256gcm12-aes256gcm16-modp4096-modp2048-modp1024,aes128-aes256-sha1-sha256-modp4096-modp2048-modp1024,aes128-sha1-modp4096,aes128-sha1-modp1024,aes128-sha1,3des-sha1!

    left=%defaultroute
    leftauth=pubkey
    leftcert=vpnHostCert.pem
    leftsendcert=always
    leftsubnet=0.0.0.0/0

    right=%any
    rightauth=pubkey
    rightsourceip=192.168.11.0/24
    rightdns=8.8.8.8, 8.8.4.4

#IKEv1. Note this is pure PSK conf as an alternative only
conn ikev1-psk-xauth
    keyexchange=ikev1
    leftauth=psk
    leftsendcert=never
    rightauth=psk
    rightauth2=xauth
    auto=add

#IKEv2 basic conf. Write your own server fqdn
conn ikev2-pubkey
    keyexchange=ikev2
    leftid=YOUR.VPN.SERVER.FQDN
    rightid=%any
    auto=add

#IKEv2 + EAP-TLS
conn ikev2-eap-tls
    also="ikev2-pubkey"
    rightauth=eap-tls
    eap_identity=%identity

Note:

  • fragmentation means breaking down large chump of data to smaller blocks, which is quite important for cert auth, especially with some wicked ISP. However, some clients may have compatibility issue. For example, Windows 7 may or may not have trouble connecting. Please test on your own.
  • ike and esp stipulates encryption algorithms. The algorithms here is enough for popular clients.
  • Each conn paragraph is a configuration. You can choose any name you like (except %default). also can expand based on previous conf.
  • Essentially left refers to the server and right refers to the client.
  • leftid and rightid are required by a few clients like iOS.
  • We configured an IKEv1 here, because there is no native IKEv2 support on Android. To avoid chicken or the egg problem and due to heavy block of PPTP, we put an IKEv1 here. Note that you have to write relevant PSK in ipsec.secrets.

Generate mobileconfig file for iOS

Below is the shell script for automatically generating mobileconfig file. You can save it as mobiconf.sh.

#!/bin/sh
if [ $1 ]; then
  localid="$1@SOME.DOMAIN"
else
  echo "Error: missing parameter"
  echo "Usage: mobiconf.sh <username> [passphrase]"
  exit 1
fi

passphrase=$2

remoteid=${3:-'YOUR.VPN.SERVER.FQDN'}
remoteid_r=$(echo $remoteid | awk 'BEGIN{FS=".";OFS=".";ORS=""}{for (i=NF;i>0;i--) printf("%s%s",$i,(i>1?OFS:ORS))}')

ca_name=${4:-'MY CA COMMON NAME'}
conf_name=${5:-'MY CONF'}
vpn_name=${6:-'MY VPN'}

uuid_conf=$(uuidgen)
uuid_vpn=$(uuidgen)
uuid_p12cert=$(uuidgen)
uuid_cacert=$(uuidgen)
p12cert_file=$1.p12
cacert_file="cacerts/strongswanCert.pem"
output_file=$1.mobileconfig


cat >${output_file} <<EOF
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <!-- Set the name to whatever you like, it is used in the profile list on the device -->
    <key>PayloadDisplayName</key>
    <string>${conf_name}</string>
    <!-- This is a reverse-DNS style unique identifier used to detect duplicate profiles -->
    <key>PayloadIdentifier</key>
    <string>${remoteid_r}</string>
    <!-- A globally unique identifier, use uuidgen on Linux/Mac OS X to generate it -->
    <key>PayloadUUID</key>
    <string>${uuid_conf}</string>
    <key>PayloadType</key>
    <string>Configuration</string>
    <key>PayloadVersion</key>
    <integer>1</integer>
    <key>PayloadContent</key>
    <array>
        <!-- It is possible to add multiple VPN payloads with different identifiers/UUIDs and names -->
        <dict>
            <!-- This is an extension of the identifier given above -->
            <key>PayloadIdentifier</key>
            <string>${remoteid_r}.instance</string>
            <!-- A globally unique identifier for this payload -->
            <key>PayloadUUID</key>
            <string>${uuid_vpn}</string>
            <key>PayloadType</key>
            <string>com.apple.vpn.managed</string>
            <key>PayloadVersion</key>
            <integer>1</integer>
            <!-- This is the name of the VPN connection as seen in the VPN application later -->
            <key>UserDefinedName</key>
            <string>${vpn_name}</string>
            <key>VPNType</key>
            <string>IKEv2</string>
            <key>IKEv2</key>
            <dict>
                <!-- Hostname or IP address of the VPN server -->
                <key>RemoteAddress</key>
                <string>${remoteid}</string>
                <!-- Remote identity, can be a FQDN, a userFQDN, an IP or (theoretically) a certificate's subject DN. Can't be empty.
                     IMPORTANT: DNs are currently not handled correctly, they are always sent as identities of type FQDN -->
                <key>RemoteIdentifier</key>
                <string>${remoteid}</string>
                <!-- Local IKE identity, same restrictions as above. If it is empty the client's IP address will be used -->
                <key>LocalIdentifier</key>
                <string>${localid}</string>
                <!-- Optional, if it matches the CN of the root CA certificate (not the full subject DN) a certificate request will be sent
                     NOTE: If this is not configured make sure to configure leftsendcert=always on the server, otherwise it won't send its certificate -->
                <key>ServerCertificateIssuerCommonName</key>
                <string>${ca_name}</string>
                <!-- Optional, the CN or one of the subjectAltNames of the server certificate to verify it, if not set RemoteIdentifier will be used -->
                <key>ServerCertificateCommonName</key>
                <string>${remoteid}</string>
                <!-- The server is authenticated using a certificate -->
                <key>AuthenticationMethod</key>
                <string>Certificate</string>
                <!-- The client uses EAP to authenticate -->
                <key>ExtendedAuthEnabled</key>
                <integer>1</integer>
                <key>PayloadCertificateUUID</key>
                <string>${uuid_p12cert}</string>
                <!-- The next two dictionaries are optional (as are the keys in them), but it is recommended to specify them as the default is to use 3DES.
                     IMPORTANT: Because only one proposal is sent (even if nothing is configured here) it must match the server configuration -->
                <key>IKESecurityAssociationParameters</key>
                <dict>
                    <key>EncryptionAlgorithm</key>
                    <string>AES-128</string>
                    <key>IntegrityAlgorithm</key>
                    <string>SHA1-96</string>
                    <key>DiffieHellmanGroup</key>
                    <integer>14</integer>
                </dict>
                <key>ChildSecurityAssociationParameters</key>
                <dict>
                    <key>EncryptionAlgorithm</key>
                    <string>AES-128</string>
                    <key>IntegrityAlgorithm</key>
                    <string>SHA1-96</string>
                    <key>DiffieHellmanGroup</key>
                    <integer>14</integer>
                </dict>
            </dict>
        </dict>
                <dict>
            <key>PayloadIdentifier</key>
            <string>${remoteid_r}.cert.client</string>
            <key>PayloadUUID</key>
            <string>${uuid_p12cert}</string>
            <key>PayloadType</key>
            <string>com.apple.security.pkcs12</string>
            <key>PayloadVersion</key>
            <integer>1</integer>
            <!-- Optional password to decrypt the PKCS#12 container, if not set the user is prompted when installing the profile -->
            <key>Password</key>
            <string>${passphrase}</string>
            <!-- This is the Base64 encoded PKCS#12 container with the certificate and private key for the client.
                 IMPORTANT: The CA certificate will not be extracted from the container, so either install it separately or include it as payload (as seen above) -->
            <key>PayloadContent</key>
            <data>
$(cat ${p12cert_file}|base64)
            </data>
        </dict>
                <!-- This payload is optional but it provides an easy way to install the CA certificate together with the configuration -->
        <dict>
            <key>PayloadIdentifier</key>
            <string>${remoteid_r}.cert.ca</string>
            <key>PayloadUUID</key>
            <string>${uuid_cacert}</string>
            <key>PayloadType</key>
            <string>com.apple.security.root</string>
            <key>PayloadVersion</key>
            <integer>1</integer>
            <!-- This is the Base64 (PEM) encoded CA certificate -->
            <key>PayloadContent</key>
            <data>
$(grep -v '^-.*-$' ${cacert_file})
            </data>
        </dict>
    </array>
</dict>
</plist>
EOF

Note:

  • Configuration for cert auth via GUI seems broken on iOS 9.3.2, so we have to use mobileconfig file to install.
  • User p12 & CA files are prerequisites for using this script. You should tailor the parameters & path to your VPN settings.
  • The generated mobileconfig file has integrated user p12 file (base64-encoded) and CA cert (base64-encoded). Therefore only mobileconfig is needed and no additional certs are needed.
  • Pay special attention that this file can only be installed through iOS native Mail app or Safari. You cannot install it via other mail apps.

Compatibility of clients & ISP

After many tests, OS supporting of IKEv2 is listed below:

  • Windows 7: Maybe yes, maybe no (sometimes fails when fragmentation is enabled), depending on individual client OS.
  • Windows 10: Yes, but requires manual configuration of gateway & metrics (either via control panel or PowerShell).
  • Windows 10 mobile:Almost no, due to no way to configure gateway & metrics, unless deploying with MDM which is used by large corporations.
  • Android 6.0: No native support, but Strongswan client is available on Google Play.
  • iOS 9.3.2: Yes, but have to configure via mobileconfig file.
  • Shanghai Telecom normal broadband: Windows sometimes fails (>60% probability), but the connection won’t drop once connected. Usually no problem for other OS.
  • Shanghai Unicom 4G LTE: Usually can connect.
  • Shanghai Mobile 4G LTE: Almost not connectable. Seems non-DNS UDP packets are dropped.

Tips for installation method

Android

No native support for IKEv2. Please install Strongswan app.
There are two methods to import certificates. First, importing cert in Strongswan (i.e. for Strongswan only). Second, importing in Settings -> Security -> Credential Storage -> Import from Internal Storage or SD card (i.e. for System, but may display security warning).

Windows 7

In short: Install CA cert on Local Computer and install User cert on Current User. As for the other certs imported along with .p12 file, you may delete them.

Steps (both are required):
Step One: search mmc.exe in Start Menu and run as administrator. Click File -> Add/Remove Snap-in… -> Certificates -> Add -> Computer account -> OK. Click Trusted Root Certification Authorities in the left panel. Click Action -> All Tasks -> Import. Import the p12 file into Local Computer.
Step Two: Double click the p12 file and install it to Current User.

Windows 10

Steps (both are required):
Step One: Double click p12 file and install it to Local Computer.
Step Two: Double click p12 file and install it to Current User.

After configuring VPN, you may find that network is still directly connected. This is because Win10 by default doesn’t add default gateway and enables Split Tunneling. To make it global, you have to manually configure via control panel or PowerShell.
Click the network icon in System Tray -> Network & Internet Settings -> Change adapter options. Right-clck the VPN connection -> Property -> Networking -> Internet Protocol Version 4 (TCP/IPv4) -> Property -> Advanced. Check “Use default gateway on remote network”. Uncheck Automatic metric and set 15 or smaller in the textbox below.

(Microsoft: This is NOT a BUG! It’s a FEATURE! We’ll never fix it!)

Windows 10 mobile

Not possible unless deploying with MDM tools for large corporations.
Update: Actually there is workaround
(Microsoft: Our OS is designed for large corporations. No support for small companies or individuals!)

iOS 9.3.2

通过系统自带的图形界面设置似乎无法成功,只能通过mobileconfig安装。
(苹果:我们的产品连傻瓜用户都会用,各种bug就让开发者自己折腾好了)

Other tips

For OpenVZ, you have to install libipsec, otherwise the below error will prompt:

charon: 12[IKE] unable to install inbound and outbound IPsec SA (SAD) in kernel

If you installed Strongswan from epel repo, simply run yum install strongswan-libipsec and it will work.

Reference

  1. Using StrongSwan for IPSec VPN on CentOS 7, Vultr Docs
  2. IKEv2 Configuration Profile for Apple iOS 8 and newer
  3. VPN everywhere: IPsec without L2TP with strongSwan (even in OpenVZ)
  4. [strongSwan] unable to add SAD entry with SPI

Leave a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.