Azure Routed VPN with StrongSwan on Linux

During these holidays I’ve spent some time working on setting up a VPN between my on-premises network and an Azure VNet. In order to setup the connectivity I have used StrongSwan on Linux at the on-premises side and a VpnGw1 VPN Gateway in Routed/Dynamic mode on the Azure side.

Scenario

Vnet GW Address 1.2.3.4
Vnet GW type Routing / Dynamic
Vnet IP Address Space 10.11.0.0./16 and 10.12.0.0/16

Note: The above addresses are not subnets from the same VNet, but separate address spaces.

Linux Distro Ubuntu 16.04 LTS
StrongSwan IP 4.3.2.1
StrongSwan IP Addres Space 100.64.0.0/24
StrongSwan version (“ipsec version”) Linux strongSwan U5.3.5/K4.4.0-22-generic

Note: The Ubuntu 16.04 LTS on-premises machine was acting as a router between the VPN tunnel and my on-premises network (100.64.0.0/24).

Setup Azure

Create a new local network gateway

Choose your desired name, IP address 4.3.2.1 (on-premises public IP) and IP Address Space 100.64.0.0/24 (on-premises network).

Screenshot_1

Create a Virtual Network Gateway on your Virtual Network.

  • Gateway type: VPN
  • VPN type: Route-based
  • SKU: VpnGw1 (or any other VpnGwX if you need more throughput)
  • Virtual Network: Select the Virtual Network you’d like to connect to.
  • Gateway subnet address range: If none available, first reserve part of your Virtual Network’s address space for a Gateway Subnet. More information about planning for a Virtual Network Gateway deployment can be found here.

Screenshot_2

Create the connection object tying both gateways from above

In the screenshot below I have chosen sharedsecret as our Shared Key or PSK.

Screenshot_3

Setup on-premises StrongSwan

We need to modify a few configuration files in order to get our StrongSwan up and running against our Azure Virtual Network Gateway.

/etc/ipsec.conf

# ipsec.conf - strongSwan IPsec configuration file
config setup
conn azure
        leftupdown=/usr/local/sbin/ipsec-notify.sh # Script to create a VTI and configure the necessary routing when doing "ipsec up azure" (and remove changes when doing "ipsec down azure"
        authby=secret
        type=tunnel
        left=4.3.2.1 # My Public IP address
        leftsubnet=100.64.0.0/24 # My IP address space / protected network(s)
        right=1.2.3.4 #Azure Dynamic Gateway
        rightsubnet=10.11.0.0/16,10.12.0.0/16 #Azure Vnet prefixes
        auto=route
        keyexchange=ikev2 # Mandatory for Dynamic / Route-based gateway

/etc/ipsec.secrets

# This file holds shared secrets or RSA private keys for authentication.
 
# In this case, we use a PSK.
4.3.2.1 1.2.3.4 : PSK 'sharedsecret'

In the example above, sharedsecret is your actual PSK or Shared Key.

In /etc/strongswan.d/charon.conf, uncomment and modify this line to leave it as below to avoid routing issues:

install_routes = no

Important: Without this change, StrongSwan will add routes on a routing table with more priority than the default one. That table doesn’t show up in a “ip route list“, but you can pull it with “ip route show table 220“. If you have skipped this step and are not reading this note, you’re now probably hitting your head on a wall as everything looks fine, but your VPN still doesn’t work.

/usr/loca/sbin/ipsec-notify.sh

#!/bin/bash
set -o nounset
set -o errexit

VTI_IF="vti${PLUTO_UNIQUEID}"

case "${PLUTO_VERB}" in
    up-client)
        ip tunnel add "${VTI_IF}" local "${PLUTO_ME}" remote "${PLUTO_PEER}" mode vti \
            okey "${PLUTO_MARK_OUT%%/*}" ikey "${PLUTO_MARK_IN%%/*}"
        ip link set "${VTI_IF}" up
        ip route add 10.12.0.0/16 dev "${VTI_IF}"
        ip route add 10.11.0.0/16 dev "${VTI_IF}"
        sysctl -w "net.ipv4.conf.${VTI_IF}.disable_policy=1"
        ;;
    down-client)
        ip tunnel del "${VTI_IF}"
        ;;
esac

Make sure it can be executed by the user ‘strongswan’, e.g.:

# chown strongswan:strongswan /usr/local/sbin/ipsec-notify.sh
# chmod 755 /usr/local/sbin/ipsec-notify.sh

The script above creates a tunnel interface, sets the link to up and creates two routes to be able to reach the Azure Virtual Network prefixes through said tunnel interface.

At this stage I was having some problems that took a while to fix. Doing some online reading (don’t we all fix everything that way?) I found this link: https://bugs.launchpad.net/ubuntu/+source/strongswan/+bug/1549436 where basically says apparmor has to be disabled for Charon and Stroke, as follows:

