Automatically add trusted IPs to an Azure IP Group for Azure Firewall

For many reasons we choose to not use a VPN for traffic which is already encrypted and authenticated. Sometimes this is a challenge though, especially when the authentication opens us up for password guessing attacks. 

In this case, that vulnerability is our on-premises Exchange Server which is needed for Public Folder access amongst other things. We've tried different solutions to prevent this, including the Azure Web Application Firewall, but always end up needing to expose a portion of the Exchange Web Services to the internet. 

I'm sure some of you are wondering what we are doing wrong with Exchange to need this but I really can't recall each of the obstacles we ran into while trying to nail this down. 

Our latest attempt at mitigating this vulnerability collects the IP addresses of all of our trusted clients and automatically adds then to an Azure IP Group which is used in an Azure Firewall rule to allow HTTPS traffic to our on-premises Exchange Server.

The solution utilizes the following:

  • Azure Functions
  • Azure Firewall
  • Azure IP Groups
  • PowerShell
  • Intune

1. We used Intune to push out a Powershell Script which will run every 5 minutes. One caveat we ran into was Intune Powershell scripts will only run once in most cases but we found a great script from Jos Lieben which will allow for the script to run every 5 minutes. It can be found here.

The PowerShell code inserted into Jos's script is simple. It just retrieves the NAT address of the client and sends a POST with the IP address to the Azure Function App webhook. The PowerShell code is:

## Test if client already has access to our server on port 443
$connectstatus = Test-NetConnection -port 443 ourserver.com

## If the connection fails run the code to get public IP of the client
if ($connectstatus.TcpTestSucceeded -ne "True") {
$ipaddress=(Invoke-WebRequest -usebasicparsing -uri "https://api.ipify.org").Content
if ($ipaddress) {

Invoke-WebRequest -method POST -usebasicparsing -Uri "https://p-functionapp-allowip.azurewebsites.net/api/ExchangeAllowIP?code=xxx==&ipaddress=$ipaddress"
}
else {
    $ipaddress=(Invoke-WebRequest -usebasicparsing -uri "http://ifconfig.me/ip").Content  
    if ($ipaddress) {
        Invoke-WebRequest -method POST -Uri "https://p-functionapp-allowip.azurewebsites.net/api/ExchangeAllowIP?code=xxx==&ipaddress=$ipaddress"
    }  
}
}

That code is just inserted into Jos's script at the bottom where he includes comments on where to insert it.

2. The client runs the script above every 5 minutes to POST its IP address to the Azure Function App web hook. The Function App code is as follows:


3. When creating the Function App in Azure you are given the option to create a System Assigned Identity. Doing so will make sure that your Function App only has permissions to modify the IP Group which will be used in the Azure Firewall rule to allow HTTPS to the Exchange Server.

When we created the IP Group we also created a Resource Group containing only this one object. So we are able to scope the permissions of the Azure Function App Identity to be a Contributor to that Resource Group:





Notes:
We chose to use an Azure Premium Function App to gain the advantage of always having a warmed instance to fire the Function so everything runs as quickly as possible.

Currently, the Azure Firewall requires a Save after the IP Group has been modified to recognize the new IP addresses in the IP Group. I've created a Feedback to hopefully have this resolved which can be found here. I'll be modifying my Function App to issue a Save to the firewall to automate this in the future.

Comments

Popular posts from this blog

Auto-installing extensions on Firefox using Intune

Disable DNS over HTTPS in Firefox using Intune

Moving Applocker control from Group Policy to Intune