IPSec Strongswan IKEv2 using authentication by certificates
Wiki entry for setting up IPSec iPhone/iPad Configuration is a bit outdated, so I created a new example which provides compatibility with most systems supporting IKEv2. Referencing this wiki entry.
This Gist was helpful when I built this.
Read me first
This is a tested example which should allow anyone to easily setup a secure and working VPN server.
In these examples lan is 192.168.0.0/16 and router's IP address is 192.168.0.1.
/etc/config/network:
config interface 'lan' option type 'bridge' option ifname 'eth0' option proto 'static' option ipaddr '192.168.0.1' option netmask '255.255.0.0' option ip6assign '60'
This setup is not necessary or mandatory, it was just used on this example. If you have DHCP issues when connecting, most likely reason is because LAN broadcast address in /etc/strongswan.conf is not configured accordingly to your LAN setup.
In this example, VPN Server's name is set to VpnTest and country code is set to Finland(FI), so you might want to change these, they appear in scripts used to generate certificates (in the end of this wiki entry). Also this setup is created for system behind a dynamic IP address, so WAN ip address is not used at all because it might change, in these cases, it is handy to use a dynamic IP hostname, there are some free provides out there, google them if in need, or if you have your own domain, it's DNS services might have a way of updating dynamic IP for a hostname. In these document this domain name is set to vpntest.lan so make sure you change occurrences of it to your own hostname. Also if you have a static IP and want it in your server certificate, check commented part in mk_server.sh script.
vpntest.lan occurs in following files:
- ipsec.conf: leftid
- mk_server.sh: SRVNAME and IPADDR variables
Also in scripts, change ORG (organisation name) to something of your liking. Accordingly, to your lan setup, make changes in:
- ipsec.conf: leftsubnet, rightsubnet, rightdns
- strongswan.conf: charon.dns1, charon.plugins.dhcp.server
Otherwise, you just could copy & paste ;) Connectivity has been tested on Mac OS X and iPhone, and there should not be any problems with Android and Windows either.
Connectivity issue with dynamic IP address
In case that you have a dynamic IP, you might face some connectivity issues after IP address has changed/updated. A workaround to this is to create a script which updates IP address to your name server, and after successful update, it restarts firewall and ipsec.
Packages
Install necessary packages.
opkg update opkg install curl strongswan-default strongswan-pki ipset strongswan-mod-openssl strongswan-mod-curl strongswan-mod-dhcp strongswan-mod-eap-tls strongswan-mod-eap-identity strongswan-mod-kernel-libipsec kmod-tun openssl-util strongswan-mod-test-vectors strongswan-mod-farp
Also, for dynamic DHCP to work, you need to use full version of dnsmasq.
/etc/init.d/dnsmasq stop opkg remove dnsmasq opkg install dnsmasq-full /etc/init.d/dnsmasq start
/etc/config/network
Define ipsec interface
config interface 'ipsec' option ifname 'ipsec0' option proto 'none' option defaultroute '0' option peerdns '0' option ipv6 '0'
/etc/firewall.user
Append policy rules.
iptables -I INPUT -m policy --dir in --pol ipsec --proto esp -j ACCEPT iptables -I FORWARD -m policy --dir in --pol ipsec --proto esp -j ACCEPT iptables -I FORWARD -m policy --dir out --pol ipsec --proto esp -j ACCEPT iptables -I OUTPUT -m policy --dir out --pol ipsec --proto esp -j ACCEPT
/etc/config/firewall
Create vpn zone
config zone option name 'vpn' list network 'ipsec' option input 'ACCEPT' option output 'ACCEPT' option forward 'ACCEPT' option masq '1' option mtu_fix '1'
Enable forwardings
config forwarding option src 'vpn' option dest 'lan' config forwarding option src 'lan' option dest 'vpn' config forwarding option src 'vpn' option dest 'wan'
Append rules to allow IPSec connections through firewall.
config rule option name 'IPSec-ESP' option src 'wan' option proto 'esp' option target 'ACCEPT' config rule option name 'IPSec-IKE' option src 'wan' option dest_port '500' option proto 'udp' option target 'ACCEPT' config rule option name 'IPSec-NAT-T' option src 'wan' option dest_port '4500' option proto 'udp' option target 'ACCEPT' config rule option name 'IPSec-Auth-Header' option src 'wan' option proto 'ah' option target 'ACCEPT'
Optional - Enable igmp pinging.
config rule option name 'Allow-Ping-From-VPN' option src 'vpn' option proto 'igmp' option icmp_type 'echo-request' option family 'ipv4' option target 'ACCEPT' config rule option name 'Allow-Ping-To-VPN' option dest 'vpn' option proto 'igmp' option icmp_type 'echo-request' option family 'ipv4' option target 'ACCEPT'
/etc/init.d/ipsec
Replace init script contents with following simple script (provided script uses /etc/config/ipsec for configuration, this setup uses ipsec/strongswan files).
#!/bin/sh /etc/rc.common # ipsec init script START=90 STOP=10 USE_PROCD=1 PROG=/usr/lib/ipsec/starter service_running() { ipsec status > /dev/null 2>&1 } reload_service() { running && { ipsec rereadall ipsec reload return } start } check_ipsec_interface() { procd_add_interface_trigger "interface.*" "ipsec0" /etc/init.d/ipsec reload } service_triggers() { procd_add_reload_trigger "ipsec" check_ipsec_interface } start_service() { procd_open_instance procd_set_param command $PROG --daemon charon --nofork procd_set_param respawn procd_close_instance }
/etc/ipsec.secrets
# /etc/ipsec.secrets - strongSwan IPsec secrets file : RSA host-vpn.der
/etc/ipsec.conf
# ipsec.conf - strongSwan IPsec configuration file # basic configuration config setup strictcrlpolicy = no uniqueids = no # Add connections here. conn default keyexchange = ikev2 ike = aes128-sha1-modp1024,aes128-sha1-modp1536,aes128-sha1-modp2048,aes128-sha256-ecp256,aes128-sha256-modp1024,aes128-sha256-modp1536,aes128-sha256-modp2048,aes256-aes128-sha256-sha1-modp2048-modp4096-modp1024,aes256-sha1-modp1024,aes256-sha256-modp1024,aes256-sha256-modp1536,aes256-sha256-modp2048,aes256-sha256-modp4096,aes256-sha384-ecp384,aes256-sha384-modp1024,aes256-sha384-modp1536,aes256-sha384-modp2048,aes256-sha384-modp4096,aes256gcm16-aes256gcm12-aes128gcm16-aes128gcm12-sha256-sha1-modp2048-modp4096-modp1024,3des-sha1-modp1024! esp = aes128-aes256-sha1-sha256-modp2048-modp4096-modp1024,aes128-sha1,aes128-sha1-modp1024,aes128-sha1-modp1536,aes128-sha1-modp2048,aes128-sha256,aes128-sha256-ecp256,aes128-sha256-modp1024,aes128-sha256-modp1536,aes128-sha256-modp2048,aes128gcm12-aes128gcm16-aes256gcm12-aes256gcm16-modp2048-modp4096-modp1024,aes128gcm16,aes128gcm16-ecp256,aes256-sha1,aes256-sha256,aes256-sha256-modp1024,aes256-sha256-modp1536,aes256-sha256-modp2048,aes256-sha256-modp4096,aes256-sha384,aes256-sha384-ecp384,aes256-sha384-modp1024,aes256-sha384-modp1536,aes256-sha384-modp2048,aes256-sha384-modp4096,aes256gcm16,aes256gcm16-ecp384,3des-sha1! mobike = yes dpdaction = clear dpddelay = 60s left = %any #leftid = <VPN SERVER ID / DOMAINNAME!!!> leftid = vpntest.lan leftsubnet = 0.0.0.0/0 leftcert = host-vpn.der leftsendcert = always right = %any rightauth = eap-tls rightsourceip = %dhcp #rightdns = <LAN DNS SERVER> rightdns = 192.168.0.1 eap_identity = %identity forceencaps = yes auto = add
/etc/strongswan.conf
# strongswan.conf - strongSwan configuration file # # Refer to the strongswan.conf(5) manpage for details # # Configuration changes should be made in the included files charon { dns1 = 192.168.0.1 load_modular = yes plugins { include strongswan.d/charon/*.conf dhcp { force_server_address = yes #use_server_port = yes # uncomment the line above if log shows that DHCP # offer can't be accepted identity_lease = yes server = 192.168.255.255 # use LAN broadcast address here, not IP address. } } } pluto { threads = 8 dns1 = 192.168.0.1 } libstrongswan { # set to n, the DH exponent size is optimized #dh_exponent_ansi_x9_42 = no #crypto_test { # on_add = yes #} } include strongswan.d/*.conf
Tools for certificates
Following scripts are provided for certificate operations:
- clean.sh: remove all generated certs
- mk_server.sh: Executed only once to root and server certificates.
- mk_user.sh: Executed for creation of client certificates.
After generating server certificates, you should disable mk_server.sh script, because if you re-create certificates, you client certificates no longer match. Also changes to these scripts allow changing certificate's validity time.
/etc/ipsec.d/clean.sh
#!/bin/sh rm /etc/ipsec.d/*/*.* 2> /dev/null
/etc/ipsec.d/mk_server.sh
#!/bin/sh SRVNAME="vpntest.lan" IPADDR="vpntest.lan" #Uncomment only if vpn server is behind a static IP #IPADDR=$(. /lib/functions/network.sh; network_get_ipaddr ip wan; echo $ip) COUNTRY="FI" ORG="VpnTest" #Change above to your org and country code VALIDDAYS="3650" LIFETIME="730" ipsec pki --gen --type rsa --size 4096 --outform der > private/strongswan.der chmod 600 private/strongswan.der ipsec pki --self --ca --lifetime $VALIDDAYS --in private/strongswan.der --type rsa --dn "C=$COUNTRY, O=$ORG, CN=$ORG Root CA" --outform der > cacerts/strongswan.der openssl x509 -inform DER -in cacerts/strongswan.der -out cacerts/strongswan.pem -outform PEM ipsec pki --print --in cacerts/strongswan.der ipsec pki --gen --type rsa --size 4096 --outform der > private/host-vpn.der chmod 600 private/host-vpn.der ipsec pki --pub --in private/host-vpn.der --type rsa | ipsec pki --issue --lifetime $LIFETIME --cacert cacerts/strongswan.der --cakey private/strongswan.der --dn "C=$COUNTRY, O=$ORG, CN=$SRVNAME" --san=$SRVNAME --san $IPADDR --san @$IPADDR --flag serverAuth --flag ikeIntermediate --outform der > certs/host-vpn.der ipsec pki --print --in certs/host-vpn.der
/etc/ipsec.d/mk_user.sh
#!/bin/sh COUNTRY="FI" ORG="VpnTest" LIFETIME="730" echo "Enter userid (no spaces or special characters):" read USERID [ -z "$USERID" ] && { echo Empty user ID. Cancelling. exit 0 } echo "Enter fullname:" read NAME [ -z "$NAME" ] && { echo Empty full name. Cancelling. exit 0 } mkdir -p /etc/ipsec.d/p12 2> /dev/null ipsec pki --gen --type rsa --size 2048 --outform der > private/$USERID.der chmod 600 private/$USERID.der ipsec pki --pub --in private/$USERID.der --type rsa | ipsec pki --issue --lifetime $LIFETIME --cacert cacerts/strongswan.der --cakey private/strongswan.der --dn "C=$COUNTRY, O=$ORG, CN=$USERID" --san "$USERID" --outform der > certs/$USERID.der openssl rsa -inform DER -in private/$USERID.der -out private/$USERID.pem -outform PEM openssl x509 -inform DER -in certs/$USERID.der -out certs/$USERID.pem -outform PEM openssl pkcs12 -export -inkey private/$USERID.pem -in certs/$USERID.pem -name "$NAME's VPN Certificate" -certfile cacerts/strongswan.pem -caname "$ORG Root CA" -out p12/$USERID.p12
Client configuration
Create client certificate with client.sh ( or manually if you want to.. ). Needed certificates:
- cacerts/strongswan.pem
- p12/USERID.p12
Send both certificates to your device. If you have a Mac, the easiest way to send them is through AirDrop. Send strongswan.pem first, install it Settings / General / Profiles. Then send the USERID.p12 and install it in the same way.
Where SRVNAME is what was used on mk-server.sh, “vpntest.lan” if you didn't change the script, and USERID is what you entered when running mk-client.sh
Copy these certificates to client device somehow (mail them, scp them, etc..) and install them (as trusted). Define connection like this:
VPN Type: IKEv2 Server Address: server ip address or url Remote ID: SRVNAME Local ID: USERID Authentication settings: Method: Certificate Certificate: USERID.p12