Creating DMZ in Azure Part II

More than one year ago I had written a blog post about Setting up a DMZ in Azure.

At that time Azure networking did not have features necessary to implement a DMZ.

At Tech Ed Barcelona there were many significant announcements related to Azure networking.

You can read a summary of all the announcements made at Tech Ed Barcelona here:

https://weblogs.asp.net/scottgu/azure-new-marketplace-network-improvements-new-batch-service-automation-service-more

 

Networking improvements that were announced at Tech Ed were:

  1. Network Security Groups: This is a key feature that allows you to create access control roles at VM or subnet level. You can control these rules independent of the life cycle of the VM. Before Network Security Groups were available the only option to restrict access to virtual machines inside of a virtual network was with Access Control Lists that were applied at individual virtual machine end points.
  2. Multi NIC Support: This is another feature that is required by most network appliance. Now you can have up to 4 NIC’s in a virtual machine based on the size of the virtual machine. This is will allow many partners to deploy their virtual network appliance on Azure platform.
  3. Forced Tunneling: This is a requirement for many enterprise grade applications. This feature allows  you to force internet bound traffic from your cloud application running in a Azure virtual network to on premises network via site to site VPN. This allows your security team to inspect this traffic.
  4. Express Route Enhancements: This will allow 1 express route connection to be shared among multiple Azure subscriptions. One Site to Site VPN can also connect with multiple Express route circuits.

 

I will now attempt to create a DMZ in Azure virtual network.  I will be able to leverage many of the improvements made to Azure networking stack with in the last 15 months since I wrote the original blog post about creating a DMZ.

 

On Premise Application

 

Here is a typical application running on premises. It has a web front end with 2 web servers. It has an application server tier with 2 application servers. It also has a highly available database tier. The two web servers are in DMZ. All the inbound HTTP/HTTPS traffic can only come to the Port 80/443 of the Web Servers. The application servers are in a App VLAN and they only accept traffic from Web Servers. Database Servers are in DB VLAN and it only accepts traffic from the servers in App VLAN.

 

 

typical app

Azure Application

Here we will attempt to implement the application application in Microsoft Azure Platform.

We will create an Azure virtual network. It will have 3 subnets.

WebSubnet will have two web server virtual machines. All the requests coming to the web servers will be load balanced by Azure public load balancer.

AppSubnet will have two application servers. All the requests coming to application servers will be load balanced by Azure internal load balancer. This load balancer is only accessible to the virtual machines in the virtual network. AppSubnet is secured with a Network security group which only allows requests from WebSubnet.

DBSubnet will have two database servers. All the requests coming to the database servers will be received by Azure internal load balancer. This is used to configure always on configuration of SQL Server 2012 or higher versions.

image

We will use Azure Management Portal and PowerShell to create this environment.

1. Create Azure Virtual network.

Log into Azure management portal at http://manage.windowsazure.com

Create New Virtual Network as shown below.

createvnet

This will open a wizard. On page 1 of the wizard you will enter the virtual network name and location and press next

vnetdetail-1

On this page we will typically enter DNS Server and VPN connectivity information. Active Directory and DNS Server is required by virtual machines running in a Virtual Network. However in our scenario we will skip this step. Press next arrow.

vnetdetail-2

On this page we need to define Virtual network address space.

You need to carefully plan the virtual network address space. It should be appropriately sized.

In the example below we have a virtual network address space of 192.168.0.0/24.

We defined 3 subnet:

WebSubnet will contain the web servers

AppSubnet will contain the application servers

DBSubnet will contain the database sergvers

image

