Azure Bastion routing in Virtual WAN

As you might know, Azure Bastion enables management connectivity to virtual machines without having to assign them public IP addresses, and without having to maintain jump hosts in your Virtual Network.

Up to recently, the virtual machines needed to be immediately peered to the VNet where Azure Bastion was deployed, but with IP-based connections Azure Bastion now supports new topologies, like connecting to on-premises systems or to VMs that are not directly connected. This feature also allows for the integration of Azure Bastion into Virtual WAN networks.

Two Small Things

There are two details about Azure Bastion that you need to know to avoid nasty surprises. The first one is that you should not override the default route (0.0.0.0/0) of the Azure Bastion subnet going to Internet as next hop, because otherwise you will break public connectivity to the Azure Bastion. This public connectivity is not only used by clients to connect to Azure Bastion over the Internet, but by Microsoft as well to manage the service.

The second important piece of information is that at the time of this writing you cannot attach a route table to the subnet where Azure Bastion is deployed (originally called AzureBastionSubnet). If you try to do so you will get an error message, as this example using the Azure CLI shows:

❯ az network vnet subnet update -n AzureBastionSubnet --vnet-name bastionVNet -g $rg --route-table bastionrt
(RouteTableCannotBeAttachedForAzureBastionSubnet) Route Table bastionrt cannot be attached to Azure Bastion Subnet AzureBastionSubnet.
Code: RouteTableCannotBeAttachedForAzureBastionSubnet
Message: Route Table bastionrt cannot be attached to Azure Bastion Subnet AzureBastionSubnet.

Virtual WAN environment

In my lab I have a Virtual WAN setup with 3 VNet connections: one for Azure Bastion, and two other ones with virtual machines:

Secured hub routing configuration

Spoiler alert: the fact that Internet traffic is not secured in the bastion VNet is important, but more to this later. The virtual machines in the workload VNets have no public IP addresses allocated:

❯ az vm list-ip-addresses -g $rg -o table
VirtualMachine    PublicIPAddresses    PrivateIPAddresses
----------------  -------------------  --------------------
testvm2                                10.11.1.4
testvm                                 10.12.1.4

So when a user connects to Azure Bastion, it will set another connection to the target virtual machine over the Virtual WAN Hub:

Connectivity between Azure Bastion and a virtual machine in another spoke

It works!

I will use Azure Bastion’s tunneling functionality to connect from my shell:

❯ vm_id=$(az vm show -n testvm -g $rg --query id -o tsv)
❯ az network bastion tunnel -n mybastion -g $rg --target-resource-id $vm_id --resource-port 22 --port 50001 &
[1] 12521
❯ Command group 'network bastion' is in preview and under development. Reference and support levels: https://aka.ms/CLI_refstatus
Opening tunnel on port: 50001
Tunnel is ready, connect on port 50001
❯ ssh localhost -p 50001
Welcome to Ubuntu 18.04.6 LTS (GNU/Linux 5.4.0-1090-azure x86_64)
...
jose@testvm:~$

The previous commands are fairly easy to understand:

  • First I get the ARM ID of the VM I want to connect to into a bash variable $vm_id
  • As second step I open a TCP tunnel between my machine and port 22 of the target VM, identified by that ARM ID I got in step 1. I am using TCP port 50001 to expose that tunnel in my local machine
  • Lastly, I can just use that tunnel with my SSH client, connecting to localhost and port 50001

Why no Internet Security in the Bastion VNet connection?

A Virtual WAN secured hub configured for Internet and private traffic security will inject certain routes into the NICs in the VNets connected to it. We can verify these routes in one of our virtual machines:

❯ az network nic show-effective-route-table -n testvmVMNic -g $rg -o table
Source                 State    Address Prefix    Next Hop Type          Next Hop IP
---------------------  -------  ----------------  ---------------------  -------------
Default                Active   10.12.0.0/16      VnetLocal
Default                Active   10.0.0.0/16       VNetPeering
VirtualNetworkGateway  Active   192.168.0.0/16    VirtualNetworkGateway  10.0.64.4
VirtualNetworkGateway  Active   0.0.0.0/0         VirtualNetworkGateway  10.0.64.4
VirtualNetworkGateway  Active   10.0.0.0/8        VirtualNetworkGateway  10.0.64.4
VirtualNetworkGateway  Active   172.16.0.0/12     VirtualNetworkGateway  10.0.64.4

Hence, unless we do anything about it, the Azure Bastion is going to receive the same routes, including the 0.0.0.0/0 that as we discussed earlier would break Internet connectivity and the control plane. No problem, we can associate a route table with a route for 0.0.0.0/0 and next hop Internet to the AzureBastionSubnet. Oh wait, we said earlier that is not supported!

Luckily Virtual WAN VNet connections offer us a knob that can be used to prevent the advertisement of a 0.0.0.0/0 route to a VNet, and that is what Firewall Manager calls “Remove Internet Traffic Security”. If you configured your Firewall Manager as the picture at the beginning of this post, and you went back to the VNet connection configuration, you would see this:

VNet connection configuration with Internet security disabled

Corollary

Note that this setting affects to the whole VNet where the Azure Bastion is deployed, hence I would personally recommend having the VNet dedicated to Azure Bastion without any other workload, so that you don’t inadvertently bypass the secured hub for production workloads.

What about traditional Hub and Spoke?

In traditional hub and spoke setups (not using Virtual WAN) similar concepts apply: since you cannot use route tables in the AzureBastionSubnet, you need to inject routes in a different way. That way is the Azure Route Server.

In a traditional hub and spoke deployment though the use case would be different than in Virtual WAN: you would probably have Azure Bastion in the hub VNet, but you might want to firewall the flows between the Azure Bastion and the target VMs, like the following picture shows:

Unfortunately, that one would be difficult to achieve because Azure Route Server cannot override the routes learnt via VNet peering. Hence, if you wanted to filter flows between Bastion and the target VM, you might want to revert to a topology similar to the one we saw earlier for Virtual WAN (note however that Azure Route Server doesn’t have a mechanism to filter out 0.0.0.0/0 advertisements for specific VNets, as Virtual WAN does):

In these diagrams I am using the icon of Azure Firewall, but since Azure Firewall doesn’t speak BGP and hence cannot interact with Azure Route Server, that would rather be a Network Virtual Appliance (unless you are using some trick like the one I describe in this blog post).

Long story short

Azure Bastion IP-based connections open a brand new collection of designs. The thing to remember here is that no route tables are supported in the AzureBastionSubnet (at this time), hence any route that you want to inject needs to be advertised with either Virtual WAN or Azure Route Server

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 )

Twitter picture

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

Facebook photo

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

Connecting to %s

%d bloggers like this: