Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Hosted-services / Azure

Web Role Remote IP Filtering

5.00/5 (1 vote)
26 Jul 2012CPOL4 min read 21.1K   193  
Enforce IP address filtering for the web role remotely

Introduction 

In the following post I would like to demonstrate how to enforce ip address filtering for a web role using a simple security settings provided by the IIS web server. The goal of this post is just a proof of concept and should only be taken as a base for future work.

For our discussion let's consider that during production the devOP have noticed from the the iis logs that a certain client is trying to access the site in an abnormal way.Naturally we would like the azure application web role to be able to reject that client's IP.

In addition the azure application has an administration web site on a second web role that can be accessed using a custom port (for example 8080).To increase the security to the administration web site we would like that only requests originated from a specific company ip address(s) will be able to have access. 

In both cases IP address restriction is needed.

Background  

IIS provides a nice feature called IP Address and Domain Restriction.As the feature name hints one of it use is to block specific IPs that try to access a web site.In order to enable this feature on windows azure we can write a short startup script for our web role.To actually add the blocked client IPs list we should modify the web.config of that role.The problem is that those settings are hard coded in the web.config file and be loaded automatically once the web role starts.

Immediate question is raised... will I need to upgrade my azure application in order to apply a new IP filter list every time a pasting little client try to get my service down ?The answer is No.We can make the ip filtering settings more "remotely" using windows azure storage, which this post is all about.

The solution is fairly simple: we can modify the web.config security settings for the azure web role using information downloaded from a blob located in windows azure storage.This way we can add as many IPs to that blob text file as we want just by pressing a button on the web site in order to commit the changes and that is it the client will be band or grant access to our site.

Some gotcha to consider is that modifying the iis web.config settings at run-time will cause a restart to the iis process so we probably should consider storing the session data externally and expect small down time while enforcing the ip filtering rules.

One more thing to consider here is that modification of the web.config settings in runtime from any place that is not in the OnStart method of the web role, for example from a test web page, will need the default AppPool identity to be gained with higher privileges.I haven't included that in the solution but it could be done from code on the web role on start method(for testing purposes just set the AppPool identify of the web role to local system)  

Using the code

First step is to enable the ip filtering feature.Lets examine what need to be done on the Web Role ServiceDefinittion configuration file: 

C++
<?xml version="1.0" encoding="utf-8"?>
<ServiceDefinition name="ProtectedRole" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition" schemaVersion="2012-05.1.7">
  <WebRole name="WebRole1" vmsize="Small">
    <Runtime executionContext="elevated" />
    <Sites>
      <Site name="Web">
        <Bindings>
          <Binding name="Endpoint1" endpointName="Endpoint1" />
        </Bindings>
      </Site>
    </Sites>
    <Endpoints>
      <InputEndpoint name="Endpoint1" protocol="http" port="80" />
    </Endpoints>
    <Imports>
      <Import moduleName="RemoteAccess" />
      <Import moduleName="RemoteForwarder" />
      <Import moduleName="Diagnostics" />
    </Imports>
    <Startup>
      <!--Execute a startup batch file in order to will install the "IP and Domain Restrictions" role service-->
      <Task commandLine="Startup\startup.cmd" executionContext="elevated"/>
    </Startup>
    <ConfigurationSettings>
      <Setting name="StorageConnectionString" />
    </ConfigurationSettings>
  </WebRole>
</ServiceDefinition>

key settings to notice is that the web role will need to be run in elevated mode (in order to install the ip filtering feature) and a start-up task would need to be defined to actually install the IP filtering feature.

below is the start-up task content that will install the ip filtering feature:

@echo off @echo Installing and unlocking the IPv4 Address and Domain Restrictions feature %windir%\System32\ServerManagerCmd.exe -install Web-IP-Security %windir%\system32\inetsrv\AppCmd.exe unlock config -section:system.webServer/security/ipSecurity  

The next step is to load the default ip filtering from a blob the first time the web role starts. 

