Azure VPN Gateway Connection with custom IPSEC Policy

I was recently setting up a VPN tunnel between an Azure VPN Gateay and an on-premise location, and ran into issues with the tunnel connecting.

The connection in Azure kept saying “connecting”. I was trying to use the VPN troubleshooter to log diagnostics to a storage account for parsing, however the diags didn’t contain the actual errors, and the wizard in the Portal wouldn’t refresh from subsequent runs, so it was stuck on an error with the pre-shared key which I had already corrected.

The on-premise device is a Cisco, and so there were accessible error messages from it:

crypo map policy not found for remote traffic selector 0.0.0.0

This led me down a path of searching resulting in the Cisco example configuration from Microsoft. The key part of this is that a Cisco ASA cannot make a connection to a native RouteBased VPN Gateway in Azure.

The fix is to apply a custom IPSec policy to your connection, particularly with this flag: -UsePolicyBasedTrafficSelectors $True

I used a small bit of PowerShell in order to try this out:

$rg          = "default-rg"
$ConnectionHEN = "vpngw"
$connection = Get-AzVirtualNetworkGatewayConnection -Name $ConnectionHEN -ResourceGroupName $rg
$newpolicy   = New-AzIpsecPolicy -IkeEncryption AES256 -ikeintegrity SHA256 -DhGroup DHGroup2 -IpsecEncryption AES256 -IpsecIntegrity SHA256 -PfsGroup none -SALifeTimeSeconds 28800
Set-AzVirtualNetworkGatewayConnection -VirtualNetworkGatewayConnection $connection -IpsecPolicies $newpolicy -UsePolicyBasedTrafficSelectors $true

However, this returned the following error:

A virtual network gateway SKU of Standard or higher is required for Ipsec Policies support on virtual network gateway

My VPN Gateway is of SKU “Basic”, so it does not support IPSec policies, according to this documentation page.

Because it’s Basic, I can’t simply upgrade to a “VpnGW1” – I have to destroy and re-create my gateway as the new SKU, which will also generate a new public IP address.

So I did these things, but I did them using Terraform since this environment is managed with that tool.

First I ‘tainted‘ the existing resource to mark it for deletion and recreation. Then I ran “terraform apply” to make the modifications, based on the resources here below:

 

resource "azurerm_resource_group" "srv-rg" {
  name     = "srv-rg"
  location = "${var.location}"
}
resource "azurerm_public_ip" "vpngw-pip" {
  name = "vpngateway-ip"
  location = "${azurerm_resource_group.srv-rg.location}"
  resource_group_name = "${azurerm_resource_group.srv-rg.name}"
  allocation_method = "Dynamic"
}
resource "azurerm_local_network_gateway" "localgateway" {
  name                = "localgateway"
  resource_group_name = "${azurerm_resource_group.srv-rg.name}"
  location            = "${azurerm_resource_group.srv-rg.location}"
  gateway_address     = "1.2.3.4"
  address_space       = "10.10.0.0/24"
}
resource "azurerm_virtual_network_gateway" "vpngw" {
  name = "vpngw"
  location = "${azurerm_resource_group.srv-rg.location}"
  resource_group_name = "${azurerm_resource_group.srv-rg.name}"
  type = "Vpn"
  vpn_type = "RouteBased"
 
  active_active = false
  enable_bgp = false
    sku = "VpnGw1"
 
  ip_configuration {
    name = "vpngateway_ipconfig"
    public_ip_address_id = "${azurerm_public_ip.vpngw-pip.id}"
    private_ip_address_allocation = "Dynamic"
    subnet_id = "${azurerm_subnet.GatewaySubnet.id}"
  }
}
 
# Client VPN Connection
resource "azurerm_virtual_network_gateway_connection" "vpnconnection" {
  name = "vpnconnection"
  location = "${azurerm_resource_group.srv-rg.location}"
  resource_group_name = "${azurerm_resource_group.srv-rg.name}"
 
  type = "IPsec"
  virtual_network_gateway_id = "${azurerm_virtual_network_gateway.vpngw.id}"
  local_network_gateway_id = "${azurerm_local_network_gateway.localgateway.id}"
  use_policy_based_traffic_selectors = true
 
  shared_key = "${var.ipsec_key}"
 
  ipsec_policy {
    dh_group = "DHGroup2"
    ike_encryption = "AES256"
    ike_integrity = "SHA256"
    ipsec_encryption = "AES256"
    ipsec_integrity = "SHA256"
    pfs_group = "None"
    sa_lifetime = "28800"
 
  }
}

 

 

Azure Application Gateway learnings

I’ve been fine-tuning some Terraform config for Azure Application Gateway lately, and have thus been fine-tuning my understanding of its components. This Microsoft Doc about the App Gateway configuration was quite helpful because of it’s diagram.

Here’s a few items I’ve learned:

  • Terraform: You must separate out private IP and public IP into different front-end configurations. If you wish to utilize both to be associated with listeners, you’d have two config blocks:
    • frontend_ip_configuration {
          name                 = "${local.frontend_ip_configuration_name}"
          public_ip_address_id = "${azurerm_public_ip.test.id}"
        }
    • frontend_ip_configuration {
          name                 = "${local.frontend_ip_configuration_name}"
          subnet_id = "${azurerm_subnet.test.id}"
          private_ip_address_id = "${azurerm_subnet.testsub.id}"
          private_ip_address_allocation = Static
        }
  • General: A listener can only be associated with 1 front end IP (either private or public). Originally I thought that I could have both a private and public front-end that were associated with the same listener, and thus the same rule with a backend. This isn’t possible, and instead you must have unique listeners to each front end configuration.
  • Terraform: Terraform seems to have a problem adding multiple rules touching the same backend. Even though these were unique rules associated with unique listeners, Terraform gave this error: ApplicationGatewayBackendAddressPoolCannotHaveDuplicateAddress
    • This was seen while trying to add an additional rule after an original was created. I haven’t yet tried to perform a ‘terraform apply’ from a fresh start with two rules referencing the same backend.
  • General: You can’t have multiple listeners on the same front-end port across two different front-end configurations. If you do, you receive the following error:
    • For example, if I have port 80 on my private front-end config, I can add multi-site listeners based on hostname here to target multiple rules and backends.  But this means I cannot use port 80 on the public front-end configuration anymore; a different port would be required.
    • Error: Two Http Listeners of Application Gateway are using the same Frontend Port and HostName (ApplicationGatewayFrontendPortsUsingSamePortNumber)