IPsec on Linux – Strongswan Configuration (IKEv2, Route-Based GRE, PSK)


IPsec is a handy tool for encryption connections on networks, but it can be a bit complicated to configure, especially when you’re dealing with multiple vendors. On Linux, the protocol that encrypts connections is built-in to the kernel, it’s called Encapsulating Security Payload. However there are many security parameters that need to be configured, so a tool is usually needed to handle the task of figuring that out. That’s where the IKE protocol comes in to handle negotiation of cryptographic keys and parameters between the two IPsec tunnel endpoints.

There are several projects that implement the IKE protocol on Linux, my personal favorite is Strongswan, due to its active development on Github and comprehensive implementation of IPsec’s features. You can check out the Strongswan website and their Github page for more information and open source code.

A “standard” IPsec tunnel usually has the two LAN subnets that the tunnel will be connecting configured into the tunnel “policy”, and is negotiated when bringing up the tunnel. Changing those subnets requires bringing down the tunnel and manually re-configuring it by hand. A solution to this is a “route-based” tunnel. instead of a policy, a logical or “virtual” interface (a network interface with no physical counterpart) exists with an IP address on it. Once the tunnel interface is up and encryption is working, you can treat it like any other interface and apply policies, shaping, routing protocols, etc. It’s like an Ethernet cable made out of math. There are two kinds (that I know of) of route-based tunneling, GRE and VTI. Today I’m going to configure GRE.

Ubuntu 18.04 Server

Ubuntu Linux 18.04 makes installation of Strongswan nice and easy with its package manager:

apt-get install strongswan

This can be out of date, though. If you want a really new version of Strongswan, you can try compiling from source using Github. Ubuntu 18.04 repositories have Strongswan 5.6.2, which is pretty feature-complete.


We’re trying to get Host1 (Ubuntu 18.04 Server) to be able to reach Host2 (Ubuntu 18.04 Server). You can think of the top of the topology as an Internet-like network, where the private networks that Host1 and Host2 are attached to cannot directly reach other, and wouldn’t want to even if they could since the Internet is not such a friendly place. Encryption and authentication are needed, which the IPsec tunnel provides.

Inside the tunnel there is a another set of IP-headers encapsulating packets, which is the essence of a tunnel in networking. There are virtual interfaces on either side, in this case I’ve randomly chosen for the Ubuntu side and for the Cisco side. The Ubuntu box has a tunnel interface name “james_gre”, while the Cisco box has an interface name of “tunnel0”.

Router1 (Ubuntu18.04)

The first order of business is to create a GRE virtual interface. It’s pretty straightforward on Ubuntu 18.04:

#Add the interface
ip tunnel add james_gre local remote mode gre

#Activate it
ip link set james_gre up

#Add an IP address
ip addr add dev james_gre

#Set the MTU, to account for GRE/ESP protocol overhead
ip link set dev james_gre mtu 1440

Second, we configure Strongswan. First edit the text file /etc/ipsec.conf in you favorite text editor, I use Vim. It should look something like this:

config setup

conn james_tunnel
 left=         #Outside interface of this router
 leftprotoport=47        #IP protocol 47 for GRE
 right=        #Outside interface of the cisco router
 rightprotoport=47       #IP protocol 47 for GRE
 ike=aes256-sha2_256-modp1024! #Corresponds to cisco ikev2 proposal
 esp=aes-sha2_256!       #Corresponds to cisco transform set
 type=transport          #Corresponds to cisco transform set "mode"

I use transport mode for less overhead, although you can use tunnel mode if you want. Just make sure all parameters on both sides match. Notice there is no policy to specify subnets to traverse the tunnel, the routing table determines that. Hence, route-based tunnel.

Then edit the /etc/ipsec.secrets text file, it should look like this: : PSK '12345'

Finally, restart strongswan to load your configuration.

ipsec restart

Router4 (Cisco IOSv, 15.4)

The Cisco IOS configuration is much like a policy-based tunnel except in place of a crypto-map there is an “ipsec profile”. This profile is attached to the GRE tunnel interface. Make sure to specify “mode transport” in your transform set. Your configuration will look like this, I’ve edited for brevity:

crypto ikev2 proposal james-proposal 
 encryption aes-cbc-256
 integrity sha256
 group 2
crypto ikev2 policy james-policy 
 proposal james-proposal
crypto ikev2 keyring james-ring
 peer remote-router-james
  pre-shared-key 12345
crypto ikev2 profile james-profile
 match identity remote address 
 authentication remote pre-share
 authentication local pre-share
 keyring local james-ring
crypto ipsec transform-set james-trans esp-aes esp-sha256-hmac 
 mode transport
crypto ipsec profile james-protect-gre
 set transform-set james-trans 
 set ikev2-profile james-profile
interface Tunnel0
 ip address
 ip mtu 1440
 tunnel source
 tunnel destination
 tunnel protection ipsec profile james-protect-gre
interface Ethernet0/0
 ip address
interface Ethernet0/1
 ip address


Router1 (Ubuntu18.04)

You can check to see the status of a particular tunnel with:

root@uvm1804:ipsec status james_tunnel

#Produces the following output:
Security Associations (1 up, 0 connecting):
james_tunnel[9]: ESTABLISHED 42 minutes ago,[]...[]
james_tunnel{9}:  INSTALLED, TRANSPORT, reqid 6, ESP SPIs: c4f61431_i b854a518_o
james_tunnel{9}:[gre] ===[gre]

The above output shows an established tunnel. Also try pinging across the tunnel, that’s always a surefire way to tell if things are working well.

Router4 (Cisco IOSv, 15.4)

On a Cisco IOS device, you can always use this command to show tunnel status:

Router4#show crypto session
Crypto session current status
Interface: Tunnel0
Profile: james-profile
Session status: UP-ACTIVE     
Peer: port 4500 
  Session ID: 13  
  IKEv2 SA: local remote Active 
  IPSEC FLOW: permit 47 host host 
        Active SAs: 2, origin: crypto map

Don’t forget you’ll also need to install routes, either statically or with a protocol, to get traffic other than pings across. Did I mention this is a route-based VPN?


Router1 (Ubuntu18.04)

My favorite command to troubleshoot Strongswan is this one:

cat /var/log/syslog | grep charon

Which produces the logs of Charon, the Strongswan IKE daemon. If there’s something wrong, you’ll probably notice it in here. The system log file is different on different Linux distributions, check yours to be sure which one.

Router4 (Cisco IOSv, 15.4)

In addition to “show crypto session”, you can run debugs for IPsec and ISAKMP on a Cisco IOS device:

Router4#debug crypto ipsec
Crypto IPSEC debugging is on
Router4#debug crypto isakmp
Crypto ISAKMP debugging is on
*Feb  1 06:51:20.425: IPSEC(key_engine): got a queue event with 1 KMI message(s)
*Feb  1 06:51:20.425: IDB is NULL : in crypto_ipsec_key_engine_delete_sas (), 5101
*Feb  1 06:51:20.425: IPSEC(key_engine_delete_sas): rec'd delete notify from ISAKMP
*Feb  1 06:51:20.425: IPSEC:(SESSION ID = 13) still in use sa: 0xF5222CE4
*Feb  1 06:51:20.425: IPSEC:(SESSION ID = 13) (key_engine_delete_sas) delete SA with spi 0xD0337743 proto 50 for
*Feb  1 06:51:20.425: IPSEC:(SESSION ID = 13) (delete_sa) deleting SA,
  (sa) sa_dest=, sa_proto= 50, 
    sa_spi= 0xD0337743(3493033795), 
    sa_trans= esp-aes esp-sha256-hmac , sa_conn_id= 14
    sa_lifetime(k/sec)= (4608000/3600),
  (identity) local=, remote=,
*Feb  1 06:51:20.425: IPSEC:(SESSION ID = 13) (delete_sa) deleting SA,

Be ready for some pretty funky output, and a lot of it. Generally you should be able to find some clues in the debug, though.

You’ll also want to do a packet capture, if possible. You’ll be looking for ISAKMP (a subset of IKE) messages attempting to establish the tunnel first:

Then when the tunnel is established, you’ll only see ESP packets where data should be. If you’re seeing unencrypted, something is definitely wrong:

Hit me up if you have a question or a tunnel you’re troubleshooting. I’m happy to help.