One of the top 10 Azure consuming companies has multiple country government-mandated requirements to block egress to and ingress from IP addresses and IP address ranges on a dynamic embargoed/sanctioned IP list. In other words, various country governments across the globe forbid the company from sending traffic to certain addresses.
The company needed a very agile solution that could translate a dynamically changing list of IP addresses and CIDR ranges into active policy applied globally across any Azure region and can scale to huge numbers of subscriptions. It needed to be very low maintenance, work quickly, and not allow for bypassing.
Solution Summary: The Microsoft CE DSE team came up with a solution leveraging Azure Firewall Policy and Azure Function Apps to enforce a parent Network Rule policy that is based on automatically updating IP Groups to control traffic to these embargoed locations across their global enterprise.
The global network has a disparate blend of network topologies, managed by dozens of lines of business (LoB). Some have both an App Gateway publishing websites and an Azure Firewall controlling general traffic (Scenario 1, below). Others have only an Azure Firewall (Scenario 2), and a handful have 3rd party Network Virtual Appliances (NVA) controlling ingress and egress (Scenario 3). In each of these scenarios, Azure Firewall policy is used to enforce the network traffic. Additionally, Azure Firewall Manager, could be added into the mix to push this policy from a single, central location to multiple firewalls (however, it does incur additional cost). Representative sample network topologies are shown in Figure 1, below.
The building blocks of the solution are as follows in Figure 2 below. The placement of resources into separate Resource Groups as implemented is not a necessity.
A storage account containing a blob storage container is required as the target location for the controlled IP address list.
The function app provides the automation behind the solution. It is configured to watch and trigger off any text files getting dropped into the blob storage container. It runs a simple PowerShell script that runs the logic to split the IP addresses into the desired number of pieces, and then runs Set-AzIPGroup in parallel processing to assign those ranges to the IP Groups.
A Network Rules Collection rule inside of Azure Firewall Policy defines the action (deny) and is linked to the IP Groups. Azure Firewall Manager can optionally be used to push this parent policy to any number of Azure Firewalls in the Azure Tenant, even across regions.
Here's how the pieces fit together (see Figure 3 below). As mentioned, the company's global security department receives mandated embargoed IP range lists from various government and regulatory agencies and compiles them into a simple text file, with one IP/IP CIDR range per line.
This is the sequence of events:
The outcome of this solution meets the mandates of global security. Global security gets to control the embargoed list of IP addresses/ranges in a manner that can be implemented company-wide, across the globe in minutes, well within their 24 hour SLA. Lines of Business are not able to add exceptions to this list, as it's enforced as a parent policy. Because it is IP Groups getting updated (as opposed to a firewall network rule getting deleted and re-created), there's never a lapse in IP blocking, except when an IP address drops off the embargo list, which is the desired behavior.
This is a very scalable solution that can accommodate the largest customers. There is no limit to the number of IP addresses/ranges that can be added to an IP Group (although the Azure networking team only has tested up to 5,000 and can guarantee performance to that number), with up to 100 IP Groups linked to a firewall rule, and multiple rules allowed, there are few scale scenarios this cannot handle.
There can be substantial cost involved to the customer, as Azure Firewall can add up, however. In my next article I'll cover the same general scenario and mechanism, which relies upon the new Azure Network Manager (ANM) feature instead of Azure Firewall. ANM does not scale as much, but should be (pricing is not set yet, as of this article writing) a much lower price point.
Here are more details about the solution.
# Input bindings are passed in via param block.
param([byte[]] $InputBlob, $TriggerMetadata)
function Split-Array($InputArray, [int]$Size)
{
$parts = [Math]::Ceiling($InputArray.count / $Size)
$outArray = New-Object System.Collections.Generic.List[psobject]
for ($i = 1; $i -le $parts; $i++)
{
$start = ($i - 1) * $size
$end = $i * $size - 1
if ($end -ge $InputArray.count) { $end = $InputArray.count - 1 }
$outArray.Add($InputArray[$start..$end])
}
return $outArray
}
# Write out the blob name and size to the information log.
Write-Host "PowerShell Blob trigger function Processed blob! Name: $($TriggerMetadata.Name) Size: $($InputBlob.Length) bytes"
$startT = Get-Date
$enc = [System.Text.Encoding]::ASCII
$addr = $enc.GetString($InputBlob)
$addrArr = $addr.Split("`n")
# $addrArr = $addr.Split("`r`n")
# Write-Host $addrArr
$numOfRules = 13 #This is user defined; for PoC the rules were pre-created and bound to a firewall network rule
[int]$chunkSize = $env:chunkSize
Set-AzContext -SubscriptionName "demo-anmtest2"
$count = $addrArr.Count
Write-Host "Number of IP addresses $($count)"
$ipGroups = Get-AzIpGroup -ResourceGroupName anmtest2-net-rg -Name from_file*
# if there is only one element in array (number of elements smaller than chunk size)
# indexing is wrong, index is not 0 but number of elements in "inner" array?!
if ($chunkSize -ge $count)
{
# first group only
Write-Output "Adding IPs for single group..."
$group = $ipGroups[0]
$group.IpAddresses.RemoveRange(0, $group.IpAddresses.Count)
$group.IpAddresses.AddRange([System.Collections.Generic.List[string]]$addrArr)
Set-AzIpGroup -IpGroup $group
}
else
{
$ipArr = Split-Array -InputArray $addrArr -Size $chunkSize
$groupArr = @()
Write-Output "Adding IPs for multiple groups..."
for ($i = 0; $i -lt $ipArr.Count; $i++)
{
$group = $ipGroups[$i]
$group.IpAddresses.RemoveRange(0, $group.IpAddresses.Count)
$group.IpAddresses.AddRange([System.Collections.Generic.List[string]]$ipArr[$i])
$groupArr += $group
}
$groupArr | foreach -Parallel {
Set-AzIpGroup -IpGroup $_
}
}
# Get rest and remove IPs
Write-Output "Removing IPs..."
if ($chunkSize -ge $count)
{
$groupsToRemoveIPs = $ipGroups[1..($numOfRules - 1)]
}
else
{
$groupsToRemoveIPs = $ipGroups[$ipArr.Count..($numOfRules - 1)]
}
$groupsToRemoveIPs | foreach -Parallel {
if ($_.IpAddresses.Count -gt 0)
{
$_.IpAddresses.RemoveRange(0, $_.IpAddresses.Count)
Set-AzIpGroup -IpGroup $_
}
}
$endT = Get-Date
$totT = $endT - $startT
Write-Output $totT
*Disclaimer
The sample scripts are not supported under any Microsoft standard support program or service. The sample scripts are provided AS IS without warranty of any kind. Microsoft further disclaims all implied warranties including, without limitation, any implied warranties of merchantability or of fitness for a particular purpose. The entire risk arising out of the use or performance of the sample scripts and documentation remains with you. In no event shall Microsoft, its authors, or anyone else involved in the creation, production, or delivery of the scripts be liable for any damages whatsoever (including, without limitation, damages for loss of business profits, business interruption, loss of business information, or other pecuniary loss) arising out of the use of or inability to use the sample scripts or documentation, even if Microsoft has been advised of the possibility of such damages.
The table below shows run times tested during implementation. We fed a text file containing various numbers of IP ranges (left column) to the Azure Function and waited for completion. The resulting time is shown in the right column. Tests were performed in the sequence shown in the table.
Qty IP Addresses in the Text file |
Function Running time (mm:ss) |
8005 |
4:40 |
13 |
4:00 |
8005 |
1:45 |
13 |
3:50 |
11157 |
3:57 |
13 |
4:25 |
13 |
5:25 |
13 |
3:40 |
11157 |
4:55 |
13 |
5:33 |
8005 |
4:29 |
11157 |
4:48 |
13 |
4:38 |
11157 |
7:53 |
13 |
9:29 |
11157 |
3:22 |
8005 |
5:23 |
11157 |
2:38 |
13 |
5:08 |
8005 |
2:36 |
11157 |
3:38 |
Putting this all together, we end up with a simple to implement and maintain solution for an enterprise’s central security to enforce a set of IP addresses or ranges across all of their Azure assets that traverse the Azure Firewall. Requiring only minutes to go into effect, this combination of Azure Storage blob, Azure Function, IP Groups and Azure Firewalls can scale to handle up to the largest enterprise customers and insure that mandated block lists can be achieved.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.