博客

保护分布式平台 — 身份、机密和密钥管理

Ankur Singla 缩略图
安库尔·辛格拉
2019 年 12 月 11 日发布

这是系列博客中的第三篇,涵盖了我们构建和运营SaaS服务的各个方面: 

  1. 分布式 Kubernetes PaaS 的控制平面
  2. 分布式应用的全局服务网格
  3. 分布式基础设施、应用和数据的平台安全 
  4. 分布式集群application及网络安全
  5. 跨全球分布式平台的可观察性
  6. 全球分布式平台的运营和 SRE 
  7. 面向分布式微服务的Golang服务框架

我最初的博客提供了一些导致我们构建新分布式云平台的需求背景。 利用这个平台,我们的客户可以构建一套复杂多样的应用——比如智能制造、公共安全视频取证、算法交易和电信 5G 网络。

由于其中许多应用都是关键任务的,我们的客户期望我们不仅提供多层安全性,而且还能够不断改进以确保其分布式集群的安全。 这个特定的博客将解决跨多个集群的基础设施、应用和数据的安全挑战。

TL;DR(摘要)

  1. 虽然人们相对了解如何为用户和员工提供对应用的安全访问(我们每天在访问银行账户或公司电子邮件时都会这样做),但对于应用程序到应用程序或应用程序到数据的访问,做同样的事情并不那么简单,因为在验证过程中没有人工参与。
  2. 在异构环境(边缘、多云和网络 PoP)中保护应用程序和数据需要我们解决多层问题——身份、身份验证和授权、机密和密钥管理
  3. 尽管针对这四个问题有许多可用的点解决方案(例如来自各个云提供商的多种服务、Hashicorp Vault、SPIFFE/Spire 等)——但是却不存在可以跨云提供商运行或无缝结合这些服务以方便使用的集成解决方案。
  4. 由于我们的开发人员和 DevOps 团队缺乏将这些部分整合在一起的专业知识,因此我们的平台团队必须将这些功能作为易于使用的集成解决方案来提供。 不断发展的安全形势和新技术使这些团队面临的困难更多,因为他们没有必要的专业知识或带宽来跟上所有变化。
  5. 作为提供多层平台安全性的一部分,我们构建了新的软件组件,以全新的方式解决了三个关键问题 - 安全地引导不受“一路向下的乌龟”问题困扰的通用身份,可以存储和分发秘密而不必担心中央保险库的泄露(金矿问题),以及分布式密钥管理以减轻静态数据的安全性。

安全问题的背景

如上所述——保护用户访问应用(例如,访问我们的银行账户或电子邮件)的问题是众所周知的。 然而,对于应用程序到应用程序或应用程序到数据的访问,执行相同操作并不那么简单,因为验证过程不需要人工参与。

为了使应用以安全的方式访问另一个资源(例如,存储在对象存储中的数据或对另一个应用进行 API 调用等),需要发生以下情况: 

  1. 身份——请求者需要安全地获取可验证的身份,该身份可在访问所需资源时用于身份验证和授权目的。 请求者还需要安全地配备必要的信任锚,以用于验证对方的身份。 
     
  2. 身份验证——在访问给定资源的过程中,请求者和资源所有者需要安全地验证彼此的身份。 根据对方提供的身份来验证其声明的身份的过程称为身份验证。 
     
  3. 授权——一旦请求者通过身份验证,检查是否允许访问资源的过程称为授权。

作为身份验证过程的一部分,请求应用可以 PKI 证书或秘密(例如密码)或密钥的形式显示其身份。 使用秘密或密钥进行身份认证时需要解决两个问题: 

  • 秘密和密钥不应直接存储在代码中,因为这是一个容易受到攻击的媒介,而泄露的密钥可能是一个大问题。 
  • 如果秘密存储在外部保险库中,那么应该使用什么身份(通常是另一个秘密)来获取秘密,以及我们如何保护这个身份以获取秘密?

以可靠且可重复的方式执行这些安全操作(用于应用程序到应用程序的交互)并非易事。 有很多原因导致这一点并不简单: 

  1. applications可以轻松克隆和生成(例如微服务)。 如何为每个克隆分配一个可能需要用于取证、审计或可观察性目的的唯一身份? 
  2. 应用身份将根据其运行的环境而有所不同。 例如,在开发与生产中,应用是相同的,但在每个环境中都需要不同的身份。 
  3. 如何建立信任,确保生成应用的基础设施不会在不被注意的情况下窃取身份、秘密和密钥? 
  4. 如何保护中央保险库免受攻击,并确保存储在该保险库中的所有秘密和密钥的安全?

