Web Role 远程 IP 筛选





5.00/5 (1投票)
远程强制对 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 错误消息确认了这一点。