Azure Site-2-Site VPN with Ubiquiti Edge Router running EdgeOS and Azure CLI

Since I started working for the “Cloud Giants” some 7 years ago I have been on a journey. This journey always involves changing as much technology in my house to use the given platform, and with my recent change of jobs to Microsoft, building a bridge between my house and Azure in the form of a Site-2-Site VPN is a near must.

Ubiquiti devices are very popular, especially the EdgeRouter series which I use with my layer 2 fibre GPON (Gigabit Fibre Passive Optical Network) internet connection. It provides PPoE authentication, DHCP and basic NAT and PAT functions to my home.

Having in the past few years moved away from pfSense running on a ProLiant DL360 G7 my house is now a polyglot of 1RU devices in the form of Cisco switching and Access Points and Ubiquiti covering routing. As of March 2022 (when I author this post) I could not find an up-to-date post on how to establish a Site-2-Site VPN using Edge OS on Azure. There are posts out there, but none is an end-2-end walk through and all are dated. We know how fast things move in the cloud (I hardly recognised the classic Azure portal!) A lot of users are now using Ubiquiti Unifi USG devices which make this process considerably easier, as they hide a lot of plumbing away with a pretty GUI.

So, in this post I will walk you through the process of

  • Establishing a Site-2-Site VPN (IKEv2 with Static Routes) between an Ubiquiti device running Edge OS 2.x and a Microsoft Azure vNET.
  • Demonstrate how to do this without a GUI. We will use AZ-CLI and SSH.

This post is based off details from the Ubiquiti Support Centre and Microsoft Docs website.

Requirements
– Microsoft Azure Subscription.
– Ubiquiti Edge OS 2.x device (Edge Router).
– Pre-Shared Key. I like to use openssl to do this. You will need to use this secret on both ends.

openssl rand -base64 64

Values Used In This Guide

Ubiquiti : eth0 - WAN - 119.148.99.243 
Ubiquiti : eth1 - LAN - 10.0.0.1/24
Ubiquiti : vti0 - No address
Azure Virtual Gateway IP Address -  20.211.114.231
Azure Public IP Address - 20.211.114.231
Azure vNET - 192.168.0.0/23
Azure VM Subnet - 192.168.0.0/24
Azure Gateway Subnet - 192.168.1.0/27
Azure Resource Group - Baldacchino_RG
Azure Local Gateway - Baldacchino_Local_Gateway
Azure Location - australiaeast 
Azure VPN Conenction Name - EdgeRouter10x
IPsec pre-shared-secret - Generated by OpenSSL (see below)

What We Will Build
We are building a site-2-site VPN. An encrypted tunnel between my house and Azure. Not between two devices, but between two sites, defined by CIDR ranges.

Visually speaking the diagram below should explain this better than words. This simplified diagram lacks switching, AP’s on the house side and highlights only what is required on the Azure side. In essence we are establishing an IPsec tunnel between Azure and my house. We will provide routes into Azure from my house and routes to my house from Azure by defining static routes. BGP can be used but this is an overkill for my basic scenario.

Visual representation of what we will build.

Step 1 : Install AZ-CLI
We will today be using the CLI, both within Azure and our Ubiquiti (via SSH) to dirve thee changes. The first thing we will do is install AZ CLI. It is a cross-platform CLI to connect to Azure and execute administrative commands on Azure resources. It allows the execution of commands through a terminal using interactive command-line prompts or a script. The Azure CLI, known as AZ CLI is available across Azure services and is designed to get you working quickly with Azure, with an emphasis on automation and I see it as that interim step between the console and using a direct SDK or service API.

You can install the Azure CLI locally on Linux, Mac, or Windows computers. It can also be used from a browser through the Azure Cloud Shell (very cool πŸ‘πŸ‘) or run from inside a Docker container.

I am not going to cover setting up Azure CLI, see the Azure CLI setup guide for more information on setting up and configuring for your subscription.

I am also not going to cover setting up a SSH client given both Windows 11, OSX and Linux OS’es all have native SSH capabilities.

Step 2 : Configure Azure Site-2-Site VPN Connection
There are many steps to perform within Azure. Many of these are plumbing steps, such as vNET and subnet creation. You can skip these steps if you have already completed this task.

If you don’t already have a virtual network, create one. When creating a virtual network, make sure that the address spaces you specify don’t overlap any of the address spaces that you have on your on-premises network (Rule number 1 of cloud networking, other wise you will be in double NAT hell).

az network vnet create --name Baldacchino_vNET --resource-group Baldacchino_RG--address-prefix 192.168.0.0/23 --location australiaeast --subnet-name VMSubnet --subnet-prefix 192.168.0.0/24