You can use the Export command in the Azure management portal to export the configuration of all virtual networks in your subscription. Here is how the XML configuration for the above virtual network looks.

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
<NetworkConfiguration xmlns:xsd=”http://www.w3.org/2001/XMLSchema” xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance” xmlns=”http://schemas.microsoft.com/ServiceHosting/2011/07/NetworkConfiguration”>
  <VirtualNetworkConfiguration>
    <Dns />
    <VirtualNetworkSites>
      <VirtualNetworkSite name=”TypicalAppVnet” Location=”Central US”>
        <AddressSpace>
          <AddressPrefix>192.168.0.0/24</AddressPrefix>
        </AddressSpace>
        <Subnets>
          <Subnet name=”WebSubnet”>
            <AddressPrefix>192.168.0.0/28</AddressPrefix>
          </Subnet>
          <Subnet name=”AppSubnet”>
            <AddressPrefix>192.168.0.16/28</AddressPrefix>
          </Subnet>
          <Subnet name=”DBSubnet”>
            <AddressPrefix>192.168.0.32/27</AddressPrefix>
          </Subnet>
        </Subnets>
      </VirtualNetworkSite>
    </VirtualNetworkSites>
  </VirtualNetworkConfiguration>
</NetworkConfiguration>

 

 

Creating Virtual Machines

It is assumed that you already created a  storage account and set the current storage account using Set-AzureSubscription PowerShell cmdlet.

Web Servers

Web Servers are created in WebSubnet. They are capable of receiving requests from public so they have a public load balanced end point.

Web server 1 will be assigned an IP address of 192.168.0.4

Web server 2 will be assigned an IP address of 192.168.0.5

You can use the following PowerShell script to create these web servers in WebSubnet

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
#Configure the virtual machines to be created
$vm11 = New-AzureVMConfig -Name $WebServer1 -InstanceSize $InstanceSize -ImageName $image.ImageName -AvailabilitySetName $WebAvailabilitySetName  |  `
                Set-AzureSubnet $WebSubnet | `
                Add-AzureEndpoint -Name “HttpIn” -Protocol “tcp” -PublicPort 80 -LocalPort 8080 -LBSetName “WebFarm” -ProbePort 80 -ProbeProtocol “http” -ProbePath ‘/’ | `
                Add-AzureProvisioningConfig -Windows -AdminUsername $credential.GetNetworkCredential().username -Password $credential.GetNetworkCredential().password 
 
$vm12 = New-AzureVMConfig -Name $WebServer2 -InstanceSize $InstanceSize -ImageName $image.ImageName -AvailabilitySetName $WebAvailabilitySetName | `
                Set-AzureSubnet $WebSubnet | `
                Add-AzureEndpoint -Name “HttpIn” -Protocol “tcp” -PublicPort 80 -LocalPort 8080 -LBSetName “WebFarm” -ProbePort 80 -ProbeProtocol “http” -ProbePath ‘/’ | `
                Add-AzureProvisioningConfig -Windows -AdminUsername $credential.GetNetworkCredential().username -Password $credential.GetNetworkCredential().password 
 
 
# Make an array of the virtual machine configuration so we can create them with 1 call
$vms = @($vm11, $vm12) 

#check to see if the cloud service for these virtual machines already exists
$service = Get-AzureService -ServiceName $WebServiceName -ErrorAction SilentlyContinue            
    
if ($service -eq $null) 
{ 
    # Create a new cloud service and Deploy Virtual Machines to Virtual Network
    New-AzureVM -ServiceName $WebServiceName -Location $DataCenter -VMs $vms -VNetName $VNetSiteName -ErrorVariable errorVariable -ErrorAction SilentlyContinue | Out-Null
} 
else 
{ 
    #Deploy Virtual Machines to Virtual Network
    New-AzureVM -ServiceName $WebServiceName -VMs $vms -ErrorVariable errorVariable -ErrorAction SilentlyContinue | Out-Null
} 
 
if (!($?)) 
{ 
    throw “Unable to create virtual machines $WebServer1 and $WebServer2. Error detail is: $errorVariable” 
} 
else
{
    Write-Verbose “Successfully created virtual machines $WebServer1 and $WebServer2” 
}

 

Once the web servers have been created you need to log into these servers, configure IIS and update the firewall rules to allow traffic over port 80.

You can use the following PowerShell  script to create the application servers in AppSubnet. Application servers are load balanced using internal load balancer. So the only server in the same virtual network can send requests to the load balanced end point for applications servers.

Application server 1 will be assigned IP address of 192.168.0.20

Application server 2 will be assigned IP address of 192.168.0.22

