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.

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:

  1. ipsec.conf: leftid
  2. 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:

  1. ipsec.conf: leftsubnet, rightsubnet, rightdns
  2. 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.

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.

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

Define ipsec interface

config interface 'ipsec'
	option ifname		'ipsec0'
	option proto		'none'
	option defaultroute	'0'
	option peerdns		'0'
	option ipv6		'0'

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

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'

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 - strongSwan IPsec secrets file
      : RSA host-vpn.der
# 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
# 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

Following scripts are provided for certificate operations:

  1. clean.sh: remove all generated certs
  2. mk_server.sh: Executed only once to root and server certificates.
  3. 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.

#!/bin/sh
rm /etc/ipsec.d/*/*.* 2> /dev/null
#!/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
#!/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

Create client certificate with client.sh ( or manually if you want to.. ). Needed certificates:

  1. cacerts/strongswan.pem
  2. 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
This website uses cookies. By using the website, you agree with storing cookies on your computer. Also you acknowledge that you have read and understand our Privacy Policy. If you do not agree leave the website.More information about cookies
  • Last modified: 2020/10/05 21:16
  • by tmomas