在充满敌意的环境中部署安全服务器,第二部分
在我的上一篇文章中,我开始了关于在 Amazon EC2 上生成安全服务器所面临的一些挑战的系列文章。在那篇文章中,我讨论了与传统基础设施相比,EC2 在安全性方面提出的一些总体挑战,并详细说明了我是如何配置安全组和管理密钥的。在本文中,我将完成该主题,并介绍我在将服务器部署到 EC2 时采取的其他一些实践。与前一篇文章一样,尽管我的示例是针对 EC2 的,但在大多数情况下,您可以轻松地将这些实践应用于任何云环境,甚至您自己的基础设施。
Puppet 特定实践我知道每个人都有自己喜欢的配置管理系统。暂且不引发口水战,我们使用 Puppet 进行配置管理,因此我认为强调一些与 Puppet 本身相关的安全实践是有价值的。我将把所有 Puppet 特定的技巧放在本节中,如果您对此不感兴趣,可以跳到下一节。
Puppet 部署中首先要介绍的是如何生成服务器。我非常喜欢使用现成的 Amazon AMI,而不是自己滚动构建。维护我自己的系统镜像会增加很多开销,尤其是在情况发生变化时,我更愿意让 Debian 团队来管理这项工作。我采用官方 Debian AMI,并添加一个 userdata 脚本来安装 Puppet,仅此而已。
在客户端/服务器模型中,当客户端首次尝试签入 Puppetmaster 时,它将出示它生成的证书,并请求 Puppetmaster 对其进行签名。一旦 Puppetmaster 签名,它将信任出示该证书的客户端确实是其声称的身份。尽管 Puppet 允许自动签名所有证书请求,但这通常被认为是不安全的做法。相反,我所做的是让我的生成脚本启动另一个简单脚本,该脚本仅检查我在接下来的十分钟内为我生成的主机是否有新证书。如果主机在这十分钟窗口内签入,则会获得签名;否则,脚本将退出。通过这种方式,我为攻击者创建了一个非常小的窗口来冒充服务器,即使这样,我也会在 Puppetmaster 上看到我未签名的重复证书请求。这使我获得了自动签名的大部分好处,而没有风险。
我知道很多 Puppet 倡导者赞成在没有 Puppetmaster 的情况下部署 Puppet。在该模型中,Puppet 配置被运送到主机,并且它们直接应用这些配置。尽管这种方法可能有一些好处,但它也有一些缺点。首先,您失去了轻松部署诸如 hiera-gpg 之类的东西的能力。对我来说更重要的是,您失去了每个 Puppet 客户端都生成了一个有效的内部证书的便利功能,该证书已由内部受信任的 CA(Puppetmaster)签名。每当我需要在任何服务器上设置 TLS 时,我都会大量重复使用这些证书,并且我们要求所有外部网络服务都使用 TLS。当另一个非 root 服务需要访问证书(例如 Nginx 服务器或 Postgres)时,我只需复制一份证书,以便特定服务可以看到,并将其存储在文件系统上的其他位置。
我们还修改了 Puppet,以便除了每个证书已经拥有的标准 hostname.domain 之外,证书还使用 role.domainname 的 Subject Alt Name 生成。因为我们集群化了内部服务以实现容错,所以我们按角色引用服务,并且该角色实际上可能指向三个或更多服务器,每个服务器都有自己的域名。通过将角色名称作为 Subject Alt Name 添加到证书,可以很容易地为所有内部服务重复使用这些证书。
管理动态 IP老实说,动态 IP 很麻烦。话虽如此,如果您使用的是公共 EC2 服务,那么您别无选择。所有 IP 都是动态分配的,并且在您停止服务器时会发生更改。这为环境增加了一个额外的复杂性级别,因为您事先不知道服务器将具有哪个 IP。为了应对这种情况,我设置了一个内部 DNS 服务器,该服务器已配置为允许动态 DNS 更新。但是,为了让主机更新其 DNS 记录,它需要有一个共享密钥,该密钥在 DNS 服务器上生成并导出到 Puppet。一旦 Puppet 开始配置服务器,它就会创建包含密钥的本地密钥文件,并构建一个本地 nsupdate 脚本,该脚本会更新主机需要的任何记录。从那时起,每次 Puppet 运行时,主机都会确认其所有动态 DNS 记录都是准确的。我还添加了一个钩子,以便当我停用主机时,Puppet 将删除所有这些记录。
总体最佳实践我还在实施其他一些常规安全实践。首先,正如我之前提到的,因为每个主机都有一个由内部受信任的 CA 为 Puppet 签名的证书,所以我们利用这些证书来要求主机之间所有网络通信都使用 TLS。鉴于您正在与其他 EC2 主机共享网络,您需要确保没有人可以读取您的流量,因为它会通过此网络传输。此外,TLS 的使用有助于我们避免中间人攻击。
我们还对软件包管理采取了其他步骤。我们托管内部软件包存储库,这些存储库仅镜像我们实际使用的上游软件包。我们对创建的任何内部软件包进行签名,并且主机在安装软件包之前会验证签名。最后,我们采取额外的步骤来限制对我们环境的访问。特别是我们的生产环境仅限于少数系统管理员访问,并通过单独的 VPN 访问。我们要求基于密钥的 SSH 身份验证,并且有一项内部策略要求系统管理员使用密码保护他们的 SSH 密钥(请阅读我在 2013 年 12 月号的“秘密特工”文章,了解有关 SSH 代理和受密码保护的密钥管理的一些技巧)。
尽管这不是我使用的(或您应该使用的)安全实践的完整列表,但希望它足以让您大致了解如果您想在云中托管安全环境,您应该考虑哪些类型的因素。