Azure Internal Load Balancer will be assigned an IP address of 192.168.0.21

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
#create the two application servers with Internal Load balancer
#Configure the virtual machines to be created
$vm11 = New-AzureVMConfig -Name $AppServer1 -InstanceSize $InstanceSize -ImageName $image.ImageName -AvailabilitySetName $AppAvailabilitySetName |  `
                Set-AzureSubnet $AppSubnet | `
                Add-AzureProvisioningConfig -Windows -AdminUsername $credential.GetNetworkCredential().username -Password $credential.GetNetworkCredential().password 
 
$vm12 = New-AzureVMConfig -Name $AppServer2 -InstanceSize $InstanceSize -ImageName $image.ImageName -AvailabilitySetName $AppAvailabilitySetName| `
                Set-AzureSubnet $AppSubnet | `
                Add-AzureProvisioningConfig -Windows -AdminUsername $credential.GetNetworkCredential().username -Password $credential.GetNetworkCredential().password 
 
 
# Make an array of the virtual machine configuration so we can create them with 1 call
$vms = @($vm11, $vm12) 

#check to see if the cloud service for these virtual machines already exists
$service = Get-AzureService -ServiceName $AppServiceName -ErrorAction SilentlyContinue            
    
if ($service -eq $null) 
{ 
    # Create a new cloud service and Deploy Virtual Machines to Virtual Network
    New-AzureVM -ServiceName $AppServiceName -Location $DataCenter -VMs $vms -VNetName $VNetSiteName -ErrorVariable errorVariable -ErrorAction SilentlyContinue | Out-Null
} 
else 
{ 
    #Deploy Virtual Machines to Virtual Network
    New-AzureVM -ServiceName $AppServiceName -VMs $vms -ErrorVariable errorVariable -ErrorAction SilentlyContinue | Out-Null
} 
 
if (!($?)) 
{ 
    throw “Unable to create virtual machines $AppServer1 and $AppServer2. Error detail is: $errorVariable” 
} 
else
{
    Write-Verbose “Successfully created virtual machines $AppServer1 and $AppServer2” 
}

# Add Internal Load Balancer to the service
Add-AzureInternalLoadBalancer -InternalLoadBalancerName AppILB -SubnetName AppSubnet -ServiceName $AppServiceName -ErrorVariable errorVariable -ErrorAction SilentlyContinue | Out-Null

if (!($?)) 
{ 
    throw “Unable to create internal load balancer on Service Name $AppServiceName. Error detail is: $errorVariable” 
} 
else
{
    Write-Verbose “Successfully created internal load balancer on Service Name $AppServiceName” 
}

# Add load balanced endpoints to ILB
Get-AzureVM -ServiceName $AppServiceName -Name $AppServer1 | Add-AzureEndpoint -Name “intappep” -LBSetName “intappeplb” -Protocol tcp -LocalPort 80 -PublicPort 80 -ProbePort 80 -ProbeProtocol tcp -ProbeIntervalInSeconds 10 -InternalLoadBalancerName AppILB | Update-AzureVM

if (!($?)) 
{ 
    throw “Unable to add internal load balanced endpoint to $AppServiceName and VM: $AppServer1.” 
} 
else
{
    Write-Verbose “Successfully added internal load balanced endpoint to $AppServiceName and VM: $AppServer1.” 
}

Get-AzureVM -ServiceName $AppServiceName -Name $AppServer2 | Add-AzureEndpoint -Name “intappep” -LBSetName “intappeplb” -Protocol tcp -LocalPort 80 -PublicPort 80 -ProbePort 80 -ProbeProtocol tcp -ProbeIntervalInSeconds 10 -InternalLoadBalancerName AppILB | Update-AzureVM 

if (!($?)) 
{ 
    throw “Unable to add internal load balanced endpoint to $AppServiceName and VM: $AppServer2.” 
} 
else
{
    Write-Verbose “Successfully added internal load balanced endpoint to $AppServiceName and VM: $AppServer2.” 
}

 

Once the application servers have been created you need to log into these servers, configure IIS and update the firewall rules to allow traffic over port 80.

