在本文中,我们将设置一个示例情况,展示如何使用开源Squid代理从Amazon虚拟私有云(VPC)中控制对Amazon简单存储服务(S3)的访问。首先,您将配置Squid以允许访问Linux Yum存储库。接下来,您将配置Squid,以限制对已批准的Amazon S3 bucket列表的访问。然后,您将配置Squid以根据URL直接流量,将一些请求发送到Internet网关(IGW),并将其他流量发送到虚拟专用网关(VGW)。最后,您将探索使Squid高度可用的选项。
为Amazon S3和Yum强制执行Squid访问策略
在我们的例子中,Alice是一家小型咨询公司的首席技术官(CTO)。尽管公司规模很小,但她的公司有很多高调的客户,爱丽丝一直努力赢得他们的信任。Alice采用了一组严格的防火墙策略,并在过去几年中部署了许多安全设备。
随着公司开始将应用程序迁移到云中,Alice的团队正在讨论如何使用Amazon Web Services (AWS)实现类似的策略。首先要做的是阻断互联网接入。开发人员应该不能从Internet上下载文件,除非有几个经过批准的场景。这些场景包括访问Yum存储库以更新Amazon Linux,以及使用AWS服务(如Amazon S3)。Alice计划通过在Amazon Elastic Compute Cloud (EC2)安全组中使用IP地址限制来实现这个策略。
Alice在AWS论坛上发现了许多帖子,人们询问Yum仓库和Amazon S3的IP地址范围。然而,亚马逊并没有公布这份名单。为什么?在云计算中,资源是高度弹性的。应用程序会根据需求增长或收缩。今天分配给一个应用程序的IP地址明天可能会分配给另一个应用程序。
随着应用程序的扩展和收缩,实例会被添加和删除,域名服务(DNS)会不断地使用新的IP地址更新。在云计算中,你不能依赖于基于IP地址的安全规则;因此,必须将安全策略建立在域名的基础上,因为它们不会随着应用程序的扩展而改变。但是,Amazon EC2安全组和网络访问控制列表(acl)不支持基于域名的规则。Alice需要找到另一个解决方案来实现她的安全策略。
部署和配置Squid
Alice决定使用开源web代理Squid来实现她的策略。Squid将允许访问一个已批准的服务列表,但拒绝所有其他互联网访问。(请注意,爱丽丝选择了乌贼,但她本可以选择许多解决方案。)
她首先创建如图1所示的VPC。
图1 - VPC配置为允许通过Squid代理访问互联网
如图1所示,Alice希望阻止从应用程序实例直接访问Internet。相反,应用程序实例必须通过Squid代理访问Internet。为了确保所有应用程序实例都使用代理,Alice使用图2表中所示的规则为应用程序子网创建了一个新的网络ACL。
注意,AWS同时提供安全组和网络acl来保护应用程序。安全组应用于实例;网络ACL应用于整个子网。Alice使用网络ACL确保规则适用于应用程序子网中部署的所有实例。有关安全组和网络acl的更多信息,请参阅Amazon VPC文档。
图2:应用程序子网ACL
图2中的ACL允许在VPC内使用HTTP/S(规则100和101),但是阻止HTTP/S到Internet(规则200和201)。因此,应用程序子网中的实例访问Internet的唯一方法是通过Squid代理。
注意,由于应用程序实例通过代理访问Internet,因此应用程序子网可以是私有的。专用子网没有到Internet的路由。有关公共和私有子网的更多信息,请参阅VPC文档。
接下来,Alice在DMZ子网中启动一个新的Amazon Linux AMI (Amazon机器映像),并给它分配一个弹性IP地址。然后,她使用以下命令安装Squid。
sudo yum update -y sudo yum install -y squid
安装Squid之后,她开始配置它。配置存储在/etc/squid/squid.conf的文本文件中。Alice使用vim编辑文件。
sudo vim /etc/squid/squid.conf
Squid使用称为ACLs的规则来识别流量。不要将Squid的acl与我们上面创建的Amazon EC2网络acl混淆。Alice遇到的第一个规则是src,它用于根据请求的源IP地址标识流量。换句话说,代理将只允许来自这些地址的请求。默认情况下,Squid将允许来自任何私人地址的请求。这是默认配置:
acl localnet src 10.0.0.0/8 acl localnet src 172.16.0.0/12 acl localnet src 192.168.0.0/16 acl localnet src fc00::/7 acl localnet src fe80::/10
Alice希望进一步限制VPC中仅包含实例的访问,因此她删除了这些规则,并创建了一个允许10.1.0.0/16请求的规则,这是VPC的无类域间路由(CIDR)范围。
acl localnet src 10.1.0.0/16 #Only allow requests from within the VPC
在只定义了源的情况下,Squid将允许访问任何URL。这是测试的好时机。Alice保存她的更改并启动Squid守护进程。
$ sudo service squid start
她打开到其中一个应用服务器的SSH会话,并将其配置为使用代理。
$ export http_proxy=http://10.1.1.10:3128 $ export https_proxy=http://10.1.1.10:3128 $ export no_proxy="169.254.169.254"
关于前一个命令,有一些重要的事情你需要知道:
这里使用的代理配置仅对当前会话有效。这对于测试来说没问题,但是要持久保存这些条目,您应该将它们添加到/etc/profile.d/proxy.sh中
大多数(但不是所有)应用程序将使用这些环境变量。有关配置代理服务器的详细信息,请查看应用程序文档。
默认情况下,Squid监听端口3128。您可以在squid.conf文件中更改端口。
169.254.169.254是Amazon EC2元数据服务。我们排除了这一点,因为我们希望实例直接命中元数据服务。如果我们代理这些请求,元数据服务将返回关于代理实例的信息,而不是发出请求的实例的信息
此时代理将允许访问任何URL。为了确保一切正常工作,Alice使用curl加载www.google.com。
$ curl -I http://www.google.com HTTP/1.1 200 OK ... Via: 1.0 ip-10-1-1-10 (squid/3.1.10)
响应代码200表示一切都按预期配置好了,而Via头表示应用程序实例正在使用代理访问Internet。一切似乎都在按照预期进行。
授予Yum访问权限
Squid安装并运行后,Alice继续执行她的安全策略。她转到Yum存储库。如图3所示,Alice希望允许对Yum存储库的访问,并拒绝所有其他Internet访问。
图3 - Squid拒绝访问Yum存储库以外的所有内容
Alice返回到Squid实例并打开Squid配置文件。
sudo vim /etc/squid/squid.conf
接下来,她在上一步中创建的源规则之后创建一组目标规则。这些规则定义实例可以访问哪些资源。Alice使用“dstdomain”规则匹配DNS名称。
每个地区有两个Yum url。如果一个区域不可用,Yum将尝试联系另一个区域。因此,Alice将所有区域添加到她的配置中。
acl yum dstdomain repo.us-east-1.amazonaws.com acl yum dstdomain repo.us-west-1.amazonaws.com acl yum dstdomain repo.us-west-2.amazonaws.com acl yum dstdomain repo.eu-west-1.amazonaws.com acl yum dstdomain repo.eu-central-1.amazonaws.com acl yum dstdomain repo.ap-southeast-1.amazonaws.com acl yum dstdomain repo.ap-southeast-2.amazonaws.com acl yum dstdomain repo.ap-northeast-1.amazonaws.com acl yum dstdomain repo.sa-east-1.amazonaws.com
acl yum dstdomain packages.us-east-1.amazonaws.com acl yum dstdomain packages.us-west-1.amazonaws.com acl yum dstdomain packages.us-west-2.amazonaws.com acl yum dstdomain packages.eu-west-1.amazonaws.com acl yum dstdomain packages.eu-central-1.amazonaws.com acl yum dstdomain packages.ap-southeast-1.amazonaws.com acl yum dstdomain packages.ap-northeast-1.amazonaws.com acl yum dstdomain packages.sa-east-1.amazonaws.com acl yum dstdomain packages.ap-southeast-2.amazonaws.com
既然定义了acl以匹配源和目标,Alice就可以更新访问规则。如前所述,默认访问规则只检查请求是否来自本地网络(在本例中是VPC)。
http_access allow localnet
Alice想检查源和目的地;因此,她更改了访问规则,以检查请求是否来自VPC,是否要转到Yum存储库。所有其他请求将被拒绝。她的规则是这样的:
http_access allow localnet yum
Alice保存她的更改并重新启动Squid守护进程。
$ sudo service squid restart
Alice准备测试新配置。她返回到应用程序实例。注意:请确保代理仍然被配置。
Alice再次测试对谷歌的访问,这一次她得到了预期的403禁止错误。注意下面的X-Squid-Error头文件。这表明Squid拒绝了请求,而不是web服务器。
$ curl -I www.google.com HTTP/1.0 403 Forbidden ... X-Squid-Error: ERR_ACCESS_DENIED 0
接下来,Alice尝试Yum URL以确保它可以正常工作。
$ curl -I http://repo.us-east-1.amazonaws.com/latest/main/mirror.list HTTP/1.1 200 OK
最后,Alice对Yum进行了测试,并显示它按预期工作。
$ yum check-update Loaded plugins: priorities, update-motd, upgrade-helper Security: kernel-3.14.20-20.44.amzn1.x86_64 is the currently running version
授予访问Amazon S3的权限
随着Yum的工作,Alice转向了Amazon S3。如图4所示,她希望允许访问Yum存储库和Amazon S3。鱿鱼将继续阻止访问所有其他url。
图4 -允许访问Yum仓库和Amazon S3存储桶的Squid
Amazon S3支持两种类型的url:路径和虚拟主机。路径URL的格式是https://s3.amazonaws.com/mybucket,而虚拟主机URL的格式是https://mybucket.s3.amazonaws.com/。有关更多信息,请参阅Amazon S3文档。
为了支持这两种URL类型,Alice使用了正则表达式。例如,所有域名在美国标准将以“s3.amazon.com”结束,不管URL类型。
Alice返回Squid实例,打开配置文件。
sudo vim /etc/squid/squid.conf
Alice计划支持所有AWS区域,因此她为每个区域添加了一行代码。
acl s3 dstdom_regex .*s3\.amazonaws\.com acl s3 dstdom_regex .*s3\.eu-central-1\.amazonaws\.com acl s3 dstdom_regex .*s3\.sa-east-1\.amazonaws\.com acl s3 dstdom_regex .*s3\.ap-northeast-1\.amazonaws\.com acl s3 dstdom_regex .*s3\.eu-west-1\.amazonaws\.com acl s3 dstdom_regex .*s3\.us-west-1\.amazonaws\.com acl s3 dstdom_regex .*s3\.us-west-2\.amazonaws\.com acl s3 dstdom_regex .*s3\.ap-southeast-2\.amazonaws\.com acl s3 dstdom_regex .*s3\.ap-southeast-1\.amazonaws\.com
Alice还添加了一个新的访问规则,允许从VPC到Amazon S3的请求。Yum规则仍然存在,访问规则如下所示:
http_access allow localnet yum http_access allow localnet s3
Alice保存她的更改并重新启动Squid守护进程。
$ sudo service squid restart
返回到应用程序实例,Alice尝试使用路径和虚拟主机url的Amazon S3 bucket,并看到两者都如预期的那样工作。如果要启动新的SSH会话,请记住配置环境变量。
$ curl -I https://mybucket.s3.amazonaws.com/test.txt HTTP/1.1 200 OK
$ curl -I https://s3.amazonaws.com/mybucket/text.txt HTTP/1.1 200 OK
最后,Alice测试来自AWS CLI的访问。一切都很好。
$ aws s3 ls s3://mybucket 2014-10-22 21:32:48 0 2014-10-22 21:38:27 15 test.txt
桶的白名单( Whitelisting Buckets)
爱丽丝对结果非常满意,但她想更进一步。目前,Squid允许访问任何AWS客户拥有的任何Amazon S3存储桶。如图5所示,Alice希望只限制团队需要访问的桶(例如,mybucket)的访问,并阻止对任何其他桶的访问。
图5 -允许访问特定S3桶的Squid
Alice返回到Squid实例并再次打开配置文件。她创建了两个新的acl,它们标识存储在US标准区域中的“mybucket”。她必须创建两个规则,分别对应上面讨论的每种URL类型。
acl virtual_host_urls dstdomain mybucket.s3.amazonaws.com acl path_urls url_regex s3\.amazonaws\.com/mybucket/.*
第一个ACL标识虚拟主机样式的URL,并使用前面看到的dstdomain。这里不需要正则表达式,因为Alice知道确切的主机头。第二个ACL标识路径样式的URL,并使用url_regex匹配以“s3.amazonaws.com/mybucket/”开头的URL。
现在,Alice找到了她之前创建的规则。
http_access allow localnet s3
然后,她用两个新规则替换规则(每个ACL一个)。完整的访问规则列表现在看起来是这样的:
http_access allow localnet yum http_access allow localnet virtual_host_urls http_access allow localnet path_urls
接下来,她重新启动Squid守护进程,以便再次进行测试。
$ sudo service squid restart
Alice返回到她的应用程序实例并测试对mybucket的访问。一切似乎都在按照预期进行。
$ aws s3 ls s3://mybucket 2014-10-22 21:32:48 0 2014-10-22 21:38:27 15 test.txt
现在我们要讨论一个HTTPS url的问题。AWS CLI和大多数其他工具使用HTTPS。请注意,在使用HTTPS进行测试时,虚拟主机风格的URL可以工作:
$ curl -I https://mybucket.s3.amazonaws.com/test.txt HTTP/1.1 200 OK
但是,路径样式URL返回一个403。为什么?
$ curl -I https://s3.amazonaws.com/mybucket/text.txt HTTP/1.0 403 Forbidden X-Squid-Error: ERR_ACCESS_DENIED 0
这失败了,因为Alice创建的路径样式ACL需要看到整个URL,其中一部分位于加密的HTTPS包中。在虚拟主机URL中,所有信息都在主机名中(参见图6)。在path样式URL中,路径(包括桶名)是加密的(参见图7)。
Figure 6 - Virtual host style URL sent over SSL
图7 -通过SSL发送的路径样式URL
Squid使用一种称为SSL Bump的特性来解密请求。SSL Bump超出了本文的范围,但是您可以在Squid的网站上阅读更多内容。幸运的是,AWS CLI使用虚拟主机url并按预期工作,不需要解密SSL。
控制Squid的出站接口
到目前为止,Alice对她所设置的安全措施感到非常兴奋。但是,在项目的前几周,团队确定了他们需要访问的其他资源。更新规则变得单调乏味。所有这些异常都是很久以前在数据中心发现的。Alice希望利用现有的基础设施。
Alice决定添加一个虚拟专用网关(VGW)来将VPC连接到她公司的数据中心。VGW就绪后,她可以配置VPC,使其通过已经定义了现有安全策略的数据中心发送所有HTTP/S请求。但是,Alice不希望通过VPN隧道发送Amazon S3请求,从而增加应用程序的延迟。
Alice希望有一种解决方案,它利用VGW向数据中心发送大多数请求,但允许她识别应该使用Internet网关低延迟访问特定服务的特殊情况。因此,她重新配置了VPC,如图8所示。
Figure 8 - Squid directing traffic to an IGW or VGW
在这个新设计中,Alice增加了一个资源子网。这个子网有一个新的路由表,默认路由指向VGW而不是IGW。她还向Squid代理(如图8所示,IP地址为10.1.2.10)添加了一个弹性网络接口(ENI),并将其放在资源子网中。当Squid代理将请求发送到10.1.1.10接口时,VPC将请求路由到IGW。当Squid代理将请求发送到10.1.2.10接口时,VPC将请求路由到VGW。
Alice没有拒绝请求,而是重新配置了Squid代理以允许所有请求,但根据URL将它们发送到两个接口中的一个。Yum和S3的请求将退出10.1.1.10接口,并被路由出IGW。所有其他流量将退出10.1.2.10接口,并通过VPN隧道路由到数据中心。请求在数据中心之后,现有的基础设施可以决定如何处理每个请求(由图8中标记为“TBD”的两条黄线表示)。
Alice再次返回到Squid配置文件。首先,她用允许来自VPC的所有流量的访问规则替换访问规则。她删除了以下项目:
http_access allow localnet yum http_access allow localnet virtual_host_urls http_access allow localnet path_urls
她补充了以下内容:
http_access allow localnet
请注意,这与我们在本文开头使用的规则相同。现在代理将再次允许来自VPC中任何位置的任何流量,而不管目的地是什么。Squid不会拒绝该流量,而是将其转发给公司的数据中心,并允许现有的基础设施决定如何处理它。
接下来,Alice配置输出地址。如果请求的目的地是Yum储存库或她的Amazon S3存储桶,那么它将使用IP地址为10.1.1.10的接口发送到Internet网关。如果没有,则使用IP地址为10.1.2.10的接口将其发送到虚拟专用网关。
tcp_outgoing_address 10.1.1.10 localnet yum tcp_outgoing_address 10.1.1.10 localnet virtual_host_urls tcp_outgoing_address 10.1.1.10 localnet path_urls tcp_outgoing_address 10.1.2.10 localnet
有了这些规则,Alice可以低延迟访问Amazon S3,同时确保基于云的应用程序遵循与数据中心托管的应用程序相同的安全策略。
高可用性
Squid已经成为Alice应用程序不可或缺的一部分,Alice的应用程序依赖于Squid来访问存储在Amazon S3中的数据。Alice想要确保鱿鱼溶液是高度可用的。有几个方法可以解决这个问题。
在前一篇文章中讨论的一种解决方案是,在一个私有弹性负载平衡器(ELB)后面的自动伸缩组中驻留多个Squid实例。不幸的是,爱丽丝的公司很小,她的预算也很紧张。她不想支付多个鱿鱼实例和ELB。
Alice决定使用单个Squid实例,如图9所示。她将这个实例放在一个自动缩放组中,最小值和最大值都为1。如果Squid实例——甚至整个可用性区域——发生故障,自动扩展组将用一个新实例替换它。
图9 -使鱿鱼高度可用
这增加了明显的复杂性。当自动缩放组替换一个Squid实例时,应用程序实例需要开始使用新的Squid实例。Alice使用Amazon Route 53创建一个DNS条目(例如,proxy.example.com)来引用代理实例。亚马逊Route 53是亚马逊的高可用性和可扩展的DNS服务。
应用程序实例将使用DNS名称引用Squid实例,而不是使用如下所示的IP地址。现在,当一个Squid实例失败时,Alice只需要更新DNS条目,并且应用程序实例都将开始使用新的Squid实例。
$ export http_proxy=http://proxy.example.com:3128 $ export https_proxy=http://proxy.example.com:3128 $ export no_proxy="169.254.169.254"
为了使这个过程自动化,Alice创建了如下所示的简单shell脚本。每当启动一个新的Squid实例时,这个脚本将更新Amazon Route 53。她将此脚本添加到自动调整组的启动配置的用户数据部分。
#!/bin/bash cat < ~/dns.conf { "Comment": "string", "Changes": [ { "Action": "UPSERT", "ResourceRecordSet": { "Name": "proxy.example.com", "Type": "A", "TTL": 60, "ResourceRecords": [ { "Value": "$(curl http://169.254.169.254/latest/meta-data/local-ipv4/)" } ] } } ] } EOF aws route53 change-resource-record-sets -hosted-zone-id "ZONEID" -change-batch "file://~/dns.conf"
这个脚本将确保DNS条目始终指向最近启动的Squid实例。该脚本使用Amazon EC2元数据服务来发现它正在运行的实例的IP地址。然后它调用Amazon Route 53 API来更新DNS条目。
注意,您必须使用您想要更新的Amazon Route 53托管区域的ID替换ZONEID。此外,您的实例必须使用Amazon EC2角色,该角色具有更新Route 53的权限。
配置了自动伸缩组后,Alice可以确保她的应用程序可以从Squid实例的故障中恢复过来。
结论
Alice已经了解到云本身是弹性的,她不能依赖于保持静态的IP地址。在过去,她基于IP地址和CIDR块构建安全规则。在云中,她需要考虑基于DNS名称的安全规则。
Alice部署了一个Squid代理来控制对Yum存储库和Amazon S3的访问。Squid可以用于访问所有的Amazon S3或特定的bucket。它还可以用于根据策略引导流量遵循不同的路径。
Alice能够在AWS上托管她的应用程序,并利用公司现有的安全基础设施。此外,她还使用Amazon Route 53和自动伸缩构建了一个高度可用的解决方案。