The virtual network gateway uses specific subnet called the gateway subnet. The gateway subnet is part of the virtual network IP address range that you specify when configuring your virtual network. It contains the IP addresses that the virtual network gateway resources and services use. The subnet must be named ‘GatewaySubnet’ (why?). You can’t specify a different subnet to deploy the gateway resources to. If you don’t have a subnet named ‘GatewaySubnet’, when you create your VPN gateway, it will fail.

az network vnet subnet create --address-prefix 192.168.1.0/27 --name GatewaySubnet --resource-group Baldacchino_RG --vnet-name Baldacchino_vNET

The local network gateway is your on-premise, my house in this scenario that Azure will connect to. You can only specify an IP address and not a FQDN (Fully Qualified Domain Name). You also specify the IP address prefixes that will be routed through the VPN gateway to the VPN device, in my case it is the 10.0.0.0/24 CIDR range.

az network local-gateway create --gateway-ip-address 119.148.99.243 --name Baldacchino_Local_Gateway --resource-group Baldacchino_RG --local-address-prefixes 10.0.0.0/24

We need to obtain a public IP address so that our EdgeRouter can communicate to. This will be then assigned to the VPN Gateway.

az network public-ip create --name Baldacchino_VPN_Public_IP --resource-group Baldacchion_RG --allocation-method Dynamic

Extract the public IP address as we will use this when configuring our Edge Router

az network public-ip list --resource-group Baldacchino_RG --output table

Create the virtual network VPN gateway. Creating a gateway can often take 45 minutes or more, depending on the selected Gateway SKUs. Averaged over 3 runs, my average time was 34 minutes, got and get yourself a cup of coffee β˜•. If you select a Multi-AZ SKU this will increase your deployment time. Select the SKU relative to your throughput and cost requirements. Given I have a 100megabit internet connection and a single site, ‘vpnGW1’ is more than capable.

az network vnet-gateway create --name EdgeRouter10x --public-ip-address Baldacchino_VPN_Public_IP --resource-group Baldacchino_RG --vnet Baldacchino_vNET --gateway-type Vpn --vpn-type RouteBased --sku VpnGw1 --no-waitΒ 

We now need to marry all the values up and create the Site-2-Site VPN connection between your virtual network gateway and your on-premises VPN device.

az network vpn-connection create --name EdgeRouter10x --resource-group Baldacchino_RG --vnet-gateway1 VNet1GW -l australiaeast --shared-key [insert your secret here] --local-gateway2 Site2

That is all we need to do on the Azure side

Step 3 : Configure the EdgeRouter
Firstly, you will need to SSH in to your EdgeRouter using your favourite SSH tool. Today mine is the Windows 11 Terminal. Login using user@edgerouter_ip_address with your relevant password.

Enable the auto-firewall-nat-exclude feature which automatically creates the IPsec firewall/NAT policies in the iptables firewall, this will allow traffic to bypass any stated firewall rules.

configure
set vpn ipsec auto-firewall-nat-exclude enable

Next we need to cerate the IKE / Phase 1 (P1) Security Associations (SAs) and set the Key Exchange to IKEv2

set vpn ipsec ike-group FOO0 key-exchange ikev2
set vpn ipsec ike-group FOO0 lifetime 28800
set vpn ipsec ike-group FOO0 proposal 1 dh-group 2
set vpn ipsec ike-group FOO0 proposal 1 encryption aes256
set vpn ipsec ike-group FOO0 proposal 1 hash sha1

Create the ESP / Phase 2 (P2) SA;’s and disable Perfect Forward Secrecy (PFS). PFS is not supported with this Diffie Hellman group (group2) in Azure. More details on Azure cryptographic requirements for VPN gateways can be found here.

set vpn ipsec esp-group FOO0 lifetime 27000
set vpn ipsec esp-group FOO0 pfs disable
set vpn ipsec esp-group FOO0 proposal 1 encryption aes256
set vpn ipsec esp-group FOO0 proposal 1 hash sha1

In order to accept connections from Azure we must define the remote peering address and set the connection-type to respond. Azure will be initiating the tunnel.

set vpn ipsec site-to-site peer 20.211.114.231 authentication mode pre-shared-secret
set vpn ipsec site-to-site peer 20.211.114.231 authentication pre-shared-secret [insert your secret here]
set vpn ipsec site-to-site peer 20.211.114.231 connection-type respond
set vpn ipsec site-to-site peer 20.211.114.231 description ipsec
set vpn ipsec site-to-site peer 20.211.114.231 local-address 119.148.99.243

This VPN tunnel is then bound to a virtual tunnel interface (vti0)