You can use the following PowerShell script to create the database servers in DBSubnet. In real world scenario I would have used SQL Server Always on and internal load balancer but in this example I just create 2 VM’s from the Microsoft supplied SQL Server 2014 image.

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
############################ Provision SQL VM’s ##########################################################
$vm11 = New-AzureVMConfig -Name $DBServer1 -InstanceSize $InstanceSize -ImageName $sqlimage -AvailabilitySetName $DBAvailabilitySetName |  `
                Set-AzureSubnet $DBSubnet | `
                Add-AzureProvisioningConfig -Windows -AdminUsername $credential.GetNetworkCredential().username -Password $credential.GetNetworkCredential().password 
 
$vm12 = New-AzureVMConfig -Name $DBServer2 -InstanceSize $InstanceSize -ImageName $sqlimage -AvailabilitySetName $DBAvailabilitySetName |  `
                Set-AzureSubnet $DBSubnet | `
                Add-AzureProvisioningConfig -Windows -AdminUsername $credential.GetNetworkCredential().username -Password $credential.GetNetworkCredential().password 
 
 
# Make an array of the virtual machine configuration so we can create them with 1 call
$vms = @($vm11, $vm12) 

#check to see if the cloud service for these virtual machines already exists
$service = Get-AzureService -ServiceName $DBServiceName -ErrorAction SilentlyContinue            
    
if ($service -eq $null) 
{ 
    # Create a new cloud service and Deploy Virtual Machines to Virtual Network
    New-AzureVM -ServiceName $DBServiceName -Location $DataCenter -VMs $vms -VNetName $VNetSiteName -ErrorVariable errorVariable -ErrorAction SilentlyContinue | Out-Null
} 
else 
{ 
    #Deploy Virtual Machines to Virtual Network
    New-AzureVM -ServiceName $DBServiceName -VMs $vms -ErrorVariable errorVariable -ErrorAction SilentlyContinue | Out-Null
} 
 
if (!($?)) 
{ 
    throw “Unable to create virtual machines $DBServer1 and $DBServer2. Error detail is: $errorVariable” 
} 
else
{
    Write-Verbose “Successfully created virtual machines $DBServer1 and $DBServer2” 
}

 

Database Server 1 will be assigned an IP address of 192.168.0.36

Database Server 2 will be assigned an IP address of 192.168.0.37

Even though these virtual machines are created in different subnets there is nothing preventing these VM’s from communicating with each other. Database Servers are running SQL Server you need to open the open the port 1433 on the firewall to allow inbound connections.

Log into your web server and application server and install Telnet client on web and application servers using Server Manager –> Add Roles and Features->Telnet Client to test the connectivity between web/application and database servers.

From command windows run the command

telnet 192.168.0.36 1433

Here 192.168.0.36 is the internal IP address of the SQL Server VM where I had opened the firewall rule to allow inbound 1433 TCP connections. If the connection is successful you will see the cursor move to the top left corner of the command windows. If your connection fails you will get an error.

You will see that you can connect to the database server from both web server and application servers. If you want to only allow application servers to connect to database servers you have a few options:

1. You can use host firewall to only allow connections from application servers

2. You can use Azure Access Control Lists and only allow connections from application servers

3. You can use Network security groups at the VM level to only allow connections from application servers

4. You can use Network security groups at the subnet level to only allow connections from servers in the AppSubnet.

I prefer option 4 for ease of management. It is cumbersome to apply ACLs and Network Security groups at the VM level. If you create Network Security groups at the subnet level any new virtual machines will automatically inherit the security groups.

Network Security Groups

Here are the default inbound and outbound rules in an Network Security Group.

Default Inbound Rules

NamePrioritySource IPSource PortDestination IPDestination PortProtocolAccess
ALLOW VNET INBOUND65000VIRTUAL_NETWORK*VIRTUAL_NETWORK**ALLOW
ALLOW AZURE LOAD BALANCER INBOUND65001AZURE_LOADBALANCER****ALLOW
DENY ALL INBOUND65500*****DENY

