Peer to Peer IPSEC VPN with StrongSwan

This will walk you through setting up an Ipsec VPN between 2 networks using 2 hosts using strongswan to build the tunnel.

This was my go to solution to connect Amazon AWS VPCs across regions… that is until AWS allowed peering VPCs across regions in December of 2018. So connecting VPCs using peering is preferred over having 2 machines (1 per VPC) running 24×7 to maintain the connections.

The thing to remember about ipsec is that you should not overlap network subnets across networks. It is ideal to have a unique CIDR for each location that will be used in the peer to peer VPN. There are methods to handle overlapping subnets, but that is another post.

In this example, we have 2 networks.
In Virginia Network 1 is 10.32.252.0/24
In Ohio Network 2 is 10.33.252.0/24

We want to bridge the 2 networks together.strongswan example

We need to create a host in each network. The host must have a private IP and a Public IP. You may use a physical host with 2 interfaces or even a host with a single internal IP that has a one to one NAT to a public IP with all protocols forwarded. Either way is fine for this solution.

On network 1, let us create a host as 10.32.252.150 as private and 1.1.1.1 as our public ip.
On network 2, let us create a host as 10.33.252.150 as private and 2.2.2.2 as our public ip.

On each host, we want to install strongswan

apt-get install strongswan

Edit the config files for each host as follows:

VA Side (private 10.32.252.150, public 1.1.1.1):
####################################
# /etc/ipsec.conf
#
config setupstrongswan example
conn %default
ikelifetime=60m
keylife=20m
rekeymargin=3m
keyingtries=1
authby=secret
keyexchange=ikev2
mobike=no

conn net-net
left=10.32.252.150
leftsubnet=10.32.0.0/16
leftid=@va
leftfirewall=yes
right=2.2.2.2
rightsubnet=10.33.0.0/16
rightid=@oh
auto=route
####################################
# /etc/ipsec.secrets
# 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.
@va @oh : PSK "secret"

Then on the other side:

OH Side (private 10.33.252.150, public 2.2.2.2):
#############################################
# /etc/ipsec.conf

config setup

conn %default
ikelifetime=60m
keylife=20m
rekeymargin=3m
keyingtries=1
authby=secret
keyexchange=ikev2
mobike=no

conn net-net
left=10.33.252.150
leftsubnet=10.33.0.0/16
leftid=@oh
leftfirewall=yes
right=1.1.1.1
rightsubnet=10.32.0.0/16
rightid=@va
auto=route
###############################################
# /etc/ipsec.secrets

# 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.

@oh @va : PSK "secret"

This is added to both hosts:

#############################################
# both hosts:
#####################################
# /etc/sysctl.conf
# Add the following:

net.ipv4.ip_forward=1
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0

After we edit sysctl we need to reload for the changes:

######################################
# Reload sysctl
$ sysctl -p

Now that AWS offers cross region VPC peering, the need to dedicated VPNs isn’t as important. But if you need to use this for AWS hosts we need to disable host check on the EC2 instance.

In AWS, the traffic that is destined for the far end of the VPN tunnel must be routed to the IP of the local VPN endpoint. You do this using a route table assigned to the subnet. Once the host check is disabled on the EC2 instance, the IP becomes eligible for a routing target. Create the route entry in the route table assigned to the subnet.

To Test the VPN, use the following commands.

########################
# Testing
ipsec up net-net
ipsec statusall

StrongSwan Ipsec VPN for Remote Users with Certificate Based Authentication

This is a working strongswan ipsec config that can be used for a roadwarrior setup for remote users utilizing certificate based authentication instead of id/pw.

This is a pure IPSEC with ESP setup, not L2tp.
This is not 2 factor, it is cert only.

To get started:

sudo apt-get install strongswan

You need is a CA that is capable of registering AltNames in a cert. OpenSSL can do this easily. I used this guide to setup the basic openssl CA. http://www.freebsdmadeeasy.com/tutorials/freebsd/create-a-ca-with-openssl.php

Once the CA is ready and you have generated your ca cert and ca private key, you next need to create a cert for the ipsec host and a cert for the end user.

