Using Windows Firewall to restrict access to Windows Azure instances

Posted by: Tom Hollanders blog, on 07 May 2013 | View original | Bookmarked: 0 time(s)

Summary: If you ever have a need to restrict access to your Windows Azure deployment to known IP address ranges, you can do this by programmatically modifying the Windows Firewall. Youll need to do this both at startup, and whenever your role topology changes, as the Windows Azure guest agent also likes to modify firewall rules. This article is from Tom Hollanders blog; if youre reading it elsewhere please deduct 15 karma points from the hoster.

When you deploy your PaaS cloud service to Windows Azure, its on the internet. This means it can be accessed by anyone who is also on the internet, subject to your applications authorisation logic. Normally this is a good thingwe build and deploy apps so people can use them. However sometimes you may want to restrict access to certain known client machines. Some of the reasons Ive seen for this include:

  • Youre working on an unreleased, super-secret application that you dont want anyone to stumble upon until youre good and ready.
  • You have multiple deployments for different environments (e.g. development, test, UAT) which should each only be accessed by certain people but you dont want to change the apps authorisation logic.
  • Your application is only designed for internal users and you dont want to allow remote access. In this case you should also use a federated identity solution to authorise individual users, but if youre really paranoid you may want to prevent these users from accessing the app when away from the corporate network.
  • Even though youre happy for anyone to access the applications public UI, you want some additional security (beyond standard user authentication) for admin services, such as RDP or an administrative UI running on a different port.

If youre only interested in blocking traffic to IIS-hosted services, one option is to use IISs IP and Domain Restrictions feature to restrict access as described in Liam Cavanagh's Blog. This is a good option as the IP restrictions are configuration-based, and Windows Azure wont do anything that interferes with your settings. However as Liam points out, this capability depends on a Windows feature that isnt installed by default, so youll need to write a startup script to install the IP and Domain Restrictions feature.

However this solution will not work if you want to restrict access to something thats not IIS hosted, such as RDP. In this case you need to step it up a level by modifying the Windows Firewall which runs locally on each Windows Azure VM. Normally its pretty easy to configure Windows Firewall to only allow access to a given port to specific IP ranges: you set up a single rule for that port and set the scope to your IP ranges; any traffic from other IP addresses will be automatically blocked as there is no matching firewall rule. Unfortunately this is a bit more complex in Windows Azure, as the fabric will automatically configure a whole bunch of firewall rules (helpfully named after random GUIDs) that open certain ports to all IP addresses. You can still add additional rules if you want, but due to the way that firewall rules combine, you cant easily block access once another rule has already granted it.

The most practical option I found was to modify the existing firewall rules to add the IP range restrictions. There are a few different APIs to do this, but the easiest way is to use the Network Security PowerShell cmdlets. These run on Windows Server 2012, so make sure you set your Windows Azure osFamily attribute to 3. Heres a simple PowerShell script that modifies all existing rules for two ports (80 and 3389) to restrict them to specific IP addresses and ranges:

# SetFirewallRestrictions.ps1
$date = Get-Date
Write-Host "Updating firewall rule restrictions at " $date "'r'n"
$allowedRanges = ('3.2.1.0/24', '1.2.3.4') 
Get-NetFirewallPortFilter | ? LocalPort -eq '80' | Get-NetFirewallRule | Set-NetFirewallRule -RemoteAddress $allowedRanges
Get-NetFirewallPortFilter | ? LocalPort -eq '3389' | Get-NetFirewallRule | Set-NetFirewallRule -RemoteAddress $allowedRanges

To make it easier to call the script and log the output, lets also create a .cmd file to wrap it:

rem SetFirewallRestrictions.cmd
cd /d %~dp0
Powershell set-executionpolicy remotesigned -force
Powershell .\SetFirewallRestrictions.ps1 >> SetFirewallRestrictions.out.log 2>> SetFirewallRestrictions.err.log

So now we have our scripts, its just a matter of including them into our Windows Azure cloud service role and calling them at the appropriate time. The obvious place to do this is at role startup, so lets add a startup task to the ServiceDefinition.csdef file. Remember to run it as elevated since were playing with security settings. Also note the <Runtime> element which Ill get to in a moment.

<ServiceDefinition name="FirewallTest" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition" schemaVersion="2013-03.2.0">
  <WebRole name="WebRole1" vmsize="Small">
   ...  
    <Startup>
      <Task commandLine="Startup\SetFirewallRestrictions.cmd" executionContext="elevated" taskType="simple" />
    </Startup>
    <Runtime executionContext="elevated" />
  </WebRole>
</ServiceDefinition>

With the startup script in place, Windows Azure will automatically update the firewall settings with your IP range restrictions just as the instance starts. So were done, right? Actually not quite. After some testing, we found that our custom firewall rules were occasionally overwritten after the instance has been running for a while. After further investigation we discovered that the Windows Azure Guest Agent (an app which the fabric installs on your VM to carry out various cloudy tasks) recreates the firewall rules whenever the role is scaled yes, even on the instances that were already running. So in order to keep our IP address restrictions, we need to reapply our firewall script when the roles topology changes. Important: This solution is based on experimentation, not insider knowledge of exactly how this part of the platform works. Its possible that there are other times when the firewall rules are overwritten. Use this solution at your own risk and test thoroughly.

In order to reapply the firewall rules when the app is scaled, its necessary to subscribe to the RoleEnvironment.Changed event in your RoleEntryPoint-derived class. In the event handler we can launch the same script again. The <Runtime executionContext=elevated /> entry we put in above ensures that this code runs as admin.

public class WebRole : RoleEntryPoint
{
    public override bool OnStart()
    {
        RoleEnvironment.Changed += RoleEnvironment_Changed;
        return base.OnStart();
    }

    void RoleEnvironment_Changed(object sender, RoleEnvironmentChangedEventArgs e)
    {
        if (e.Changes.Any(ch => ch is RoleEnvironmentTopologyChange))
        {
            var processStartInfo = new ProcessStartInfo(@"Startup\SetFirewallRestrictions.cmd");
            var process = Process.Start(processStartInfo);
        }
    }
}

So far it looks like this is a solid option for restricting access to your Windows Azure applications to specific IP addresses. If you find any issues or have any suggestions please let me know, and Ill promise to do the same!

Category: IIS | Other Posts: View all posts by this blogger | Report as irrelevant | View bloggers stats | Views: 368 | Hits: 5

Similar Posts

  • Windows Azure How Do I videos for developers recently released more
  • Apple Safari for Windows and Microsoft Silverlight more
  • CSharp FAQ : What's New in the C# 2.0 Language and Compiler more

News Categories

.NET | Agile | Ajax | Architecture | ASP.NET | BizTalk | C# | Certification | Data | DataGrid | DataSet | Debugger | DotNetNuke | Events | GridView | IIS | Indigo | JavaScript | Mobile | Mono | Patterns and Practices | Performance | Podcast | Refactor | Regex | Security | Sharepoint | Silverlight | Smart Client Applications | Software | SQL | VB.NET | Visual Studio | W3 | WCF | WinFx | WPF | WSE | XAML | XLinq | XML | XSD