这是系列博客中的第三篇,涵盖了我们构建和运营SaaS服务的各个方面:
我最初的博客提供了一些导致我们构建新分布式云平台的需求背景。 利用这个平台,我们的客户可以构建一套复杂多样的应用——比如智能制造、公共安全视频取证、算法交易和电信 5G 网络。
由于其中许多应用都是关键任务的,我们的客户期望我们不仅提供多层安全性,而且还能够不断改进以确保其分布式集群的安全。 这个特定的博客将解决跨多个集群的基础设施、应用和数据的安全挑战。
如上所述——保护用户访问应用(例如,访问我们的银行账户或电子邮件)的问题是众所周知的。 然而,对于应用程序到应用程序或应用程序到数据的访问,执行相同操作并不那么简单,因为验证过程不需要人工参与。
为了使应用以安全的方式访问另一个资源(例如,存储在对象存储中的数据或对另一个应用进行 API 调用等),需要发生以下情况:
作为身份验证过程的一部分,请求应用可以 PKI 证书或秘密(例如密码)或密钥的形式显示其身份。 使用秘密或密钥进行身份认证时需要解决两个问题:
以可靠且可重复的方式执行这些安全操作(用于应用程序到应用程序的交互)并非易事。 有很多原因导致这一点并不简单:
因此,在动态环境中保护基础设施、应用和数据是一个非常具有挑战性的问题。 虽然云提供商已经迎接这一挑战并提供了大量工具来解决这些问题,但由于以下原因,集成和维护这些问题并不容易:
虽然许多企业都是单一云提供商,并且他们可能愿意投入资源来管理和增强该提供商的安全原语,但我们在异构环境(混合云和边缘)中运营,必须构建新的解决方案来解决这些问题。
我们的团队的任务是为可能驻留在分布式基础设施中的应用、基础设施和数据提供安全保障,如图 1 所示。
因此,我们的平台团队决定构建新的软件服务,整合以下四个方面,以跨边缘、任何云和我们的网络 PoP 提供平台安全:
身份是一个根本问题,因为一旦身份问题解决了,许多安全挑战就可以更容易地应对。 然而,要解决身份问题,我们需要定义身份的含义以及如何以可靠的方式发布身份。 加密极客喜欢对一切事物都有自己的看法,身份的定义也不例外:
由受信任的机构通过非委托的安全协议以加密方式认证的独特的、完整的不可伪造且可通过加密方式验证的特征集,这些特征构成了对某人或某物的已知或考虑。
本质上,我们需要的是一个不可伪造的、可通过加密验证的安全身份。 颁发这样的身份认证很有挑战性,原因有二: 1)身份引导和 2)信任根
在与身份相关的话题中,有几种经常被讨论的方法——SPIFFE 和 Hashicorp Vault。 我们要澄清的是,SPIFFE 是一种命名方案,可以在我们的系统中用作身份证件(X.509 证书)——但是,该格式不适用于包含二进制数据的某些身份属性。 虽然 Vault 可用于颁发身份证明文件,但它并不能解决身份引导和信任根问题的挑战:
例如,当在 AWS 中启动虚拟机时,它会被赋予一个引导标识,并且 AWS 的元数据服务充当信任根。 身份证明文件(由 AWS 使用其自己的加密密钥进行签名)如下所示:
虽然instanceId可能表示已启动的应用实例的唯一标识,但它需要链接到某个众所周知的名称 (myserver.acmecorp.com),其他应用将使用该名称与该特定实例进行通信。 因此,即使这个 AWS 引导身份文档是不够的,但可以用来发布另一个身份,供应应用用于与另一个应用进行通信。
如前所述,对于我们来说,提供一种身份至关重要,它允许应用跨云提供商和/或边缘位置进行通信和共享信息(图 2)。 这意味着我们必须构建一个适用于所有这些环境的身份引导和信任根系统。
由于我们使用 Kubernetes 来管理和编排应用(微服务和虚拟机),这意味着每个启动的 Pod 的唯一身份的引导必须与 Kubernetes 的 Pod 创建过程挂钩。 图 3 展示了我们如何使用 K8s webhook 机制来挂钩 pod 创建过程。 这个名为 Voucher 的 webhook 会将一个名为Wingman的安全 sidecar 注入到集群中创建的所有 Pod 中,同时提供可用作信任根的必要加密签名信息。 此 webhook 提供了一个短期签名令牌,Wingman 使用它从 Volterra 的 SaaS 后端中的身份颁发机构请求 X.509 证书。
身份认证机构强制执行身份认证规则,以最小化“爆炸半径”,以防某个 K8s 集群受到威胁。 许多其他依赖于通用根 CA 或 K8s 集群联合的解决方案无法限制爆炸半径,这是我们设计决策的重要输入。
对于 K8s 中给定的 Pod,属性可以在 Pod 创建后发生变化 — — 例如,Pod 在创建后可以附加到新服务。 这意味着身份证书必须使用新服务进行更新。 Wingman 持续监视跟踪 K8s API 服务器的 Voucher,以获取任何此类更新。
该机制为我们平台上运行的每个应用实例提供了唯一且通用的全局标识,无论它是我们自己的工作负载还是客户的工作负载。 然后使用这个唯一的身份和边车(Wingman)来保护分布式系统中的所有通信、访问和密钥/秘密。
每个 Pod 拥有唯一的身份是一个很好的开始,因为它简化了在通信服务之间实现相互认证的任务。 我们的底层基础设施由许多不同的服务组成,这些服务运行在不同协议上,例如 gRPC、REST、IPSec、BGP 等。 从早期开始,团队就设定了一个目标,即实现所有通信方无论使用何种协议,都能实现相互认证和通信安全(加密通道)。 这也意味着我们不能将我们的身份与仅限于一组特定协议(例如 HTTP/TCP 与)的解决方案(例如 Istio)绑定在一起。 基于IP)。
由于我们的平台还允许客户运行他们选择的工作负载,我们预计这些工作负载可以运行各种协议,并且平台不应该通过提供仅限于特定协议集的身份来限制它们的能力。 相反,身份验证与身份分离(通过 Wingman),这使得连接到各种服务网格技术成为可能。 我们的服务网格边车/数据平面(在之前的博客中介绍过)使用此身份为客户工作负载提供 mTLS。
由于我们自己的许多基础设施服务都是使用 Volterra Golang 服务框架编写的(将在未来的博客中讨论),因此我们决定将使用身份的逻辑(来自 Wingman)直接构建到框架中。 这有助于我们的开发人员开箱即用地保护他们的服务到服务通信,而无需依赖服务网格边车进行 mTLS。
实现相互认证的安全通道后的下一个合乎逻辑的步骤是授权——请求的接收者(服务器)确定是否允许来自已识别调用者(客户端)的请求的过程。 不允许请求的原因可能有很多——配额限制、权限等等。 由于这些原因及其阈值在动态变化,因此一组硬编码的授权规则(策略)对于我们的平台来说是行不通的。
我们决定使用 Open Policy Agent 的引擎作为起点,并在 Wingman sidecar 中构建一个授权包装器。 该包装器代码动态地获取相关策略并保持其热度以便快速评估。 与身份验证类似,将授权引擎与身份(和身份验证)分离使我们能够在请求处理的多个阶段实施授权策略,包括深入业务逻辑,而不仅仅是在身份验证之后立即实施。
由于 Wingman 插入到所有工作负载(包括客户的工作负载)中,因此我们的平台提供了授权引擎作为内置功能。 尽管开放策略代理 (OPA) 是基于一种名为 Rego 的强大语言构建的,但我们希望向开发人员和客户隐藏它的复杂性。 我们平台上的所有策略也可以使用更容易理解和直观的策略结构来定义,不需要用户(DevOps)学习 Rego,从而避免错误。 与身份验证配置类似,我们的 Golang 服务框架被挂接到 Wingman 的授权引擎中,通过自动调用 Wingman 进行授权,并向开发人员隐藏授权的复杂性。
使用唯一身份(使用 Wingman 颁发)进行身份验证并使用可编程策略引擎(在 Wingman 内)进行授权,我们能够使用 mTLS 保护通信并使用强大且可编程的策略控制每个访问。
每天,工程师都会无意中犯下错误,将密钥和密码存储在代码中,并以某种方式进入公共代码或工件存储库。 管理秘密很困难,如果没有易于使用的工具包和明确定义的流程,开发人员就只能遵循最短的路径。 因此,从公司成立之初,我们的平台安全(不同于网络和应用安全)团队的使命就是确保开发人员不必问这样的问题:“我应该将秘密存储在哪里——源代码、工件还是……?”
当我们开始构建平台时,我们评估了两种可用于秘密管理的常见方法,它们都存在一定的缺点:
在我们的案例中,作为一项 SaaS 服务,我们必须想出一种更强大的方法来保护客户的秘密,因为任何妥协都不应泄露他们的秘密。
因此,我们决定实施一项称为 Volterra Blindfold(商标)的新技术,该技术与我们的安全边车 Wingman 协同工作,如图 4 所示。 这种方法允许秘密的所有者以某种方式锁定(加密)秘密,使得秘密永远不会向任何不良方(包括解密服务器)公开。 秘密甚至不存储在中央解密服务器中,这种设计在某些方面大大简化了服务器设计。
我们为用户提供了一个蒙眼工具,可以在完全离线的环境中使用它来加密秘密(S),然后可以将其分发 - 例如,秘密本身可以与工作负载一起存储并上传到注册表。 一旦实现这一目标,就需要采取以下步骤:
这确保了集中控制平面永远无法以明文(S)形式访问秘密,并且秘密仅在访问期间在 Pod 的运行时内存中可用。 此外,可以应用访问策略来定义谁可以访问秘密。 可以根据身份属性(例如应用名称、位置、合规性级别等)定义策略。 这样,就可以分离出任何复杂的工作负载子集,并实现精确的访问控制。 该策略以密码方式融入加密、致盲、解密和揭秘过程——从计算上来说,推翻该策略的意图是不可行的。
使用我们独特的 Blindfold 技术来锁定每个秘密,并使用 Wingman 根据策略和不可伪造的身份解封每个秘密,我们能够克服现有解决方案的问题,并在分布式环境中提供秘密管理,而不必担心中央金矿的泄露。
尽管秘密和密钥管理听起来像是同一问题的两个不同名称,但两者之间存在细微的(但在实践中很重要的)差异,这取决于你问谁以及人们可能如何实施解决方案。
秘密是指任何应当保密并且未经授权的各方不可获取的信息。 从某种程度上来说,加密密钥是秘密的一个特例。 另一方面,密钥管理通常是指安全存储敏感加密密钥材料并控制材料使用的系统。 在某些情况下,密钥管理系统可能会将密钥的原始字节分发给授权方,因此可能会与秘密管理系统混淆。 然而,在大多数情况下,密钥管理系统实际上并不会分发密钥材料的原始字节,而是为授权请求者执行操作并仅发送操作的输出。 许多密钥管理系统还由用于存储密钥材料的硬件存储(例如 HSM)支持,这样密钥就永远不会以明文形式暴露给软件。
在分布式环境中,即使对于单个云提供商而言,管理、同步和轮换加密密钥的问题也非常具有挑战性,并且当前的解决方案容易出错、效率低下甚至不安全。 例如,如果一个人今天使用 3 个 AWS 区域,并且想要在这 3 个区域中使用相同的加密密钥,则他们将必须手动同步和轮换密钥。 当环境跨多个云提供商(公共和/或私人)时,问题会变得更加严重。 即使完成了所有这些,应用所有者仍然必须编写复杂的代码才能使用这些 KMS 功能。
我们的平台通过让 Wingman sidecar 完成所有繁重工作并为应用提供简单的接口来发出密钥管理请求,包括加密、解密、HMAC、HMAC 验证、数字签名、签名验证、获取密钥(允许时)等,从而隐藏了应用密钥管理的所有复杂性。 这使得密钥管理对于我们自己的基础设施服务以及客户工作负载来说变得一点也不困难。
下图(图 5)展示了 Volterra 的 KMS 如何跨环境工作,并帮助工作负载将其密钥管理和加密操作卸载到 Wingman 边车上。 根据配置,Wingman 能够缓存密钥并刷新缓存,而应用甚至不知道这一点。 这里的促成因素是我们之前介绍过的普遍的、不可伪造的身份。 由于每个 Pod 无论其位置如何都具有全局唯一的身份,因此 Volterra KMS 系统可以轻松地将访问策略应用于加密密钥以及以非常准确的方式执行特定操作,例如加密、解密、HMAC、HMAC 验证、数字签名和签名验证。
由于所有密钥都通过 Volterra 的 SaaS 后端进行管理,因此在异构环境中运行的工作负载不必处理密钥同步、轮换、撤销等问题——他们只需了解简单的 Wingman API 即可满足所有静态数据的安全需求。
通过多层平台安全性,我们能够以全新的方式为三个关键问题提供全面的解决方案! 我们的系统能够安全地引导一个不受“一路向下的乌龟”问题困扰的通用身份,管理可以存储和分发的秘密而不必担心金矿问题,并提供密钥管理以减轻分布式环境中静态数据的安全性。 这为我们的内部团队和客户带来了以下收益:
本系列博客将涵盖我们构建和运营全球分布式 SaaS 服务的各个方面,其中包括公共云、私有网络 PoP 和边缘站点中的许多应用集群。 接下来是应用和网络安全……
我们正在寻找一些志愿开发人员和解决方案架构师来帮助我们将此功能作为开源项目带给更广泛的社区,如果有兴趣参与这项有趣的活动,请直接联系asingla@volterra.io !