65.9K
CodeProject 正在变化。 阅读更多。
Home

Web Role 远程 IP 筛选

starIconstarIconstarIconstarIconstarIcon

5.00/5 (1投票)

2012年7月26日

CPOL

4分钟阅读

viewsIcon

21464

downloadIcon

237

远程强制对 Web Role 进行 IP 地址筛选

介绍 

在接下来的文章中,我想演示如何使用 IIS Web 服务器提供的简单安全设置来实施 Web Role 的 IP 地址过滤。这篇文章的目标仅仅是一个概念验证,应该仅作为未来工作的基础。

对于我们的讨论,让我们考虑一下,在生产过程中,开发运维人员从 IIS 日志中注意到某个客户端正在以异常方式访问网站。自然地,我们希望 Azure 应用程序 Web Role 能够拒绝该客户端的 IP。

此外,Azure 应用程序在第二个 Web Role 上有一个管理网站,可以使用自定义端口(例如 8080)访问。为了提高管理网站的安全性,我们希望仅允许来自特定公司 IP 地址的请求进行访问。

在两种情况下,都需要 IP 地址限制。

背景  

IIS 提供了一个名为 **IP 地址和域限制** 的好功能。正如该功能名称所暗示的那样,它的一个用途是阻止尝试访问网站的特定 IP。为了在 Windows Azure 上启用此功能,我们可以为我们的 Web Role 编写一个简短的启动脚本。要实际添加被阻止的客户端 IP 列表,我们应该修改该 Role 的 web.config 文件。问题是这些设置在 web.config 文件中是硬编码的,并且一旦 Web Role 启动就会自动加载。

一个直接的问题是... 我是否需要升级我的 Azure 应用程序,以便在每次出现一个小的客户端尝试让我的服务停机时应用新的 IP 过滤列表?答案是否定的。我们可以使用 Windows Azure 存储使 IP 过滤设置更“远程”,这就是这篇文章的全部内容。

解决方案非常简单:我们可以使用从位于 Windows Azure 存储中的 Blob 下载的信息来修改 Azure Web Role 的 web.config 安全设置。这样,我们只需通过按一下网站上的按钮来提交更改,就可以将任意数量的 IP 添加到该 Blob 文本文件中,这样客户端将被禁止或被授予对我们网站的访问权限。

需要考虑的一些问题是,在运行时修改 IIS web.config 设置将导致 IIS 进程重启,因此我们可能应该考虑在外部存储会话数据,并预计在强制执行 IP 过滤规则时会有很短的停机时间。

这里需要考虑的另一件事是,在运行时从任何不在 Web Role 的 OnStart 方法中的位置(例如,从测试网页)修改 web.config 设置,将需要使用更高的权限来获取默认的 AppPool 身份。我没有将此包含在解决方案中,但可以从 Web Role 的启动方法中的代码中完成(为了测试目的,只需将 Web Role 的 AppPool 身份设置为本地系统)

使用代码

第一步是启用 IP 过滤功能。让我们检查一下 Web Role ServiceDefinition 配置文件中需要做什么:

<?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>

需要注意的关键设置是,Web Role 需要在提升模式下运行(以便安装 IP 过滤功能),并且需要定义一个启动任务来实际安装 IP 过滤功能。

下面是将会安装 IP 过滤功能的启动任务内容

@echo off
@echo 正在安装和解锁 IPv4 地址和域限制功能
%windir%\System32\ServerManagerCmd.exe -install Web-IP-Security
%windir%\system32\inetsrv\AppCmd.exe unlock config -section:system.webServer/security/ipSecurity

下一步是从 Blob 中加载默认的 IP 过滤,首次 Web Role 启动时。

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();
        }
    }

代码下载一个小文本文件,然后修改 web.config IP 安全设置以应用内容。web.config 中的以下部分将被修改:

<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>

我创建了一个小页面,用于显示从 Blob 文件下载的被阻止的 IP 内容(按下加载按钮):

 

如果我们仔细查看下面的代码,我们可以注意到,当我按下保存按钮时,IP 将存储在 Azure 存储上的 Blob 文件中,然后相关的 IP 过滤设置将存储在 web.config 文件中(在此阶段,将发生一个小型的 IIS 重启,以便加载新的网站配置设置)。

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);
            }
        }

最后,为了测试上面指定的 IP 过滤是否有效,我包含了我的 IP,403 错误消息确认了这一点。

© . All rights reserved.