Old habits die really hard.
My mental model has been “one fixed IP address per public site” (or gateway or proxy or the like). IOW, if you do an nslookup
on a site, you get back a public IP address that’s more or less fixed.
Then today, I was watching a tail
of the access log for a site I host in Azure while ssh
probe after probe from all over the IPv4 space just kept bombarding me. Some of these sites are legit scanners. But many more of them are looking for a way in.
I have all kinds of defenses in place but I wondered if there was a way to reliably rotate the public IP address as a way of throwing bad actors off the scent.
Turns out the thing I’ve always tried to avoid in Azure networking — dynamic public IP addresses — are just the thing.
A dynamic Azure public IP (pip) address is simply a pip with the correct property set. Here’s a Bicep snippet of what I used to define my dynamic public ip address:
... param location string = resourceGroup().location param publicIpAddressName string = 'pipDynamic' param publicIpAddressType string = 'Dynamic' param publicIpAddressSku string = 'Basic' ... resource publicIpAddress 'Microsoft.Network/publicIpAddresses@2020-08-01' = { name: publicIpAddressName location: location properties: { publicIPAllocationMethod: publicIpAddressType dnsSettings: { domainNameLabel: 'yourAzureDnsLabel' } } sku: { name: publicIpAddressSku } } ...
Note that I used the soon-to-be-deprecated Basic SKU. You probably don’t want to use the Basic pip — I’m just stubborn.
With a dynamic pip, every time you stop the VM via the Azure portal (or API, meaning via PowerShell, CLI or REST call), Azure will de-allocate the addresss of that pip. But you still have a pip resource. And if your IP configuration for that VM references that dynamic pip, you’ll get a new address when the VM is started.
Notice domainNameLabel
in the Bicep snippet above. This gets combined with the standard Azure domain name. For example, if your VM is running in the East US region, the Bicep above would update DNS for yourAzureDnsLabel.eastus.cloudapp.azure.com
with whatever new dynamic pip was allocated at startup.
Now, all you have to do is create a CNAME record to point to the Azure-created FQDN, as shown in the table below. Note that you do not have to match the low-level name of the canonical name to domain name in the CNAME record.
Type | Domain Name | TTL | Canonical Name |
---|---|---|---|
CNAME | yourRealSiteDns.com | 3600 | yourAzureDnsLabel.eastus.cloudapp.azure.com |
Now, when the client requests the domain name, the canonical name is returned and the client knows that it’s a CNAME and it should query the canonical name and use what the query returns for the current IP address. Yes, it’s an extra DNS query, but these are small and designed to be fast.
That brings up the question of how fast Azure updates the canonical name to point to a new dynamic pip. In test after test, as soon as I restarted the VM, the first DNS query I send reliably produced the correct dynamic pip. Azure is doing a great job of making sure the dynamic pip’s IP address is current.
Here are a couple of screenshots showing what this looks like in the portal. First, is a screenshot showing what the Bicep above would produce for the Azure DNS settings. I’ve blurred out my specific CNAME entry and, naturally, that IP address you see is long gone. 🙂
The second screenshot shows what happens when you stop the VM in the portal. Azure gives you the chance to keep the current IP address, though the whole point of this is to not do so.
One really good way to use this is with Azure Backup to create a crash-consistent backup. Or, you can couple the restart with a maintenance cycle.
One thing is for sure: this is not sufficient to repel attackers. But, for zero cost and very low effort, it seems like a no-brainer.
Leave a Reply