Background
The idea behind a VPN is to create a tunnel, that is to say, packets that have IP packets embedded inside IP packets. The private part means the this tunnel is encrypted so that an attacker listening along the path between the two tunnel endpoints will not be able to steal any meaningful data.
Some info about Strongswan – it’s an implementation of the IKE protocol (Internet Key Exchange) which is designed to secure exchange cryptographic key information across an untrusted network, usually the Internet, but not always. The actual encrypting of packets and sending them using a protocol called ESP (Encapsulating Security Payload) is already built into Linux, but Strongswan is needed to give ESP the correct key information to build the tunnel.
There are other IKE implementations, but I find Strongswan the be the best and most actively updated as of this writing in January 2020. You can find the Strongswan website and their documentation at https://www.strongswan.org/, as well as their Github page with the most recent code at https://github.com/strongswan/strongswan.
There are lots of ways to configure IPsec, but for this lab we’ll be using IKE version 2 with a policy-based configuration. The policy-based part means a policy negotiated by the two tunnel endpoints (routers) determines what traffic is allowed or not allowed into the tunnel. Another way to do this is route-based, where the routing tables determine tunneled traffic, allowing for more flexibility. But that’s a story for another day.
Installation
Strongswan can be installed with relative ease on a number of Linux distributions that have package managers, on Ubuntu this is pretty simple:
Ubuntu 18.04 Server
apt-get install strongswan
You can also build from source to get a more recent version, although you’ll probably want to get the instructions from their Github page, as building from source is more involved.
Topology
The idea here is to get UbuntuServer18.04-1 on the bottom left to be able to reach UbuntuServer18.04-2 on the bottom right. You can think of the U_18Router and CiscoIOSv15.6(2)T-3 as being connected to an Internet-like network, for this lab’s purposes. Needless to say, the subnet 192.168.0.0/24 on the left can’t reach 172.16.0.0/24 on the right without some sort of tunnel mechanism, and traffic needs to be encrypted as someone is capturing packets in the middle. A man in the middle, if you will 😛
I’ll be configuring Strongswan on an Ubuntu18.04 Server (U18_Router) on one end and a Cisco IOSv router on the other end. That way we can see that it actually works with other vendors.
Configuration
U18_Router (Ubuntu18.04)
First edit the text file /etc/ipsec.conf using your favorite text editor, I use vim. It should look something like this:
config setup strictctlpolicy=yes uniqueids=no conn &default ikelifetime=1440m keylife=60m conn james_tunnel left=1.1.1.1 #Outside interface of this router leftsubnet=192.168.0.0/24 #Inside network behind this router right=3.3.3.2 #Outside interface of the cisco router rightsubnet=172.16.0.0/24 #Inside network of the cisco router ike=aes256-sha2_256-modp1024! #Corresponds to cisco ikev2 proposal esp=aes-sha2_256! #Corresponds to cisco transform set keyingtries=0 ikelifetime=1h lifetime=8h authby=secret auto=start keyexchange=ikev2 type=tunnel #Corresponds to transform set "mode"
Then edit /etc/ipsec.secrets, and put your pre-shared key in there along with tunnel endpoint IP addresses:
1.1.1.1 3.3.3.2 : PSK '12345'
Then restart strongswan to load your configuration
ipsec restart
CiscoIOSv15.6(2)T-3
This is an abbreviated version of the Cisco IOS router configuration, as they tend to include a lot of info that’s not relevant here:
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 address 1.1.1.1 pre-shared-key 12345 ! ! crypto ikev2 profile james-profile match identity remote address 1.1.1.1 255.255.255.255 authentication local pre-share authentication remote pre-share keyring local james-ring ! crypto ipsec transform-set james-trans esp-aes esp-sha256-hmac mode tunnel ! crypto map james-map 1 ipsec-isakmp set peer 1.1.1.1 set transform-set james-trans set ikev2-profile james-profile match address james-vpn ! interface GigabitEthernet0/0 ip address 3.3.3.2 255.255.255.0 duplex auto speed auto media-type rj45 crypto map james-map ! interface GigabitEthernet0/1 ip address 172.16.0.1 255.255.255.0 duplex auto speed auto media-type rj45 ! ip route 0.0.0.0 0.0.0.0 3.3.3.1 ! ip access-list extended james-vpn permit ip 172.16.0.0 0.0.0.255 192.168.0.0 0.0.0.255
Verification
U18_Router (Ubuntu18.04)
Verifying the status of your tunnel is fairly simple, just issue the command ‘ipsec statusall’. Below is an example of a tunnel that’s up an running:
root@uvm1804:/var/log# ipsec statusall Status of IKE charon daemon (strongSwan 5.6.2, Linux 4.15.0-20-generic, x86_64): uptime: 19 hours, since Jan 15 21:48:59 2020 malloc: sbrk 1626112, mmap 0, used 861840, free 764272 worker threads: 11 of 16 idle, 5/0/0/0 working, job queue: 0/0/0/0, scheduled: 5 loaded plugins: charon aes rc2 sha2 sha1 md4 md5 mgf1 random nonce x509 revocation constraints pubkey pkcs1 pkcs7 pkcs8 pkcs12 pgp dnskey sshkey pem openssl fips-prf gmp agent xcbc hmac gcm attr kernel-netlink resolve socket-default connmark stroke updown eap-mschapv2 xauth-generic counters Listening IP addresses: 1.1.1.1 192.168.0.1 Connections: tunnel: 1.1.1.1...3.3.3.2 IKEv2 tunnel: local: [1.1.1.1] uses pre-shared key authentication tunnel: remote: [3.3.3.2] uses pre-shared key authentication tunnel: child: 192.168.0.0/24 === 172.16.0.0/24 TUNNEL Security Associations (1 up, 0 connecting): tunnel[26]: ESTABLISHED 6 minutes ago, 1.1.1.1[1.1.1.1]...3.3.3.2[3.3.3.2] tunnel[26]: IKEv2 SPIs: 8c847c9fa74fd6f6_i 5158e0ac789bebb4_r*, pre-shared key reauthentication in 40 minutes tunnel[26]: IKE proposal: AES_CBC_256/HMAC_SHA2_256_128/PRF_HMAC_SHA2_256/MODP_1024 tunnel{42}: INSTALLED, TUNNEL, reqid 25, ESP SPIs: cda62364_i bb016c85_o tunnel{42}: AES_CBC_128/HMAC_SHA2_256_128, 8400 bytes_i (100 pkts, 271s ago), 8400 bytes_o (100 pkts, 271s ago), rekeying in 36 minutes tunnel{42}: 192.168.0.0/24 === 172.16.0.0/24
CiscoIOSv15.6(2)T-3
On an Cisco IOS device, there are a few different show commands you can use to verify tunnel status however I find ‘show crypto session’ to quickly get you the info you need. You can get more info by adding ‘detail’ on the end:
Router#show crypto session Crypto session current status Interface: GigabitEthernet0/0 Profile: james-profile Session status: UP-ACTIVE Peer: 1.1.1.1 port 500 Session ID: 95 IKEv2 SA: local 3.3.3.2/500 remote 1.1.1.1/500 Active IPSEC FLOW: permit ip 172.16.0.0/255.255.255.0 192.168.0.0/255.255.255.0 Active SAs: 2, origin: crypto map Router#
Packet Capture
A packet capture can really help you figure out how far you have progressed and what next steps should be taken to solve a configuration issue or troubleshoot a problem. After you configure Strongswan on Linux and Crypto map on your Cisco, you should be seeing ISAKMP (an extension of IKE) protocol messages in the packet capture that are negotiating tunnel parameters:
Finally, when you have a tunnel established, you should see ESP packets carrying the actual data when UbuntuServer18.04-1 and UbuntuServer18.04-2 ping each other. You should NOT see ICMP (ping) packets in your capture.
Troubleshooting
U18_Router (Ubuntu18.04)
The Strongswan IKE daemon is called “charon”, and it deposits its log messages in the system syslog file. On different distributions this is in different locations or uses a different name, in Ubuntu 18.04 it’s in /var/log/syslog. You can search this file for clues as to what’s going wrong, I suggest using grep to find what you’re looking for. A useful starting point would be to issue ‘cat /var/log/syslog | grep charon’ to show only charon messages. The following example is an example of a typo in the Strongswan configuration resulting in the charon exiting and not attempting to bring up the tunnel. You would not see any ISAKMP packets in your packet capture:
Jan 16 18:00:22 uvm1804 charon: 00[DMN] Starting IKE charon daemon (strongSwan 5.6.2, Linux 4.15.0-20-generic, x86_64) Jan 16 18:00:22 uvm1804 charon: 00[CFG] loading ca certificates from '/etc/ipsec.d/cacerts' Jan 16 18:00:22 uvm1804 charon: 00[CFG] loading aa certificates from '/etc/ipsec.d/aacerts' Jan 16 18:00:22 uvm1804 charon: 00[CFG] loading ocsp signer certificates from '/etc/ipsec.d/ocspcerts' Jan 16 18:00:22 uvm1804 charon: 00[CFG] loading attribute certificates from '/etc/ipsec.d/acerts' Jan 16 18:00:22 uvm1804 charon: 00[CFG] loading crls from '/etc/ipsec.d/crls' Jan 16 18:00:22 uvm1804 charon: 00[CFG] loading secrets from '/etc/ipsec.secrets' Jan 16 18:00:22 uvm1804 charon: 00[CFG] loaded IKE secret for 1.1.1.1 3.3.3.2 Jan 16 18:00:22 uvm1804 charon: 00[LIB] loaded plugins: charon aes rc2 sha2 sha1 md4 md5 mgf1 random nonce x509 revocation constraints pubkey pkcs1 pkcs7 pkcs8 pkcs12 pgp dnskey sshkey pem openssl fips-prf gmp agent xcbc hmac gcm attr kernel-netlink resolve socket-default connmark stroke updown eap-mschapv2 xauth-generic counters Jan 16 18:00:22 uvm1804 charon: 00[LIB] dropped capabilities, running as uid 0, gid 0 Jan 16 18:00:22 uvm1804 charon: 00[JOB] spawning 16 worker threads Jan 16 18:00:22 uvm1804 charon: 05[CFG] received stroke: add connection 'tunnel' Jan 16 18:00:22 uvm1804 charon: 05[CFG] algorithm 'aes265' not recognized Jan 16 18:00:22 uvm1804 charon: 05[CFG] skipped invalid proposal string: aes265-sha2_256-modp1024 Jan 16 18:00:22 uvm1804 charon: 07[CFG] received stroke: initiate 'tunnel' Jan 16 18:00:22 uvm1804 charon: 07[CFG] no config named 'tunnel' root@uvm1804:/var/log#
You’ll notice on line 13 it says “algorithm ‘aes265’ not recognized”. That’s because AES265 isn’t a thing. It’s AES256.
Another example that may be harder to detect and requires some good “stare and compare” skills, would be mismatched encryption/authentication parameters. Your configurations may be valid but if parameters don’t match up, your tunnel won’t come up. The following is an example of when ESP parameters aren’t matching. On the Ubuntu side I’ve specified AES256 for ESP but the Cisco’s transform set calls for AES, which is short for AES128:
Jan 16 18:08:38 uvm1804 charon: 05[CFG] added configuration 'tunnel' Jan 16 18:08:38 uvm1804 charon: 07[CFG] received stroke: initiate 'tunnel' Jan 16 18:08:38 uvm1804 charon: 07[IKE] initiating IKE_SA tunnel[1] to 3.3.3.2 Jan 16 18:08:38 uvm1804 charon: 07[ENC] generating IKE_SA_INIT request 0 [ SA KE No N(NATD_S_IP) N(NATD_D_IP) N(FRAG_SUP) N(HASH_ALG) N(REDIR_SUP) ] Jan 16 18:08:38 uvm1804 charon: 07[NET] sending packet: from 1.1.1.1[500] to 3.3.3.2[500] (334 bytes) Jan 16 18:08:38 uvm1804 charon: 09[NET] received packet: from 3.3.3.2[500] to 1.1.1.1[500] (348 bytes) Jan 16 18:08:38 uvm1804 charon: 09[ENC] parsed IKE_SA_INIT response 0 [ SA KE No V V N(NATD_S_IP) N(NATD_D_IP) ] Jan 16 18:08:38 uvm1804 charon: 09[IKE] received Cisco Delete Reason vendor ID Jan 16 18:08:38 uvm1804 charon: 09[IKE] received Cisco FlexVPN Supported vendor ID Jan 16 18:08:38 uvm1804 charon: 09[IKE] authentication of '1.1.1.1' (myself) with pre-shared key Jan 16 18:08:38 uvm1804 charon: 09[IKE] establishing CHILD_SA tunnel{1} Jan 16 18:08:38 uvm1804 charon: 09[ENC] generating IKE_AUTH request 1 [ IDi N(INIT_CONTACT) IDr AUTH SA TSi TSr N(MOBIKE_SUP) N(ADD_4_ADDR) N(EAP_ONLY) N(MSG_ID_SYN_SUP) ] Jan 16 18:08:38 uvm1804 charon: 09[NET] sending packet: from 1.1.1.1[4500] to 3.3.3.2[4500] (272 bytes) Jan 16 18:08:38 uvm1804 charon: 10[NET] received packet: from 3.3.3.2[4500] to 1.1.1.1[4500] (160 bytes) Jan 16 18:08:38 uvm1804 charon: 10[ENC] parsed IKE_AUTH response 1 [ V IDr AUTH N(NO_PROP) ] Jan 16 18:08:38 uvm1804 charon: 10[IKE] authentication of '3.3.3.2' with pre-shared key successful Jan 16 18:08:38 uvm1804 charon: 10[IKE] IKE_SA tunnel[1] established between 1.1.1.1[1.1.1.1]...3.3.3.2[3.3.3.2] Jan 16 18:08:38 uvm1804 charon: 10[IKE] scheduling reauthentication in 2848s Jan 16 18:08:38 uvm1804 charon: 10[IKE] maximum IKE_SA lifetime 3388s Jan 16 18:08:38 uvm1804 charon: 10[IKE] received NO_PROPOSAL_CHOSEN notify, no CHILD_SA built Jan 16 18:08:38 uvm1804 charon: 10[IKE] failed to establish CHILD_SA, keeping IKE_SA root@uvm1804:/var/log#
You’ll notice that the last message states “failed to establish CHILD_SA” which generally means your ESP parameters don’t match, on the Cisco side you’ll need to check your transform set. If your IKE proposal doesn’t match, you’ll get a different message about no proposal being chosen. On the Cisco side you’ll need to check your IKE proposal as well.
CiscoIOSv15.6(2)T-3
You’ll notice that when the Ubuntu Strongswan router isn’t trying to bring up it’s tunnel due to an invalid configuration, the Cisco IOS device says it’s tunnel is DOWN:
Router#show crypto session Crypto session current status Interface: GigabitEthernet0/0 Session status: DOWN Peer: 1.1.1.1 port 500 IPSEC FLOW: permit ip 172.16.0.0/255.255.255.0 192.168.0.0/255.255.255.0 Active SAs: 0, origin: crypto map Router#
When parameters mismatch, you’ll see an UP-IDLE session status:
Router#show crypto session Crypto session current status Interface: GigabitEthernet0/0 Profile: james-profile Session status: UP-IDLE Peer: 1.1.1.1 port 4500 Session ID: 101 IKEv2 SA: local 3.3.3.2/4500 remote 1.1.1.1/4500 Active Router#
IPsec is a compex protocol, but when it works it’s pretty cool. Shoot me a message or post a comment if you’re having trouble with a VPN tunnel, I’m always happy to help if I can.
I have two Lanner IP-Encryptor (FW-7525) devices with Installed Ubuntu 18.04 servers in both of them. Then I install and build StrongSwan in both of them.
I connected one Lanner Device to one LAN connection and other with other LAN Connection. And see their outputs through console port on TeraTerm. How do I connect both Devices to create a secure VPN Tunnel.
Kindly mail me your suggestion.
If I understand your topology correctly – You have two firewalls and Ubuntu servers connected to the LAN side interfaces of those. You are trying to use Strongswan on the Ubuntu servers to build a tunnel. If that’s the case – I haven’t created a lab with that specific topology but it wouldn’t be hard to do. I think you can port-forward UDP ports 500 and 4500 and successfully build a tunnel without assigning publicly routable IP addresses to your Ubuntu servers. I’ll see if I can give it a try
Hi James,
First of all I love your blog and learned a lot.
I don’t see any example where NAT involved, I am working on a project where Linux Server on one of and Cisco router on the other end behind Firewall (Where One to One Static NAT configured).
I am running in to an issue where I do decaps but not encaps on the Cisco.
Please give me an insight what’s needed on Linux server where another end is behind NAT, I am familiar much with Cisco 🙂
I don’t think there’s actually much involved if your Strongswan server is the one with the public IP and the other end is a one-to-one NAT. Just use the public IP that the Cisco device will be NAT’ed to and you should be good to go, there may be a setting to enable for NAT-T on your Strongswan server to check though. I’ll have to do a post on this 🙂 Thanks for your question!