因此,在动态环境中保护基础设施、应用和数据是一个非常具有挑战性的问题。 虽然云提供商已经迎接这一挑战并提供了大量工具来解决这些问题,但由于以下原因,集成和维护这些问题并不容易: 

  • 复杂性——每个云提供商都要求客户配置和管理多项服务。 例如在 AWS 中 - 元数据服务、IAM 角色、服务账户、RBAC、KMS 和机密管理器。 每个云提供商提供的这些服务在语义、API 和监控方面都有很大的差异。 
     
  • 互操作性——即使云提供商服务已配置并投入运营,但它们都无法实现互操作性。 例如,在 GCP 中运行的虚拟机无法访问 AWS 中的资源,因为 AWS 资源无法理解使用 GCP 服务帐户分配的身份。 
     
  • 异构环境——如果环境分布在公共云和私有云,或多个公共云,或更糟的是,在边缘,那么挑战将是如何以及在何处存储密码、密钥等秘密——集中还是分布式。 
     
  • 环境特定- 每个云提供商的凭证轮换和/或撤销解决方案都非常不同,而边缘集群则不存在这些解决方案。

虽然许多企业都是单一云提供商,并且他们可能愿意投入资源来管理和增强该提供商的安全原语,但我们在异构环境(混合云和边缘)中运营,必须构建新的解决方案来解决这些问题。

分布式平台安全解决方案

我们的团队的任务是为可能驻留在分布式基础设施中的应用、基础设施和数据提供安全保障,如图 1 所示。

密钥管理-01
图 1: 保护分布式平台——身份、机密和密钥管理

因此,我们的平台团队决定构建新的软件服务,整合以下四个方面,以跨边缘、任何云和我们的网络 PoP 提供平台安全: 

  1. 身份管理——我们将描述如何向应用以及分布在异构环境(多个云、我们的全球 PoP 和地理上分散的边缘位置)中的基础设施提供加密安全且不可伪造的基于 PKI 的身份,并作为多个环境(例如开发人员机器、单元测试、生产等)运行 
     
  2. 身份验证和授权——我们的基础设施建立在微服务之上,利用基于 PKI 的身份并相互验证所有通信,无论使用何种通信协议。 我们还将授权与身份验证分离,以便可以使用通用授权引擎进行各种授权决策,并统一策略结构。 
     
  3. 秘密管理系统——我们的软件以及客户工作负载需要多种类型的秘密(如 TLS 证书、密码、令牌等)。 最简单的方法可能是采用一个集中式保险库来存储所有的秘密,但这种方法的缺点是,任何妥协都会泄露所有的秘密。 我们将描述一种不同的方法,通过放弃一些简单性来实现更高级别的安全性。 
     
  4. 密钥管理系统 (KMS) —数据安全对于我们的分布式系统至关重要,团队必须提供可跨云提供商无缝运行的 KMS。 KMS 必须管理、版本控制和轮换用于对称加密和解密、CSRF 令牌的 HMAC 以及数字签名二进制文件的密钥。 我们将讨论如何使用此 KMS 为安全敏感操作提供功能,以及如何使用秘密管理系统为延迟敏感操作提供功能。

基础设施和应用程序的不可伪造身份

身份是一个根本问题,因为一旦身份问题解决了,许多安全挑战就可以更容易地应对。 然而,要解决身份问题,我们需要定义身份的含义以及如何以可靠的方式发布身份。 加密极客喜欢对一切事物都有自己的看法,身份的定义也不例外:

由受信任的机构通过非委托的安全协议以加密方式认证的独特的、完整的不可伪造可通过加密方式验证的特征集,这些特征构成了对某人或某物的已知或考虑。

本质上,我们需要的是一个不可伪造的、可通过加密验证的安全身份。 颁发这样的身份认证很有挑战性,原因有二: 1)身份引导和 2)信任根

在与身份相关的话题中,有几种经常被讨论的方法——SPIFFE 和 Hashicorp Vault。 我们要澄清的是,SPIFFE 是一种命名方案,可以在我们的系统中用作身份证件(X.509 证书)——但是,该格式不适用于包含二进制数据的某些身份属性。 虽然 Vault 可用于颁发身份证明文件,但它并不能解决身份引导和信任根问题的挑战:

  1. 身份引导——在现实生活中,当一个人出生时,他/她的身份由出生证明确定。 这在逻辑上引导了个人的身份,并且使用此证书,个人可以派生/请求更多身份证书,例如护照,驾驶执照等。 类似地,在计算世界中,每次启动应用(或微服务)都需要引导一个身份。 为了能够与其他服务交互,任何运行的代码需要执行的第一步就是建立身份。 此外,由于相同的应用代码可能会多次启动,并且在不同的环境中启动(开发人员笔记本电脑、单元测试和生产),因此还需要每次启动都建立单独的引导标识。
  2. 信任根- 虽然发布引导身份似乎是一个简单的过程,但问题是谁来提供这个身份。 例如,在现实世界中,身份的提供始于医院断言某个孩子在特定的日期和时间出生在某个人身上,并且假设医院通过适当的检查伪造了出生记录,并且无法伪造。 因此,出生记录文件可以作为真实来源(或信任根)。 类似地,当应用由人工或自动代码启动时,需要有一个可以可验证地断言所启动应用的身份的信任根。 然后可以使用此断言为应用生成后续的身份证明文件。

