-
How to setup an IPSec tunnel with Strongswan with high-availability on Linux
It is possible to secure your communication between several sites (datacenters for example) by using an open-source VPN IPSec on your Linux System. We will see here how to:
- Set-up a VPN IPSec on Linux with Strongswan (https://www.strongswan.org)
- Set-up a high availability mechanism on top of this VPN connection to ensure the link will always be up with KeepAlived (http://www.keepalived.org/)
Architecture
Here is the architecture example I will use in this post.
- – – – represents a local link
- === represents a VPN link
192.168.50.0/24 - - - 192.168.100.1 === 192.168.100.2 - - - 10.0.0.0/16
VPN Installation
First of all, install the package strongswan using the package manager you used to, or by compiling it from sources. In the same time, install the keepalived package to be able to set it highly available at the end of this post.
$apt-get install strongswan keepalived
This will install the packages and the libraries needed to make them work.
Certificates
First thing to do will be to generate certificates used for the encryption of communication within the VPN. This can be done in two ways:
- Using CLI with StrongSwan’s PKI Tool (https://wiki.strongswan.org/projects/strongswan/wiki/IpsecPKI)
- Using a GUI tool to manage certificates like XCA (http://xca.sourceforge.net/)
CA Certificate
Once the packages are properly installed, we will have to create the different certificates that we will use to encrypt our connection between peers. For the example we will be using self-signed certificate here. The first certificate to generate wil be the CA certificate with which one we will sign any certificate we want to use in the VPN network.
Generate a 2048 bit RSA private key (caKey.der) for the CA certificate (caCert.der) and self-sign it with this key:
$ipsec pki --gen > caKey.der $ipsec pki --self --in caKey.der --dn "C=FR, O=myCompany, CN=myCompany CA" --ca > caCert.der
End Entity Certificate
For each peer (i.e. each gateway), a private key (peerKey.der) and a certificate (peerCert.der) will have to be generated using the CA previously created:
$ipsec pki --gen > peerKey.der $ipsec pki --pub --in peerKey.der | ipsec pki --issue --cacert caCert.der --cakey caKey.der --dn "C=FR, O=myCompany, CN=vpn-peer1" > peerCert.der
Install certificates
On each peer, store the following certficates and private keys in /etc/ipsec.d/ subdirectory as:
- /etc/ipsec.d/private/peerKey.der for the private key of the peer
- /etc/ipsec.d/certs/peerCert.der for the certificate of the peer
- /etc/ipsec.d/cacerts/caCert.der for the CA certificate that signed the certificates
The CA private key (caKey.der) should never be stored on a server directly reachable from the Internet and be kept safe.
IPSec configuration
To configure IPSec, you will have to configure two files:
- /etc/ipsec.conf for the configuration of your tunnels
- /etc/ipsec.secrets for the configuration of your keys and/or PSK (pre-shared keys)
If you use certificate for your connection, here is what your configuration should look like:
#/etc/ipsec.conf # ipsec.conf - strongSwan IPsec configuration file config setup conn %default ikelifetime=60m keylife=20m rekeymargin=3m keyingtries=1 keyexchange=ikev2 mobike=no conn peer1-peer2 left=192.168.100.1 leftcert=peerCert.der leftid="C=FR O=myOrganisation, CN=vpn-peer1" leftsubnet=192.168.50.0/24 leftfirewall=yes right=192.168.100.2 rightid="C=FR, O=myOrganisation, CN=vpn-peer2" rightsubnet=10.0.0.0/16 auto=start closeaction=restart
#/etc/ipsec.secrets # This file holds shared secrets or RSA private keys for authentication. : RSA peerKey.der
If instead of using certificates you prefered to use Pre-Shared Key (as you will have to if you want to connect to AWS VPN Services), here are how should be configured both files:
#/etc/ipsec.conf # ipsec.conf - strongSwan IPsec configuration file config setup conn %default ikelifetime=60m keylife=20m rekeymargin=3m keyingtries=1 authby=secret keyexchange=ikev2 mobike=no conn peer1-peer2 left=192.168.100.1 leftid=@vpn-peer-1 leftsubnet=192.168.50.0/24 leftfirewall=yes right=192.168.100.2 rightsubnet=10.0.0.0/16 auto=start
#/etc/ipsec.secrets # This file holds shared secrets or RSA private keys for authentication. @vpn-peer-1 @vpn-peer-2 : PSK "poiuYTREzaQSdfGhJKlmNbvCxw"
IPsec commands and monitoring
First of all, each time you are changing part of the configuration, it’s strongly advised to reload configuration by doing:
$ipsec restart
Once this done, you can easily up/down a configuration by using:
$ipsec up peer1-peer2 $ipsec down peer1-peer2
A last command very useful is “statusall” that allows you to check and monitor VPN links:
$ipsec statusall Status of IKE charon daemon (strongSwan 5.1.2, Linux 3.13.0-45-generic, x86_64): uptime: 7 hours, since Feb 15 23:25:20 2015 malloc: sbrk 1486848, mmap 0, used 407360, free 1079488 worker threads: 11 of 16 idle, 5/0/0/0 working, job queue: 0/0/0/0, scheduled: 6 loaded plugins: charon test-vectors aes rc2 sha1 sha2 md4 md5 random nonce x509 revocation constraints pkcs1 pkcs7 pkcs8 pkcs12 pem openssl xcbc cmac hmac ctr ccm gcm attr kernel-netlink resolve socket-default stroke updown eap-identity addrblock Listening IP addresses: 192.168.100.1 Connections: peer1-peer2: 192.168.100.1...192.168.100.2 IKEv2 peer1-peer2: local: [C=FR, O=myOrganisation, CN=vpn-peer1] uses public key authentication peer1-peer2: cert: "C=FR, O=myOrganisation, CN=vpn-peer1" peer1-peer2: remote: [C=FR, O=myOrganisation, CN=vpn-peer2] uses public key authentication peer1-peer2: child: 192.168.50.0/24 === 10.0.0.0/16 TUNNEL Security Associations (1 up, 0 connecting): peer1-peer2[25]: ESTABLISHED 2 minutes ago, 192.168.100.1[C=FR, O=myOrganisation, CN=vpn-peer1]...192.168.100.2[C=FR, O=myOrganisation, CN=vpn-peer2] peer1-peer2[25]: IKEv2 SPIs: 9f7e598d22e98081_i 003d60cae30e9548_r*, public key reauthentication in 52 minutes peer1-peer2[25]: IKE proposal: AES_CBC_128/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_2048 peer1-peer2{23}: INSTALLED, TUNNEL, ESP SPIs: cc8513e9_i c729b4f7_o peer1-peer2{23}: AES_CBC_128/HMAC_SHA1_96, 0 bytes_i, 0 bytes_o, rekeying in 12 minutes peer1-peer2{23}: 192.168.50.0/24 === 10.0.0.0/16
High availability configuration
So that your VPN can be highly available, you will need to configure keepalived that you just installed at the beginning. You will have to configure it on both sides by using a virtual IP and a script to automate the restart on both nodes depending on the state of the cluster (option “notify”).
Here is the configuration for the master server:
! Configuration File for keepalived vrrp_instance VI_1 { state MASTER interface eth0 virtual_router_id 51 priority 150 advert_int 1 authentication { auth_type PASS auth_pass $ place secure password here. } virtual_ipaddress { 192.168.100.1 } notify /opt/notifyipsec.sh }
The configuration for the slave (backup) server is almost similar but state and priority are changing:
! Configuration File for keepalived vrrp_instance VI_1 { state BACKUP interface eth0 virtual_router_id 51 priority 100 advert_int 1 authentication { auth_type PASS auth_pass $ place secure password here. } virtual_ipaddress { 192.168.100.1 } notify /opt/notifyipsec.sh }
And here is the script notifyipsec.sh for the “notify” option:
#!/bin/bash TYPE=$1 NAME=$2 STATE=$3 case $STATE in "MASTER") ipsec restart exit 0 ;; "BACKUP") ipsec stop exit 0 ;; "FAULT") ipsec stop exit 0 ;; *) echo "unknown state" exit 1 ;; esac
Now you can restart both services and your IPSec VPN inter-site is ready with a high-availability mechanism enabled!!
Obviously, you will have to do the same on the other node if you want your VPN to work properly, by reversing configuration.
-
How to capture results of a watch in a file
For debugging purposes, it can sometimes be useful to write the results of a command executed at a given time lapse. You can easily monitor a command by using the watch command.
By default a watch command is only displaying result, and you have to be in front of to see the results. If you want to write them in a file with a timestamp, you can easily do it by using this command that combines a watch command with tee:
# watch -t -n 1 "(date '+TIME:%H:%M:%S' ; netstat -np | egrep -i *:443) | tee -a /tmp/logfilewatch"
You can now open your file /tmp/logfilewatch and see the results you just got!
For example, here is what you could get with my previous command, checking all the connections on port TCP 443:TIME:22:24:22 tcp 0 0 0.0.0.0:443 0.0.0.0:* LISTEN 1722/apache2 TIME:22:24:23 tcp 0 0 0.0.0.0:443 0.0.0.0:* LISTEN 1722/apache2 TIME:22:24:35 tcp 0 0 192.168.100.250:443 12.34.56.78:27911 TIME_WAIT - TIME:22:24:36 tcp 0 0 192.168.100.250:443 12.34.56.78:27911 TIME_WAIT - TIME:22:24:37 tcp 0 0 192.168.100.250:443 12.34.56.78:27911 TIME_WAIT - TIME:22:24:38 tcp 0 0 192.168.100.250:443 12.34.56.78:27911 TIME_WAIT - TIME:22:24:39 tcp 0 0 192.168.100.250:443 12.34.56.78:27916 SYN_RECV - tcp 0 0 192.168.100.250:443 12.34.56.78:27911 TIME_WAIT - TIME:22:24:41 tcp 0 0 192.168.100.250:443 12.34.56.78:27916 ESTABLISHED 16810/apache2 tcp 0 0 192.168.100.250:443 12.34.56.78:27911 TIME_WAIT - TIME:22:24:42 tcp 0 0 192.168.100.250:443 12.34.56.78:27916 ESTABLISHED 16810/apache2 tcp 0 0 192.168.100.250:443 12.34.56.78:27911 TIME_WAIT -
-
Find which process is using a specific port
If you need to know/find which process is using a specific port on your system, you can use some default tools for troubleshooting:
- netstat
- lsof
WARNING: For the use of these tools, you will need root rights (or sudo rights will be efficient)
First, with netstat you can use options tnlpu to display much informations on connections:
$ sudo netstat -tlnpu Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 127.0.0.1:9000 0.0.0.0:* LISTEN 5786/php-fpm: pool tcp 0 0 127.0.0.1:27017 0.0.0.0:* LISTEN 18591/mongod tcp 0 0 0.0.0.0:8080 0.0.0.0:* LISTEN 15481/python tcp 0 0 0.0.0.0:8081 0.0.0.0:* LISTEN 19238/nginx tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1105/sshd tcp 0 0 127.0.0.1:5432 0.0.0.0:* LISTEN 15098/postgres tcp6 0 0 :::22 :::* LISTEN 1105/sshd
Then, with lsof you can easily find which connections are opened on a specified port with the option -i on your command-line:
$ sudo lsof -i tcp:22 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME sshd 1054 root 3u IPv4 9672 0t0 TCP *:ssh (LISTEN) sshd 1054 root 4u IPv6 9674 0t0 TCP *:ssh (LISTEN) sshd 1146 root 3u IPv4 9794 0t0 TCP 192.168.56.101:ssh->192.168.56.1:49931 (ESTABLISHED) sshd 1150 root 3u IPv4 9876 0t0 TCP 192.168.56.101:ssh->192.168.56.1:49933 (ESTABLISHED) sshd 1254 admin 3u IPv4 9794 0t0 TCP 192.168.56.101:ssh->192.168.56.1:49931 (ESTABLISHED) sshd 1265 admin 3u IPv4 9876 0t0 TCP 192.168.56.101:ssh->192.168.56.1:49933 (ESTABLISHED)