Simulating VPN sites in Azure with Ubuntu 24.04 and StrongSwan

Disclaimer: this post is going to be quite geeky. So this is not the kind of post you want to read if you don’t need this stuff. But hey, I needed to tell someone after getting this to work, plus this might be useful for somebody else, since I struggled to find these details out there.

TL;DR: you can find a script to deploy and configure an Ubuntu 24.04 VM in Azure with StrongSwan and BIRD to create IPsec+BGP tunnels to Azure Virtual WAN or an Azure Virtual Network Gateway here.

Many moons ago I started simulating onprem VPN sites with StrongSwan (open source software to create IPsec tunnels) running on Ubuntu 18.04 (I guess I am showing my age here). Fast forward some (a lot of) time, and now Ubuntu 18.04 is end-of-everything. Time to upgrade!

But back to our story: back then I used VTI interfaces in Ubuntu 18.04 (see here for the detailed commands for Ubuntu 18.04 if you happen to be interested), but I was reading that XFRM interfaces are now the new cool thing, so why not. According to StrongSwan documentation for routed VPNs (a VPN is not a VPN if it doesn’t run BGP on top) the latest versions of StrongSwan come with some goodness for XFRM from 5.9.10 on, and since Ubuntu 22.04 comes with 5.9.5 I went straight to Ubuntu 24.04 with StrongSwan 5.9.13.

The first thing that I painfully learnt is that my old way of configuring StrongSwan with /etc/ipsec.conf was not cool any more and it wouldn’t work correctly with XFRM interfaces. By the way, it kind of worked, but then it didn’t. My sincere gratitude to Tobias Brunner from the StrongSwan team for helping me out, you saved my week!

Where was I? Ah yes, time to move on from ipsec.conf. You still need to leave the now empty /etc/ipsec.conf and /etc/ipsec.secrets files there, but now you need to install an additional tool in your system: strongswan-swanctl (not surprisingly, with apt install -y strongswan-swanctl). Now you can port your connection configuration from the old files (/etc/ipsec.conf and /etc/ipsec.secrets) to the new /etc/swanctl/swanctl.conf. Don’t miss the migration document, it is excellent. I struggled a bit with the naming convention for the secrets, but eventually I got it right (see my GitHub repo for the exact contents of the swanctl.conf file).

And I have to say, swanctl is a nice tool: for example, you can manually start and stop connections, and it will show you the corresponding debug. If you use a naming convention similar to the one I have here, you can start the connection and show the logs like this:

root@onpremnva2:~# swanctl --terminate --ike vng0 --child s2s0
[IKE] closing CHILD_SA s2s0{109} with SPIs c09f3c57_i (3010 bytes) ec19181c_o (3029 bytes) and TS 0.0.0.0/0 === 0.0.0.0/0
[IKE] sending DELETE for ESP CHILD_SA with SPI c09f3c57
[ENC] generating INFORMATIONAL request 8 [ D ]
[NET] sending packet: from 10.13.77.5[4500] to 172.177.82.10[4500] (76 bytes)
[NET] received packet: from 172.177.82.10[4500] to 10.13.77.5[4500] (76 bytes)
[ENC] parsed INFORMATIONAL response 8 [ D ]
[IKE] received DELETE for ESP CHILD_SA with SPI ec19181c
[IKE] CHILD_SA closed
terminate completed successfully

root@onpremnva2:~# swanctl --initiate --ike vng0 --child s2s0
[IKE] establishing CHILD_SA s2s0{112}
[ENC] generating CREATE_CHILD_SA request 9 [ SA No TSi TSr ]
[NET] sending packet: from 10.13.77.5[4500] to 172.177.82.10[4500] (348 bytes)
[NET] received packet: from 172.177.82.10[4500] to 10.13.77.5[4500] (204 bytes)
[ENC] parsed CREATE_CHILD_SA response 9 [ SA No TSi TSr ]
[CFG] selected proposal: ESP:AES_GCM_16_256/NO_EXT_SEQ
[IKE] CHILD_SA s2s0{112} established with SPIs c862f5e5_i 20d6396a_o and TS 0.0.0.0/0 === 0.0.0.0/0
initiate completed successfully

Very useful to troubleshoot pre-shared key mismatches or unaligned IKEv2/ESP proposals.

Routing was a bit tricky, especially since StrongSwan uses a custom route table 220, so you need to make sure that you avoid routing loops (I went for the solution of the lazy man, throw statements).

After getting that right, BIRD picked up the tunnels immediately without changing a single comma in the config (you can see my BIRD config file here as well). And yes, you can leave your BIRD kernel table to the default, no need to point it at 220. Both BGP adjacencies up, and routes get installed correctly from both gateway instances for Equal-Cost Multi-Path:

root@onpremnva2:~# birdc show prot
BIRD 1.6.8 ready.
name     proto    table    state  since       info
device1  Device   master   up     2024-07-03
direct1  Direct   master   down   2024-07-03
kernel1  Kernel   master   up     2024-07-03
static1  Static   master   up     2024-07-03
vpngw0   BGP      master   up     2024-07-03  Established
vpngw1   BGP      master   up     2024-07-03  Established

root@onpremnva2:~# ip route
default via 10.13.77.1 dev eth0 proto dhcp src 10.13.77.5 metric 100
10.13.75.0/24 proto bird
        nexthop via 10.13.76.36 dev ipsec1 weight 1
        nexthop via 10.13.76.37 dev ipsec0 weight 1
10.13.76.0/24 proto bird
        nexthop via 10.13.76.36 dev ipsec1 weight 1
        nexthop via 10.13.76.37 dev ipsec0 weight 1
10.13.76.36 dev ipsec1 scope link
10.13.76.37 dev ipsec0 scope link
10.13.77.0/28 via 10.13.77.1 dev eth0 proto bird
10.13.77.0/28 dev eth0 proto kernel scope link src 10.13.77.5 metric 100
10.13.77.1 dev eth0 proto dhcp scope link src 10.13.77.5 metric 100
168.63.129.16 via 10.13.77.1 dev eth0 proto dhcp src 10.13.77.5 metric 100
169.254.169.254 via 10.13.77.1 dev eth0 proto dhcp src 10.13.77.5 metric 100
172.177.82.10 via 10.13.77.1 dev eth0
172.200.25.238 via 10.13.77.1 dev eth0

I know there is a lot of room for improvement in my config, like for example I should do the commands boot persistent (any hints would be really appreciated). But hey, this was my success of the week, so sharing it with y’all!

6 thoughts on “Simulating VPN sites in Azure with Ubuntu 24.04 and StrongSwan

  1. Mauricio Rojas's avatarMauricio Rojas

    I’ve been playing around with this. Very handy as a reference. Brilliant as always! Specially since Cisco license are through the roof

    Like

  2. Love this Jose! I had tried getting this working a few years back but gave up. I really appreciate you braving the struggle!

    Like

  3. Thank you for this excellent post! Yesterday, I successfully configured an Azure Site-to-Site VPN Gateway with StrongSwan on Ubuntu 24.04 using ipsec.conf. It worked perfectly for my scenario, which involved connecting via SSH to an Azure VM using its private IP (without a public IP). I really appreciate the insights and the updated approach you’ve shared here—very informative and helpful as always!

    Like

    1. Happy it helped Giulio!

      Liked by 1 person

Leave a comment