Default Outbound Rules

Name

Priority

Source IP

Source Port

Destination IP

Destination Port

Protocol

Access

ALLOW VNET OUTBOUND

65000

VIRTUAL_NETWORK

*

VIRTUAL_NETWORK

*

*

ALLOW

ALLOW INTERNET OUTBOUND

65001

*

*

INTERNET

*

*

ALLOW

DENY ALL OUTBOUND

65500

*

*

*

*

*

DENY

To protect the servers in DBSubnet you need to create these rules to only servers in the AppSubnet to communicate with DBSubnet over port 1433.

Name

Priority

Source IP

Source Port

Destination IP

Destination Port

Protocol

Access

DBDENY

100

192.168.0.0/28

*

192.168.0.32/27

*

TCP

DENY

DBALLOW

101

192.168.0.16/28

*

192.168.0.32/27

1433

TCP

ALLOW

RDPALLOW

65500

INTERNET

*

192.168.0.32/27

3389

TCP

DENY

To protect the servers in AppSubnet you need to create these rules to only allow servers in WebSubnet to send requests to the AppSubnet.

Name

Priority

Source IP

Source Port

Destination IP

Destination Port

Protocol

Access

APPDENY

100

192.168.0.32/27

*

192.168.0.16/28

*

TCP

DENY

RDPALLOW

101

INTERNET

*

192.168.0.16/28

3389

TCP

ALLOW

WEBALLOW

102

192.168.0.0/28

*

192.168.0.16/28

80

TCP

ALLOW

The  following script creates network security groups and assigns them to DBSubnet and AppSubnet. It implements the rules shown in the tables above.

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
#Only allow traffic from AppSubnet to come to DBSubnet
#Create a Network Security Group
New-AzureNetworkSecurityGroup -Name “DBSG” -Location $DataCenter -Label “Security group for DBSubnet in virtual network $VNetSiteName in Central US”

#Add a rule to deny WebSubnet access to DBSubnet
Get-AzureNetworkSecurityGroup -Name “DBSG” | Set-AzureNetworkSecurityRule -Name DBDENY -Type Inbound -Priority 100 -Action Deny -SourceAddressPrefix ‘192.168.0.0/28’  -SourcePortRange ‘*’ -DestinationAddressPrefix ‘192.168.0.32/27’ -DestinationPortRange ‘*’ -Protocol TCP
#Add a rule to allow AppSubnet to the DBSubnet
Get-AzureNetworkSecurityGroup -Name “DBSG” | Set-AzureNetworkSecurityRule -Name DBALLOW -Type Inbound -Priority 101 -Action Allow -SourceAddressPrefix ‘192.168.0.16/28’  -SourcePortRange ‘*’ -DestinationAddressPrefix ‘192.168.0.32/27’ -DestinationPortRange ‘1433’ -Protocol TCP
#Add a rule to allow RDP Connections into the DBSubnet. Only do this if you want to allow RDP connections from internet
Get-AzureNetworkSecurityGroup -Name “DBSG” | Set-AzureNetworkSecurityRule -Name RDPALLOW -Type Inbound -Priority 102 -Action Allow -SourceAddressPrefix ‘INTERNET’  -SourcePortRange ‘*’ -DestinationAddressPrefix ‘192.168.0.32/27’ -DestinationPortRange ‘3389’ -Protocol TCP
#Assign the network security group DBSubnet
Get-AzureNetworkSecurityGroup -Name “DBSG” | Set-AzureNetworkSecurityGroupToSubnet -VirtualNetworkName $VNetSiteName -SubnetName $DBSubnet

