Azure: Disable outbound traffic from VNet to internet except the Azure Services

Virtual Networks and Virtual Network Interfaces in Azure could have own Network Security Groups. Every group consists from security rules which enable or disable traffic by defined rules. In some cases we want to disable outbound traffic to the internet but unfortunately this means we disable traffic to various Azure services which are out of our virtual network. In this article I’ll explain how to solve this issue in a few steps.

Meet the NSG and NSG Rules

NSG (Network Security Group) is a security rule set. Every isolated security rule enable or disable traffic based on the configuration. Configuration consists from IP address and protocol or kind of service (Virtual Network, Internet, Load Balancer). There are some examples on the image below:

ukazka-nsg-rules

All the rules are applicated by their priority (higher numbers are applied first). Your own security rules you can define with priority 0-4096. At the end of the rules set we can see Default Rules which are defined everytime and they have significantly lower priority (priority 60000 and higher).

ukazka-vlastniho-pravidla

This Default Rules cannot be deleted neither changed but logically they can be overrided by another rules with higher priority. See the example below. Default rule with low priority is overriden by custom rule. This is good example how to disable all the outbound traffic to the internet.

zakazani-konektivity-outbound-inet

The rule set (NSG) is applicable to Virtual Networks or Virtual Network Interfaces (NIC). In the real world we apply the rule set for the whole Virtual Network together with specific rules for Virtual Machines (each Virtual Machine can only have one NIC).

How to disable outbound traffic and solve troubles with Azure services availability

Ideally we could create Virtual Network and disable all the outbound trafic to the internet. The problem is that most of the Azure services could not be assigned to our Virtual Network (they are available on the internet) so that we cannot disable all the trafic. Fortunately we can apply a few rules:

  1. By default all the outbound traffic is allowed (we cannot delete this rule)
  2. We‘ll add new rule and disable all the outbound traffic (this is way how to override rule no 1)
  3. We’ll add new rule set and allow outbound traffic to IP addresses of Azure Services (we partially override rule no 2)

It seems to be easy but the problem is we never know which IP addresses will have Azure Services assigned. We usually do not care about it because we typically use just DNS. The best solution is to allow outbound traffic to all the IP addresses of all the Azure Services. List of all IP ranges we can download as XML:

seznam-ip-adres-datovych-center-a-sluzeb-v-azure

Restriction for NSG (max 200 security rules)

There is another issue we have to solve. NSG has restriction for maximum of 200 security rules and the West Europe region use over 400 IP ranges. It means we must contact Microsoft Azure Support and request for increasing limit. Finally, Microsoft allows us to manage 500 security rules. More about Azure limits you can read on:

Design: Automation with Azure PowerShell and VSTS Release Management

How to add over the 400 security rules? By hand? Nooo.. IP Ranges could change and we need to automate. And right now we need Azure Powershell. With accomplishing this challenge helped me article from Keyth Mayer:

His solution is interactive script and a wizard which creates rule set based on user input. In my case I needed change the behavior and support simple requirement: to be able to run script recurently with well known configuration using the VSTS Release Management. As the trigger I decided to use simple timer scheduler.

Solution: Automatic continuous update of NSG rules

From the high-level perspective the solution is easy:

  1. Write a PowerShell script
  2. Create VSTS Release Definition which consists from one step: run the script from step 1

1. PowerShell script

How the algorithm works? First I delete all the rules created previously by automation, then I download new XML file with IP address ranges and finaly I add all the IP address ranges as a set of new rules. It means that NSG is never changed (just the rules inside). Update of the NSG is fired in the end of the script so that I do not have to be afraid of issues and unsuccessful partial update. The final script follows here:

$subscriptionId = 'xxx'
$nsgName = 'xxx'
$rgName = 'xxx'
 
Select-AzureRmSubscription -SubscriptionId $subscriptionId
$nsg = Get-AzureRmNetworkSecurityGroup -Name $nsgName -ResourceGroupName $rgName
 
# Download current list of Azure Public IP ranges
$downloadUri = "https://www.microsoft.com/en-in/download/confirmation.aspx?id=41653"
$downloadPage = Invoke-WebRequest -Uri $downloadUri -UseBasicParsing
$xmlFileUri = ($downloadPage.RawContent.Split('"') -like "https://*PublicIps*")[0]
 
$response = Invoke-WebRequest -Uri $xmlFileUri -UseBasicParsing
 
# Get list of regions & public IP ranges
[xml]$xmlResponse = [System.Text.Encoding]::UTF8.GetString($response.Content)
 
$regions = $xmlResponse.AzurePublicIpAddresses.Region
 
#$selectedRegions = $regions.Name | Out-GridView -Title "Select Azure Datacenter Regions ..." -PassThru
$ipRange = ( $regions | where-object Name -In "europewest" ).IpRange
 
$rulePriority = 300
$counter = 1
 
ForEach ($rule in $nsg.SecurityRules | where-object Direction -Eq 'Outbound') {
    If($rule.Name -like '*AllowAzureOutbound_*'){
        Remove-AzureRmNetworkSecurityRuleConfig -Name $rule.Name -NetworkSecurityGroup $nsg
 
        Write-Host("REMOVED NSG RULE " + $rule.Name)
    }
}
 
ForEach ($subnet in $ipRange.Subnet) {
 
    $rulePriority++
    $counter++
 
    $ruleName = "AllowAzureOutbound_" + $subnet.Replace("/","-")
 
    $nsgRule = New-AzureRmNetworkSecurityRuleConfig `
            -Name $ruleName `
            -Description "Allow outbound to Azure $subnet" `
            -Access Allow `
            -Protocol * `
            -Direction Outbound `
            -Priority $rulePriority `
            -SourceAddressPrefix VirtualNetwork `
            -SourcePortRange * `
            -DestinationAddressPrefix "$subnet" `
            -DestinationPortRange *
 
    $nsg.SecurityRules += ,$nsgRule
 
    Write-Host("ADDED NSG RULE " + $subnet)
 
    If ($counter -gt 490)
    {
        break
    }
}
 
If (!$error) {
    Set-AzureRmNetworkSecurityGroup -NetworkSecurityGroup $nsg
} else {
    Write-Host("NOTHING CHANGED BECAUSE OF ERROR")
}

I am not a PowerShell guru and probably the script could be written more elegantly and efficiently.

2. VSTS Release Definition

My Release Definition consists from one environment with one single step (Azure PowerShell task). This task connects to Azure Subscription and runs the script from step 1.

release-definition-s-taskem-azure-ps

It looks nice but there is a small catch. Current version of Azure PowerShell allows to create inline script with max length 500 chars. We have to hack a bit: open developer console in favorite browser and break the client validation.

4 thoughts on “Azure: Disable outbound traffic from VNet to internet except the Azure Services

  1. Cyjrk

    Hi ,I tried to block SSH traffic in outgoing rules .But it didn’t happen.still traffic is going ,and but I mentoided SSH in incoming rule also.its just a scenario.does it mean if I put incoming rules and then try to block outgoing traffic even if i mention same SSH rule as a deny in outgoing traffic .Please suggest

    Like

    Reply
    1. Sriram

      Please check ephemeral ports once. This is because when outgoing port of SSH is not 22.
      It is common we expect SSH outgoing port as 22 since incoming port is 22. But, this is not the case.
      SSH uses ephemeral ports as outgoing ports.

      Like

      Reply
  2. Sami Holtta

    You have nowadays a service tag AzureCloud in the NSG, it contains Azure public IPs. So you can block Internet outbound and allow AzureCloud outbound.

    Like

    Reply

Leave a comment