C#
public class WebRole : RoleEntryPoint
    {
        public override bool OnStart()
        {
            using (var server = new ServerManager())
            {
                string storageConnection = RoleEnvironment.GetConfigurationSettingValue("StorageConnectionString");
                CloudStorageAccount storageAccount = CloudStorageAccount.Parse(storageConnection);
                var blobClient = storageAccount.CreateCloudBlobClient();
                CloudBlobContainer container = blobClient.GetContainerReference("security");
                container.CreateIfNotExist();
                CloudBlob blob = container.GetBlobReference("ipfiltering.txt");
                bool isBlobExist = false;
                try
                {
                    blob.FetchAttributes();
                    isBlobExist = true;
                }
                catch (Exception e)
                {
                    Console.Write(e.Message);
                }

                if (isBlobExist)
                {
                    var siteNameFromServiceModel = "Web"; // update this site name for your site. 
                    var siteName = string.Format("{0}_{1}", RoleEnvironment.CurrentRoleInstance.Id, siteNameFromServiceModel);
                    var siteConfig = server.Sites[siteName].GetWebConfiguration();
                    var ipAddressSettings = siteConfig.GetSection("system.webServer/security/ipSecurity").GetCollection();
                    ipAddressSettings.Clear();

                    string ipFilteringSettings = blob.DownloadText();
                    using (StringReader sr = new StringReader(ipFilteringSettings))
                    {
                        string line;
                        
                        while ((line = sr.ReadLine()) != null)
                        {
                            if (line.StartsWith("#")) continue;
                            string[] settings = line.Split(' ');
                            ConfigurationElement newElement = ipAddressSettings.CreateElement("add");
                            newElement["ipAddress"] = settings[0];
                            newElement["subnetMask"] = settings[1];
                            newElement["allowed"] = settings[2];
                            ipAddressSettings.Add(newElement);
                        }
                    }
                    
                    server.CommitChanges();
                }
                else
                {
                    blob.UploadText("# ip filtering settings");
                }

            }

            return base.OnStart();
        }
    }

The code download a small text file and then modify the web.config IP security settings to applied the content.The following section in the web.config is the one that will be modified: 

<system.webServer>
    <modules runAllManagedModulesForAllRequests="true" />
    <security>
      <!--listed IP addresses will be denied access-->
      <ipSecurity>
                <clear />
                <add ipAddress="93.172.252.150" subnetMask="255.255.255.255" allowed="false" />
      </ipSecurity>
    </security>
  </system.webServer>

I've created a small page that display the blocked IPs content as downloaded from the blob file (pressing the load button): 

Image 1 

If we look closer at the code below we can noticed that when i press the save button the IPs will be stored in the blob file on azure storage and than the relevant IP filtering settings will be stored in the web.config file (in this phase a small IIS restart will occur in order to load the new web site config settings). 

C#
protected void btnSave_Click(object sender, EventArgs e)
        {
            string storageConnection = RoleEnvironment.GetConfigurationSettingValue("StorageConnectionString");
            CloudStorageAccount storageAccount = CloudStorageAccount.Parse(storageConnection);
            var blobClient = storageAccount.CreateCloudBlobClient();
            CloudBlobContainer container = blobClient.GetContainerReference("security");
            container.CreateIfNotExist();
            CloudBlob blob = container.GetBlobReference("ipfiltering.txt");
            try
            {
                blob.FetchAttributes();
                blob.UploadText(txtTest.Text);
                using (var server = new ServerManager())
                {

                    var siteNameFromServiceModel = "Web"; // update this site name for your site. 
                    var siteName = string.Format("{0}_{1}", RoleEnvironment.CurrentRoleInstance.Id, siteNameFromServiceModel);
                    var siteConfig = server.Sites[siteName].GetWebConfiguration();
                    var ipAddressSettings = siteConfig.GetSection("system.webServer/security/ipSecurity").GetCollection();
                    ipAddressSettings.Clear();

                    string ipFilteringSettings = txtTest.Text;
                    using (StringReader sr = new StringReader(ipFilteringSettings))
                    {
                        string line;

                        while ((line = sr.ReadLine()) != null)
                        {
                            if (line.StartsWith("#")) continue;
                            string[] settings = line.Split(' ');
                            Microsoft.Web.Administration.ConfigurationElement newElement = ipAddressSettings.CreateElement("add");
                            newElement["ipAddress"] = settings[0];
                            newElement["subnetMask"] = settings[1];
                            newElement["allowed"] = settings[2];
                            ipAddressSettings.Add(newElement);
                        }
                    }

                    server.CommitChanges();
                }
            }
            catch(Exception err)
            {
                Console.WriteLine(err.Message);
            }
        }

Finally to test that the IP filtering specified above work I've included my own IP, 403 error message confirm that. 

Image 2

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)