例如,当在 AWS 中启动虚拟机时,它会被赋予一个引导标识,并且 AWS 的元数据服务充当信任根。 身份证明文件(由 AWS 使用其自己的加密密钥进行签名)如下所示:

原始代码 1

虽然instanceId可能表示已启动的应用实例的唯一标识,但它需要链接到某个众所周知的名称 (myserver.acmecorp.com),其他应用将使用该名称与该特定实例进行通信。 因此,即使这个 AWS 引导身份文档是不够的,但可以用来发布另一个身份,供应应用用于与另一个应用进行通信。

如前所述,对于我们来说,提供一种身份至关重要,它允许应用跨云提供商和/或边缘位置进行通信和共享信息(图 2)。 这意味着我们必须构建一个适用于所有这些环境的身份引导和信任根系统。

密钥管理-02
图 2: 跨云提供商、网络 PoP 和边缘的通信

由于我们使用 Kubernetes 来管理和编排应用(微服务和虚拟机),这意味着每个启动的 Pod 的唯一身份的引导必须与 Kubernetes 的 Pod 创建过程挂钩。 图 3 展示了我们如何使用 K8s webhook 机制来挂钩 pod 创建过程。 这个名为 Voucher 的 webhook 会将一个名为Wingman的安全 sidecar 注入到集群中创建的所有 Pod 中,同时提供可用作信任根的必要加密签名信息。 此 webhook 提供了一个短期签名令牌,Wingman 使用它从 Volterra 的 SaaS 后端中的身份颁发机构请求 X.509 证书。

身份认证机构强制执行身份认证规则,以最小化“爆炸半径”,以防某个 K8s 集群受到威胁。 许多其他依赖于通用根 CA 或 K8s 集群联合的解决方案无法限制爆炸半径,这是我们设计决策的重要输入。

密钥管理-03
图 3: 每个 Kubernetes 集群中的信任根

对于 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 保护通信并使用强大且可编程的策略控制每个访问。

无需集中式保险库的机密管理

每天,工程师都会无意中犯下错误,将密钥和密码存储在代码中,并以某种方式进入公共代码或工件存储库。 管理秘密很困难,如果没有易于使用的工具包和明确定义的流程,开发人员就只能遵循最短的路径。 因此,从公司成立之初,我们的平台安全(不同于网络和应用安全)团队的使命就是确保开发人员不必问这样的问题:“我应该将秘密存储在哪里——源代码、工件还是……?”

当我们开始构建平台时,我们评估了两种可用于秘密管理的常见方法,它们都存在一定的缺点: 

  1. Kubernetes Secrets——虽然我们使用 Kubernetes,并且它带有机密解决方案,但由于各种原因,它并不是特别有用——机密不是静态加密的,策略构造不全面,并且它不能解决多集群场景。
     
  2. 集中式保险库(例如 Hashicorp 或 Cyberark Vault) ——另一种方法可能是使用保险库,将秘密集中存储并分发给授权的请求者。 这些秘密受到用于 Vault 加密存储的单个加密密钥的保护。 然而,这种方法的问题在于,秘密管理系统可以访问明确的秘密(即使它们以加密形式存储),并且系统的任何泄露都可能泄露所有秘密。

在我们的案例中,作为一项 SaaS 服务,我们必须想出一种更强大的方法来保护客户的秘密,因为任何妥协都不应泄露他们的秘密。

因此,我们决定实施一项称为 Volterra Blindfold(商标)的新技术,该技术与我们的安全边车 Wingman 协同工作,如图 4 所示。 这种方法允许秘密的所有者以某种方式锁定(加密)秘密,使得秘密永远不会向任何不良方(包括解密服务器)公开。 秘密甚至不存储在中央解密服务器中,这种设计在某些方面大大简化了服务器设计。

密钥管理-04
图4: 用于机密管理的 Blindfold 和 Wingman

我们为用户提供了一个蒙眼工具,可以在完全离线的环境中使用它来加密秘密(S),然后可以将其分发 - 例如,秘密本身可以与工作负载一起存储并上传到注册表。 一旦实现这一目标,就需要采取以下步骤:

这确保了集中控制平面永远无法以明文(S)形式访问秘密,并且秘密仅在访问期间在 Pod 的运行时内存中可用。 此外,可以应用访问策略来定义谁可以访问秘密。 可以根据身份属性(例如应用名称、位置、合规性级别等)定义策略。 这样,就可以分离出任何复杂的工作负载子集,并实现精确的访问控制。 该策略以密码方式融入加密、致盲、解密和揭秘过程——从计算上来说,推翻该策略的意图是不可行的。