set vpn ipsec site-to-site peer 20.211.114.231 ike-group FOO0
set vpn ipsec site-to-site peer 20.211.114.231 vti bind vti0
set vpn ipsec site-to-site peer 20.211.114.231 vti esp-group FOO0
set interfaces vti vti0

Lasty we then modify the TCP Maximum Segment Size (MSS) and define a static route to our Azure vNET (192.168.1.0/23)

set firewall options mss-clamp interface-type vti
set firewall options mss-clamp mss 1350
set protocols static interface-route 192.168.1.0/23 next-hop-interface vti0
commit
save




Step 4 : Test & Validate
We know want to validate this connection is online . This can be validated from both the EdgeRouter aswell as Azure, and ultimatley the real test is transmitting data from one CIDR range to another over the connection.

Validating from EdgeRouter

show vpn ipsec sa

Which will produce output similar to this below

  _____    _            
 | ____|__| | __ _  ___          (c) 2010-2020
 |  _| / _  |/ _  |/ _ \         Ubiquiti Networks, Inc.
 | |__| (_| | (_| |  __/         
 |_____\__._|\__. |\___|         https://www.ubnt.com
             |___/

Welcome to EdgeOS

Last login: Mon Mar 21 09:31:47 2022 from 10.0.0.202
ubnt@Baldacchino-EdgeRouter10x:~$ show vpn ipsec sa
peer-20.211.114.231-tunnel-vti: #32, ESTABLISHED, IKEv2, fd8d95c6f4702887_i dcea8b3ddb81c06a_r*
  local  '119.148.99.243' @ 119.148.99.243[4500]
  remote '20.211.114.231' @ 20.211.114.231[4500]
  AES_CBC-256/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_1024
  established 4165s ago, rekeying in 23914s
ubnt@Baldacchino-EdgeRouter10x:~$ 

Validating from Azure

az network vpn-connection show --name EdgeRouter10x --resource-group Baldacchino_RG

Which will produce the following

{
  "authorizationKey": null,
  "connectionMode": "Default",
  "connectionProtocol": "IKEv2",
  "connectionStatus": "Connected",
  "connectionType": "IPsec",
  "dpdTimeoutSeconds": 0,
  "egressBytesTransferred": 3526591,
  "egressNatRules": null,
  "enableBgp": false,
  "etag": "W/\"29e9bec1-3021-4d8d-af10-9aebc19b72e5\"",
  "expressRouteGatewayBypass": false,
  "id": "/subscriptions/xxxxxxxxxxxx/resourceGroups/Baldacchino_RG/providers/Microsoft.Network/connections/EdgeRouter10x",
  "ingressBytesTransferred": 2610251,
  "ingressNatRules": null,
  "ipsecPolicies": [],
  "localNetworkGateway2": {
    "id": "/subscriptions/xxxxxxxxxxxxx/resourceGroups/Baldacchino_RG/providers/Microsoft.Network/localNetworkGateways/Baldacchino_Local_Gateway",
    "resourceGroup": "Baldacchino_RG"
  },
  "location": "australiaeast",
  "name": "EdgeRouter10x",
  "provisioningState": "Succeeded",
  "resourceGroup": "Baldacchino_RG",
  "resourceGuid": "xxxxxxxxxxxxxxx",
  "routingWeight": 0,
  "sharedKey": "xxxxxxxxxxxxx",
  "tags": null,
  "trafficSelectorPolicies": [],
  "tunnelConnectionStatus": null,
  "type": "Microsoft.Network/connections",
  "useLocalAzureIpAddress": false,
  "usePolicyBasedTrafficSelectors": false,
  "virtualNetworkGateway1": {
    "id": "/subscriptions/xxxxxxxxxx/resourceGroups/Baldacchino_RG/providers/Microsoft.Network/virtualNetworkGateways/Baldcchino_VPNGW",
    "resourceGroup": "Baldacchino_RG"
  }
}

The best way to validate is to send traffic, it doesn’t get any more real than that.
In the video below we can see our Azure VM has a private IP address of 192.168.0.4. Our local compute device, our Raspberry PI with an IP address of 10.0.0.202/24. It knows nothing about the 192.168.0.0/24 network, we validate this based on the route table. Upon performing an ICMP Ping, we are timing out as the Azure VM is powered off but upon starting the VM, ICMP replies are received. We have validated traffic is flowing in both directions. Success!!


Summary
Thanks for sticking it out. Networks can be bit a dry, and whilst the cloud may seem a bit ‘magic’ with many higher order services, fundamentals such as networking, is today a service that cannot be easily abstracted by an API. I hope this post helped you out and enabled you setup and establish your site-2-site VPN between Azure and your EdgeRouter.

I work for @Microsoft but opinions expressed are my own.

Thanks
Shane Baldacchino

Leave a Comment