IPSec VPN tunnel on Raspbian using strongSwan

IPSec VPN tunnel on Raspbian using strongSwan

In my backup solution for my NAS I use a Raspberry Pi configured to use vpnc to securely connect back to my home through an IPSec tunnel to a Cisco ASA firewall. (read more – https://bourskov.dk/2015/03/raspberry-pi-as-a-nas-backup-appliance/). This works quite well, but since it is a remote access VPN client the Pi will be “offline” as seen from my home network, since the script will bring up the tunnel when the backup is running and take it down afterwards. I have used this for ~8 months now and it does the job, but I would really love to have access to my off-site backup device without having to either drive to the location or have to use TeamViewer etc. to a local computer at the site. So I decided to setup the Backup Pi for Site-to-Site VPN instead of Remote Access VPN.

Site-to-Site IPsec VPNs are fairly easy to setup, but since the Backup Pi should be a “zero-touch” setup, it should be able to run of a DHCP provided address without any change to the configuration in either end of the tunnel. Since the public IP address for the backup location is subject to changes, normal static tunnel configurations cannot be used. The use of pre shared keys as authentication on dynamic VPN tunnels is very insecure and should not be used at all. Therefore authentication will use X.509 digital certificates in both ends.

There are different possibilities for bulding IPSec tunnels in Linux, and I chose strongSwan (https://www.strongswan.org/) for two reasons – It is available as a package in Raspbian and because I have some experience with strongSwan from pfSense which uses it behind the scenes.


What is used?

  • Cisco ASA 5505 running software version 9.2(1)
  • Raspberry Pi model B running Raspbian Jessie Lite, Release date 2015-11-21, Kernel version 4.1
  • Ubuntu Server with OpenSSL Certification Authority



Install strongSwan

sudo apt-get update
sudo apt-get install -t jessie strongswan
sudo apt-get install -t jessie libcharon-extra-plugins


Configuration added to /etc/ipsec.conf

# Default settings for all profiles where not specifically set

conn %default

conn BKG-FW
rightid="C=DK, ST=Syddanmark, L=Kolding, O=Urskov, CN=BKG-FW01"

I will not go through all parameters in details, since full official documentation for ipsec.conf can be found here: https://wiki.strongswan.org/projects/strongswan/wiki/IpsecConf

Left is the “local” endpoint of the tunnel, which means the endpoint where the ipsec.conf is located. Right is the far end of the tunnel, the remote party.

left – strongSwan outside address –  is set to %any since this end will receive a dynamic IP address from DHCP.
leftsubnet – protected network behind strongSwan – set to a virtual host/loopback address –
leftid – IKEID sent by this endpoint
right – Remote endpoint outside address – could be IP or FQDN
rightsubnet – protected network behind remote endpoint
rightid – IKEID from remote endpoint – This is the remote endpoints certificate – “C=DK, ST=Syddanmark, L=Kolding, O=Urskov, CN=BKG-FW01”
auto=start – open tunnel when ipsec is started – i.e. at boot time
left|rightauth – how endpoints will authenticate – pubkey = certificates
leftcert – path to certificate to use for this connection
ike – Phase 1 (IKE) parameters – AES256 – SHA – D-H group2 (a ! means only use these parameters)
esp – Phase 2 (IPsec) parameters – AES256 – SHA – (a ! means only use these parameters)
keyexchange – key exchange protocol – IKEv1 or IKEv2

left|rightsubnet defines the traffic, that will be encrypted and sent through the tunnel. Since the Pi only has a single NIC, and I only need to access the Pi itself, a virtual IP address is added, and used as the leftsubnet (Pi side).

In /etc/network/interfaces a virtual interface can be added

auto eth0:1
iface eth0:1 inet static

By default, IPv4 forwarding is not enabled, so this has to be changed in order for strongSwan to work. This is done in the file /etc/sysctl.conf – this will make the change permanent, but requires a reboot.

Add or uncomment following line

# Uncomment the next line to enable packet forwarding for IPv4

To make the kernel forward packets immediately, run this command

sysctl -w net.ipv4.ip_forward=1

For authentication a certificate is needed. This can be bought or a personal certification authority can be used to issue the certificates. I have used a private CA running on Ubuntu Server (see https://jamielinux.com/docs/openssl-certificate-authority/ for inspiration if you want to setup your own CA.)

In order to use a certificate to authenticate IPSec tunnels, at least on an ASA, an IPSECTUNNEL OID have to be in the extended key usage of the certificate. The OID is:

In OpenSSL just add the OID to the extendedKeyUsage field of the profile used to sign the certificate.

Ideally, you should generate private keys and the certificate request on the device needing the certificate. This is more secure, since the private key never leaves the device. I have created my keys and request on the CA server because it easy – yes, I’m lazy… 😉

Generate private key with 2048 bits

openssl genrsa -aes256 -out intermediate/private/bckpi01.urskov.eu 2048


Generate Certificate request using the previously  created private key

openssl req -config intermediate/openssl.cnf  -key intermediate/private/bckpi01.urskov.eu -new -sha256 -out intermediate/csr/bckpi01.urskov.eu


Sign certificate, and set validity, usage etc.

openssl ca -config intermediate/openssl.cnf -extensions ipsec_cert -days 740 -notext -md sha256 -in intermediate/csr/bckpi01.urskov.eu -out intermediate/certs/bckpi01.urskov.eu.cert.pem


Now the certificate is ready to use, and the private key and signed certificate should be copied to /etc/ipsec.d/private and /etc/ipsec.d/certs respectively.

To make strongSwan able to use the certificate for authentication, the /etc/ipsec.secrets file needs to be updated with a reference to where the private key is stored. If you used a password to protect the private key, which I would recommend, you also needs to specify this password in order for strongSwan to decrypt the key.

# This file holds shared secrets or RSA private keys for authentication.
# RSA private key for this host, authenticating it to any other host
# which knows the public part.
# this file is managed with debconf and will contain the automatically created private key
include /var/lib/strongswan/ipsec.secrets.inc

: RSA bckpi01.urskov.eu.key.pem "verySecretPassWord"

In etc/ipsec.conf a reference to the signed certificate is needed so strongSwan knows which certificate to use. The keyword leftcert is where the certificate path should be placed.


The Cisco ASA will also need a certificate to use for authentication, and can be issued the same way. My ASA already had a certificate from my CA, so this will not be covered here.

But installing a device certificate is not all that needs to be done, since everybody can create their own CAs and sign their own certificates, a trusted root and/or intermediate CA certificate is needed as well. These certificates, is a part of the certificate chain and provides a way for endpoints to verify if the certificate is actually trustworthy or not.

The root and intermediate CA certificates we trust, should be copied to /etc/ipsec.d/cacerts. Likewise on the Cisco ASA we need to install the certificates, a so called trustpoint in the ASA (not covered here).


The ASA needs to be configured to accept connection from the Pi.

First a certificate for the ASA needs to be imported as well as any root and intemediate certificates


Create a trustpoint for the root and intermediate (if any).

crypto ca trustpoint MY-CA
enrollment terminal
crl optional
crypto ca authenticate MY-CA
Enter the base 64 encoded CA certificate.
End with the word "quit" on a line by itself:


Next create a trustpoint for the device certificate for use with VPN. For ease I’ve used a PKCS12 file containing both the private key and the signed ceritificate

crypto ca import DEV-CERT pkcs12 "passphrase"
Enter the base 64 encoded pkcs12.
End with the word "quit" on a line by itself:
-----BEGIN PKCS12-----
-----END PKCS12-----

Now, setup IKEv1 parameters to match the parameters configured in StrongSWAN. Note that multiple policies can be configured on the ASA. The policy number is used as preference for first match.

crypto ikev1 policy 20
authentication rsa-sig
encryption aes-256
hash sha
group 2
lifetime 86400

Configure IPsec transform set

crypto ipsec ikev1 transform-set ESP-AES-256-SHA esp-aes-256 esp-sha-hmac


Create an access-list for interesting traffic for the tunnel.

access-list VPN-BACKUP-PI extended permit ip


Create a group policy for the tunnel

group-policy GRP-BCKPI-VPN internal
group-policy GRP-BCKPI-VPN attributes
vpn-tunnel-protocol ikev1

Next, configure a tunnel-group for the VPN tunnel and map it with a certificate map for a specific CN.

tunnel-group DYN-BCKPI-VPN type ipsec-l2l
tunnel-group DYN-BCKPI-VPN general-attributes
default-group-policy GRP-BCKPI-VPN
tunnel-group DYN-BCKPI-VPN ipsec-attributes
peer-id-validate nocheck
ikev1 trust-point VPN-CERT
crypto ca certificate map DefaultCertificateMap 10
subject-name attr cn eq bckpi01
tunnel-group-map DefaultCertificateMap 10 DYN-BCKPI-VPN

Wrap it all up in a crypto map and enable the map on an interface

crypto dynamic-map DYN-BCKPI-MAP 5 match address VPN-BACKUP-PI
crypto dynamic-map DYN-BCKPI-MAP 5 set ikev1 transform-set ESP-AES-256-SHA
crypto map outside_map 65533 ipsec-isakmp dynamic DYN-BCKPI-MAP
crypto map outside_map interface outside


That about it…

Verify status of strongSwan by typing: ipsec statusall


The Pi can now be accessed on the virtual address of from inside mt home LAN


Job done… 🙂


UPDATE: I have experienced some problems with the tunnel going down for various reasons, so I created a small script to check the status of the tunnel and reconnect if it should be down.


# Check if IPSEC service is running
ipsec status

if [ $? != 0 ] ; then
echo "IPSEC service NOT running! Starting...";
logger -t IPSEC-CHECK Service NOT running! Starting...
ipsec start
# Wait to allow tunnel to go up...
sleep 2
echo "IPSEC service is running...";
logger -t IPSEC-CHECK Service is running...

TUNNELSTATUS="$(ipsec status BKG-FW | grep 'ESTABLISHED')"

if [ -z ";$TUNNELSTATUS"; ] ; then
echo ";Tunnel NOT up! Bringing up...";
logger -t IPSEC-CHECK Tunnel NOT up! Bringing up...
ipsec up BKG-FW
echo "Tunnel is established, no more to do...";
logger -t IPSEC-CHECK Tunnel is established...


The script is scheduled to run every hour using crontab

# m h dom mon dow user command
10 * * * * root /home/pi/ipsec-check.sh