使用我们独特的 Blindfold 技术来锁定每个秘密,并使用 Wingman 根据策略和不可伪造的身份解封每个秘密,我们能够克服现有解决方案的问题,并在分布式环境中提供秘密管理,而不必担心中央金矿的泄露。

分布式系统的密钥管理

尽管秘密和密钥管理听起来像是同一问题的两个不同名称,但两者之间存在细微的(但在实践中很重要的)差异,这取决于你问谁以及人们可能如何实施解决方案。

秘密是指任何应当保密并且未经授权的各方不可获取的信息。 从某种程度上来说,加密密钥是秘密的一个特例。 另一方面,密钥管理通常是指安全存储敏感加密密钥材料并控制材料使用的系统。 在某些情况下,密钥管理系统可能会将密钥的原始字节分发给授权方,因此可能会与秘密管理系统混淆。 然而,在大多数情况下,密钥管理系统实际上并不会分发密钥材料的原始字节,而是为授权请求者执行操作并仅发送操作的输出。 许多密钥管理系统还由用于存储密钥材料的硬件存储(例如 HSM)支持,这样密钥就永远不会以明文形式暴露给软件。

在分布式环境中,即使对于单个云提供商而言,管理、同步和轮换加密密钥的问题也非常具有挑战性,并且当前的解决方案容易出错、效率低下甚至不安全。 例如,如果一个人今天使用 3 个 AWS 区域,并且想要在这 3 个区域中使用相同的加密密钥,则他们将必须手动同步和轮换密钥。 当环境跨多个云提供商(公共和/或私人)时,问题会变得更加严重。 即使完成了所有这些,应用所有者仍然必须编写复杂的代码才能使用这些 KMS 功能。

我们的平台通过让 Wingman sidecar 完成所有繁重工作并为应用提供简单的接口来发出密钥管理请求,包括加密、解密、HMAC、HMAC 验证、数字签名、签名验证、获取密钥(允许时)等,从而隐藏了应用密钥管理的所有复杂性。 这使得密钥管理对于我们自己的基础设施服务以及客户工作负载来说变得一点也不困难。

下图(图 5)展示了 Volterra 的 KMS 如何跨环境工作,并帮助工作负载将其密钥管理和加密操作卸载到 Wingman 边车上。 根据配置,Wingman 能够缓存密钥并刷新缓存,而应用甚至不知道这一点。 这里的促成因素是我们之前介绍过的普遍的、不可伪造的身份。 由于每个 Pod 无论其位置如何都具有全局唯一的身份,因此 Volterra KMS 系统可以轻松地将访问策略应用于加密密钥以及以非常准确的方式执行特定操作,例如加密、解密、HMAC、HMAC 验证、数字签名和签名验证。

密钥管理-05
图5: 我们 SaaS 后端中的 Wingman 和 KMS 服务器

由于所有密钥都通过 Volterra 的 SaaS 后端进行管理,因此在异构环境中运行的工作负载不必处理密钥同步、轮换、撤销等问题——他们只需了解简单的 Wingman API 即可满足所有静态数据的安全需求。

我们的平台安全解决方案带来的好处

通过多层平台安全性,我们能够以全新的方式为三个关键问题提供全面的解决方案! 我们的系统能够安全地引导一个不受“一路向下的乌龟”问题困扰的通用身份,管理可以存储和分发的秘密而不必担心金矿问题,并提供密钥管理以减轻分布式环境中静态数据的安全性。 这为我们的内部团队和客户带来了以下收益: 

  1. 安全性与合规性——不可伪造的通用身份、Blindfold 和安全边车(Wingman),由平台完全集成和自动管理,以保护所有通信、授权每次访问并管理分布式系统中的密钥/秘密。 利用这些进步,我们能够提供更强大的安全解决方案,从而增强我们和客户的合规能力。 
     
  2. 生产力改进——使用集成在我们的 DevOps 流程和服务框架中的安全解决方案,开发人员和 DevOps 团队可以轻松地专注于他们的可交付成果,而不必担心应用和数据安全的关键方面。 
     
  3. 持续发展——不断发展的安全形势和新技术推动着 Blindfold、Wingman 和我们的 Golang 服务框架的持续发展。 因此,平台会自动推出新功能,而无需对应用逻辑进行任何更改。

待续…

本系列博客将涵盖我们构建和运营全球分布式 SaaS 服务的各个方面,其中包括公共云、私有网络 PoP 和边缘站点中的许多应用集群。 接下来是应用和网络安全……

我们正在寻找一些志愿开发人员和解决方案架构师来帮助我们将此功能作为开源项目带给更广泛的社区,如果有兴趣参与这项有趣的活动,请直接联系asingla@volterra.io