A few weeks ago I was involved in a discussion about the Staging slot in Cloud Services. The staging slot typically contains the new version of your application which you are testing (and planning to release). If you worked with Cloud Services in the past you’ll know that a Cloud Service comes with 2 deployment slots:
- Production: http://myapp.cloudapp.net
- Staging: http://cddb883659d04d0bbbb570f17c52ea01.cloudapp.net
As you can see the staging URL is obfuscated and is (almost) impossible to guess. But what happens if someone gains access to this staging URL? That person might gain access to a version of the application containing critical bugs / security breaches or even information that shouldn’t be available just yet.
This issue can easily be solved by adding IP address restrictions to your Cloud Service. An example would be that you can only access the application from your home or from your company. And besides securing the staging deployment slot you could use it for other purposes, like restricting access to a WCF service, to the administrative portal of your application, to a Node.js app, …
What are my options?
The first option you can look at is the <ipSecurity> element in IIS. With this element you can restrict access to a web application or a specific path based on IP ranges and domain names. While this is probably a good option in most cases it’s not what I need. I’m actually looking for IP address restrictions on port level since I also want to secure resources which do not run in IIS.
Besides IIS I only see one viable option, being the Windows Firewall. A Web Role/Worker Role comes with the same Windows Firewall you have on your Windows 7/8 machine. If you open it (Start > Run > Firewall.cpl) you’ll see what is possible on-premises and in the cloud. You can grant access to specific applications, configure local and remote port ranges, configure remote addresses, … Let’s go and use the Windows Firewall to apply our IP address restrictions to our Cloud Services.
Automating the Windows Firewall
The most popular way to automate the Windows Firewall is by using netsh.exe, while this is great to do the basic stuff, we won’t be able to use it for what we need (more on that later). Now besides netsh.exe you also have the PowerShell Cmdlets. While these Cmdlets are very complete, I want to look at a third option.
Almost 3 years ago I blogged about a new feature in the .NET 4.0 Framework, the dynamic keyword. And the example in which I used it was… the Windows Firewall: The ‘dynamic’ keyword for higher productivity: Windows Firewall API example. In that blog post I showed how easy it was to use COM objects in your .NET application through the dynamic keyword:
Requirements for a good implementation?
Before we jump in it I think it’s important to take a moment and look at what we want to achieve. First of all, I think it’s important that we can secure a specific endpoint (let’s say restrict access to port 80 or to port 443) based on IP addresses or IP address ranges.
Next I think it’s also important that we can set these IP addresses through code or by using configuration. Finally, we should also be able to change these settings while our application is deployed. This could be useful when we want to give access to other people or when we want to move from staging to production (where we don’t want any IP address restrictions for example).
One last thing before we continue. Each time your instance starts it will look at the endpoints which have been configured for the Role and open the required ports in the Firewall. What we’ll do in our code is simply disable these rules and create new rules which are restricted to a few IP addresses / IP address ranges.
The core of this solution is the IPAddressRestrictionManager.cs class, that parses settings from the ServiceConfiguration.cscfg (which you can modify while the application is deployed) and modifies the Firewall on each instance. You can find the library with a sample application on GitHub or you can go ahead and use the NuGet package:
PM> Install-Package WindowsAzure.IPAddressRestriction
After adding the package to your WebRole/WorkerRole you’ll need to do some configuration. First, make sure the role is running in elevated mode (you can set this in the ServiceDefinition.csdef):
If you want to link the IPAddressRestrictionManager to your ServiceConfiguration you’ll need to add the following settings to your Role:
The syntax for the settings isn’t too hard to understand:
- IPAddressRestriction.Enabled = true or false
- IPAddressRestriction.Settings = <port>=<ip> or <port>=<ip-range-start>-<ip-range-end> (delimiter between ports is “;“)
Finally you need to hook everything up in your WebRole/WorkerRole.cs:
Adding this code to your WebRole/WorkerRole.cs will do a few things:
- Clean up previous changes
- Read the configuration from the cscfg
- If enabled: apply the restrictions to the Firewall
- Each time a setting was changed in the portal / through the Service Management API, re-initialize the Role
That’s it! For all of you on GitHub, don’t hesitate to fork the repo and add features as you see fit. There’s a lot more we can do here (sync rules with Table Storage, SQL, optimize code, …).
So first I set the configuration to only allow traffic from 18.104.22.168 to port 80. That’s clearly not my IP. The result:
Now I simply go to the portal and add my IP to port 80:
And here is the result, I now have access to port 80:
Now in order to remove the IP address restriction simply change the setting in the portal to false.