For the Server:
Since I need the Alt Names in the certs, make a copy of /etc/ssl/openssl.cnf to be used for the Server.

cp /etc/ssl/openssl.cnf /etc/ssl/openssl-for-server.cnf
# Extension copying option: use with caution. copy_extensions = copy [ v3_req ] # Extensions to add to a certificate request basicConstraints = CA:FALSE keyUsage = nonRepudiation, digitalSignature, keyEncipherment subjectAltName=@alt_names [alt_names] DNS.1 = ipsecvpn.mydomain.com DNS.2 = another_name.mydomain.com

Now, using Openssl, create the request for the server, fill in the details of the req as needed, then sign the request.

openssl req -new -nodes -out ipsechost-req.pem -keyout private/ipsechost-key.pem -config /etc/ssl/openssl-for-server.cnf
openssl ca -config /etc/ssl/openssl-for-server.cnf -out ipsechost-cert.pem -in ipsechost-req.pem

Copy the certs to the correct locations for strongswan to use.

cp cacert.pem /etc/ipsec.d/cacerts
cp ipsechost-cert.pem /etc/ipsec.d/certs
cp private/ipsechost-key.pem /etc/ipsec.d/private/

Stongswan is configured using the /etc/ipsec.conf and /etc/ipsec.secrets files.
This is a very simple config that will work for providing access to remote users:
Edit /etc/ipsec.conf

 
# /etc/ipsec.conf - strongSwan IPsec configuration file
config setup

conn %default
        ikelifetime=60m
        keylife=20m
        rekeymargin=3m
        keyingtries=1

conn common
        left=IP_OF_IPSEC_HOST          # Ip of the host
        leftcert=ipsechost-cert.pem    # the cert we just created and copied
        leftid=@ipsecvpn.mydomain.com  # the Alt name in the Cert we just created
        leftsubnet=172.16.31.0/24      # The internal subnet the remote user wants to access
        right=%any                     # Connections can come from anywhere
        rightsourceip=192.168.1.0/24   # Use this pool of IPs to assign to these inbound connections
        auto=add

conn ikev2
        keyexchange=ikev2 
        also=common

Edit the /etc/ipsec.secrets file

: RSA ipsechost-key.pem

Restart/Reload IPsec.

 
ipsec restart

I like to watch logs just to be sure there are no errors:

 
tail -f /var/log/syslog /var/log/auth.log

Next we create a client cert. We need another copy of the openssl config file for user requests since the Alt Name changes from DNS to Email.

cp /etc/ssl/openssl-for-server.cnf /etc/ssl/openssl-for-users.cnf
[ v3_req ] # Extensions to add to a certificate request basicConstraints = CA:FALSE keyUsage = nonRepudiation, digitalSignature, keyEncipherment subjectAltName=email:copy #[alt_names] #DNS.1 = ipsecvpn.mydomain.com #DNS.2 = another_name.mydomain.com

Create the request, fill in the details as needed for the user especially the email address, and Sign the request. The email address specified in the request prompts will be used in the cert for the Alt name and in the config for the user’s side of the tunnel.

openssl req -new -nodes -out user1-req.pem -keyout private/user1-key.pem -config /etc/ssl/openssl.cnf 
openssl ca -config /etc/ssl/openssl-for-users.cnf -out user1-cert.pem -in user1-req.pem 

You need to copy the user1-cert.pem, user1-key.pem, and the cacert.pem to the user’s machine. We will need these file to complete the VPN.

On the User’s Side:

sudo apt-get install strongswan

Copy the files into the proper directories
user1-cert.pem to /etc/ipsec.d/certs
user1-key.pem to /etc/ipsec.d/private
cacert.pem to /etc/ipsec.d/cacerts

Edit the client side ipsec.conf. This is a working config:

conn %default
     ikelifetime=60m
     keylife=20m
     rekeymargin=3m
     keyingtries=1
     keyexchange=ikev2