# apparmor_parser -R /etc/apparmor.d/usr.lib.ipsec.charon
# apparmor_parser -R /etc/apparmor.d/usr.lib.ipsec.stroke
# ln -s /etc/apparmor.d/usr.lib.ipsec.charon /etc/apparmor.d/disable/
# ln -s /etc/apparmor.d/usr.lib.ipsec.stroke /etc/apparmor.d/disable/

Please make sure you understand what the above implies. Disabling security features might carry a risk. In my case that risk was minimal and worth taking, but your situation might be different.

Bring the tunnel up and rock on!

Restart the ipsec daemon:

# ipsec restart

Stopping strongSwan IPsec...
Starting strongSwan 5.3.5 IPsec [starter]...

Now launch the Azure connection

# ipsec up azure

initiating IKE_SA azure[10] to 1.2.3.4
generating IKE_SA_INIT request 0 [ SA KE No N(NATD_S_IP) N(NATD_D_IP) N(HASH_ALG) ]
sending packet: from 4.3.2.1[500] to 1.2.3.4[500] (1124 bytes)
received packet: from 1.2.3.4[500] to 4.3.2.1[500] (38 bytes)
parsed IKE_SA_INIT response 0 [ N(INVAL_KE) ]
peer didn't accept DH group MODP_2048, it requested MODP_1024
initiating IKE_SA azure[10] to 1.2.3.4
generating IKE_SA_INIT request 0 [ SA KE No N(NATD_S_IP) N(NATD_D_IP) N(HASH_ALG) ]
sending packet: from 4.3.2.1[500] to 1.2.3.4[500] (996 bytes)
received packet: from 1.2.3.4[500] to 4.3.2.1[500] (360 bytes)
parsed IKE_SA_INIT response 0 [ SA KE No N(NATD_S_IP) N(NATD_D_IP) V V ]
authentication of '4.3.2.1' (myself) with pre-shared key
establishing CHILD_SA azure
generating IKE_AUTH request 1 [ IDi N(INIT_CONTACT) IDr AUTH SA TSi TSr N(MOBIKE_SUP) N(ADD_4_ADDR) N(ADD_4_ADDR) N(ADD_4_ADDR) N(EAP_ONLY) ]
sending packet: from 4.3.2.1[4500] to 1.2.3.4[4500] (412 bytes)
received packet: from 1.2.3.4[4500] to 4.3.2.1[4500] (140 bytes)
parsed INFORMATIONAL request 0 [ N(NO_ADD_ADDR) N(COOKIE2) ]
generating INFORMATIONAL response 0 [ N(COOKIE2) ]
sending packet: from 4.3.2.1[4500] to 1.2.3.4[4500] (132 bytes)
received packet: from 1.2.3.4[4500] to 4.3.2.1[4500] (212 bytes)
parsed IKE_AUTH response 1 [ IDr AUTH N(MOBIKE_SUP) SA TSi TSr ]
authentication of '1.2.3.4' with pre-shared key successful
IKE_SA azure[10] established between 4.3.2.1[4.3.2.1]...1.2.3.4[1.2.3.4]
scheduling reauthentication in 9941s
maximum IKE_SA lifetime 10481s
connection 'azure' established successfully

You can check the status of your azure connection as follows:

# ipsec status azure

Routed Connections:
 azure{1}: ROUTED, TUNNEL, reqid 1
 azure{1}: 100.64.0.0/24 === 10.11.0.0/16 10.12.0.0/16
Security Associations (1 up, 0 connecting):
 azure[10]: ESTABLISHED 3 minutes ago, 4.3.2.1[4.3.2.1]...1.2.3.4[1.2.3.4]
 azure{12}: INSTALLED, TUNNEL, reqid 1, ESP SPIs: c58685fa_i 0fb5129e_o
 azure{12}: 100.64.0.0/24 === 10.11.0.0/16 10.12.0.0/16

Important: If you don’t see any ESP SPIs above, the tunnel is not up.

As an example, after some inactivity time the SPIs might be killed and the output would be like this:

# ipsec status azure

Routed Connections:
 azure{1}: ROUTED, TUNNEL, reqid 1
 azure{1}: 100.64.0.0/24 === 10.11.0.0/16 10.12.0.0/16
Security Associations (1 up, 0 connecting):
 azure[10]: ESTABLISHED 7 minutes ago, 4.3.2.1[4.3.2.1]...1.2.3.4[1.2.3.4]

As a last check, your routes from /usr/local/sbin/ipsec-notify.sh should have been added to the main routing table:

# ip route list

10.11.0.0/16 dev vti10  scope link
10.12.0.0/16 dev vti10  scope link

And that’s it. Now any on-premises machine in the 100.64.0.0/24 should be able to use this Ubuntu box as a VPN gateway to privately reach our resources in the connected Azure VNet.

Let me know in the comments section if you had any problems following the above steps.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s