#Only allow traffic from WebSubnet to come to AppSubnet
#Create a Network Security Group
New-AzureNetworkSecurityGroup -Name “APPSG” -Location $DataCenter -Label “Security group for APPSubnet in virtual network $VNetSiteName in Central US”
#Add a rule to deny DBSubnet access to AppSubnet
Get-AzureNetworkSecurityGroup -Name “APPSG” | Set-AzureNetworkSecurityRule -Name APPDENY -Type Inbound -Priority 102 -Action Deny -SourceAddressPrefix ‘192.168.0.32/27’  -SourcePortRange ‘*’ -DestinationAddressPrefix ‘192.168.0.16/28’ -DestinationPortRange ‘*’ -Protocol TCP
#Add a rule to allow WebSubnet access to AppSubnet
Get-AzureNetworkSecurityGroup -Name “APPSG” | Set-AzureNetworkSecurityRule -Name APPALLOW -Type Inbound -Priority 103 -Action Allow -SourceAddressPrefix ‘192.168.0.0/28’  -SourcePortRange ‘*’ -DestinationAddressPrefix ‘192.168.0.16/28’ -DestinationPortRange ’80’ -Protocol TCP
#Add a rule to allow RDP from internet to AppSubnet. Only do this if you want to allow RDP connections from internet
Get-AzureNetworkSecurityGroup -Name “APPSG” | Set-AzureNetworkSecurityRule -Name RDPALLOW -Type Inbound -Priority 103 -Action Allow -SourceAddressPrefix ‘INTERNET’  -SourcePortRange ‘*’ -DestinationAddressPrefix ‘192.168.0.16/28’ -DestinationPortRange ‘3389’ -Protocol TCP
#Assign the network security group AppSubnet
Get-AzureNetworkSecurityGroup -Name “APPSG” | Set-AzureNetworkSecurityGroupToSubnet -VirtualNetworkName $VNetSiteName -SubnetName $AppSubnet

 

Once you have applied Network Security Groups you will be unable to RDP to the virtual machine because default Network Security Groups do not allow traffic from the internet. I created rules to allow RDP into AppSubnet and DBSubnet.

RDP into a web server and try to access IIS over an internal ILB address 192.168.1.21 and you will see the default IIS web page.

Test connectivity from web server to a DBServer using telnet IP_Address 1433 and the connection will fail because DBSubnet only allows traffic from AppSubnet.

Log into an Application Server VM and test connectivity to DB Server using telnet IP_Address 1433 and you will be able to successfully connect.

In this blog post you have seen how you can leverage features like Azure virtual network, public and internal load balancer and network security groups to securely deploy an on premise application to Azure platform. Network security groups can control both inbound and outbound traffic at VM or subnet level. You  can have up to 200 rules in a Network Security Group. You cannot apply both an Access Control List(ACL) and Network Security Group(NSG) to the same virtual machine. A virtual machine/subnet can be only be controlled by 1 Network Security Group. Priority of NSG starts at 100 which is the lowest priority. Rules with the lower priority get executed first.

 

References

http://azure.microsoft.com/blog/2014/11/04/network-security-groups/

http://msdn.microsoft.com/en-us/library/azure/dn848316.aspx

http://msdn.microsoft.com/en-us/library/azure/dn655058.aspx

http://azure.microsoft.com/blog/2014/05/20/internal-load-balancing/

http://msdn.microsoft.com/en-us/library/azure/dn690121.aspx

This entry was posted in DevOps, Virtual Machines, Virtual Networks and tagged , . Bookmark the permalink.
  • ronit

    I am using azure backup services for my servers in azure. And I have
    applied NSG to the subnet and blocked the INTERNET where my VMs are deployed.

    But azure backup extension need access to azure datacenter IPs
    for successful backup.I have downloaded IPs from this link

    http://www.microsoft.com/en-us
    .Microsoft recommend to create a rule in
    NSG and allow the access to all IPs from server.

    I am using this rule: Get-AzureNetworkSecurityGroup -Name
    “AZURE_BACKUP” | Set-AzureNetworkSecurityRule -Name “Allow
    internet” -Type Outbound -Priority 347 -Action Allow -SourceAddressPrefix
    ‘10.0.0.0/29’ -SourcePortRange ‘*’
    -DestinationAddressPrefix ‘??????’ -DestinationPortRange ‘*’ -Protocol ‘*’.

    But the problem is that I don’t want to run this command for hundred of times
    as there are hundreds of IPs are there.I want to run this command once and it
    fetch all the IPs from xml file and allow access to following IPs.

    Is it possible.