conn roadwarrior
     leftsourceip=%config                # This will take an IP from the ip pool on server
     leftcert=ipsecuser1-cert.pem        # The user cert we copied in
     leftid=ipsecuser1@openpeak.org      # This is the email included in the Alt Name in the user cert
     leftfirewall=yes
     right=cert-ipsec-vpn.openpeak.org   # The location of the host, FQDN or IP 
     rightid=cert-ipsec-vpn.openpeak.org # the Altname used by the ipsec host
     rightsubnet=172.16.31.0/24          # the subnet on the servers side you want to access. 
     auto=start

Edit the ipsec.secrets file

: RSA ipsecuser1-key.pem

On the client, issue an “ipsec restart” and it should attempt to build the tunnel with that you are done.

Troubleshooting:
Use “ipsec statusall” to get details on the tunnels. From the server, a healthy tunnel looks like this:

Security Associations (1 up, 0 connecting):
       ikev2[11]: ESTABLISHED 3 minutes ago, HOST_IP[vpnhost.mydomain.com]...REMOTE_IP[ipsecuser1@mydomain.com]
       ikev2[11]: IKEv2 SPIs: 49c4512b56436e5b_i 6276554588ce1803_r*, public key reauthentication in 50 minutes
       ikev2[11]: IKE proposal: AES_CBC_128/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_2048
       ikev2{9}:  INSTALLED, TUNNEL, ESP in UDP SPIs: cd1e015f_i cd3cb1c1_o
       ikev2{9}:  AES_CBC_128/HMAC_SHA1_96, 0 bytes_i, 0 bytes_o, rekeying in 12 minutes
       ikev2{9}:   HOST_INTERNAL_SUBNET=== POOL_IP_ASSIGNED_TO_REMOTEUSER

Use “ipsec listall” for details on the host’s certs and configs. Here we want to be sure Alt Names are good, and the CA and certs are loaded correctly.

List of X.509 End Entity Certificates:
  altNames:  ipsecvpn.mydomain.com
  subject:  "C=US, ST=NY, O=OpenPeak, OU=IT, CN=ipsecvpn.mydomain.com, E=ipsechost@mydomain.com"
  issuer:   "C=US, ST=NY, L=NY, O=mydomain, OU=IT, CN=ipsecserver-ca, E=ca@mydomain.com"
  serial:    10:03
  validity:  not before Mar 18 20:44:25 2015, ok
             not after  Nov 24 20:44:25 2028, ok 
  pubkey:    RSA 2048 bits, has private key
  keyid:     10:15:77:ae:2e:a4:e8:3f:cc:1f:6d:a9:d9:80:bd:9f:41:fb:63:e5
  subjkey:   3f:0c:bf:01:2f:c7:16:be:d4:83:5c:76:81:56:a9:f1:3a:84:b4:5f
  authkey:   b7:61:d7:32:19:65:c3:10:1a:43:23:27:bc:46:29:e5:ff:df:03:1c

List of X.509 CA Certificates:

  subject:  "C=US, ST=NY, L=NY, O=mydomain, OU=IT, CN=ipsecserver-ca, E=ca@mydomain.com"
  issuer:   "C=US, ST=NY, L=NY, O=mydomain, OU=IT, CN=ipsecserver-ca, E=ca@mydomain.com"
  serial:    db:e9:16:e0:44:a3:57:83
  validity:  not before Mar 18 15:49:45 2015, ok
             not after  Mar 15 15:49:45 2025, ok 
  pubkey:    RSA 2048 bits
  keyid:     18:47:07:92:b8:3d:a0:bb:88:bf:27:2b:4d:0b:a7:79:8b:c1:1b:ba
  subjkey:   b7:61:d7:32:19:65:c3:10:1a:43:23:27:bc:46:29:e5:ff:df:03:1c
  authkey:   b7:61:d7:32:19:65:c3:10:1a:43:23:27:bc:46:29:e5:ff:df:03:1c

Note that if you want to enable 2 factor with this, change the openssl request for the Clients to omit the -nodes option. This will prompt you for a password during the certificate creation that must be entered every time the client wants to connect.