使用双向 TLS(mTLS)及 Istio 来保护应用程序通信

深入研究保护应用程序通信、mTLS 和 Istio,以在应用程序之间实现端到端 mTLS。

Oct 17, 2023 | By Lin Sun - Solo.io, Yuval Kohavi - Solo.io; Translated by Wilson Wu - DaoCloud

用户采用服务网格的最大原因之一是使用基于加密可验证身份的双向 TLS(mTLS) 来实现应用程序之间的安全通信。在本博客中,我们将讨论应用程序之间安全通信的要求, 以及 mTLS 如何启用和满足所有这些要求,并介绍了简单的步骤让您可以使用 Istio 在应用程序之间启用 mTLS。

您需要怎样保护应用程序之间的通信?

现代云原生应用程序经常分布在多个 Kubernetes 集群或虚拟机上。 新版本被频繁上架,还需要根据用户请求量快速扩缩。 随着现代应用程序不再依赖于同位共址的方式来获得资源利用效率, 增加了多个访问入口的同时也导致了更大的攻击面, 因此对分布式应用程序施加访问策略并确保这些应用程序之间通信安全的能力变得至关重要。忽视这一点将会因数据丢失、 数据被盗、伪造数据或简单的处理不当而带来巨大的业务风险。

以下是应用程序之间安全通信的常见关键要求:

身份

身份是任何安全架构的基本组成部分。在您的应用程序可以安全地发送数据之前, 必须为应用程序建立身份。这个建立身份的过程称为身份验证 - 它涉及一些众所周知的、 受信任的权威机构对应用程序工作负载执行一项或多项检查, 以确定其是否如所声称的那样。一旦权威机构设定的要求被满足,该机构就会授予工作负载一个身份。

考虑一下签发护照的行为 - 您将向某个机构申请护照,该机构可能会要求您提供几种不同的身份验证, 以证明您是自己所陈述的身份 - 出生证明、居住地址、医疗记录等。一旦您满足了所有身份验证要求, 您将(有希望)获得身份证明文件。您可以将该身份证明文件交给其他人, 作为您已满足发证机构的所有身份验证要求的证据,如果他们信任发证机构(以及身份证明文件本身), 他们就可以相信其对您的描述(或他们可以联系这个受信任的机构并验证该文件)。

身份可以采取任何形式,但是,与任何形式的身份证明文件一样,身份验证越弱, 就越容易伪造,并且身份证明文件对于任何使用它来做出决定的人来说用处就越小。 这就是为什么在计算领域,可加密验证的身份如此重要 - 身份由可验证的权威机构签署, 类似于您的护照和驾照。未经权威机构签发的身份机制是一个相对容易被利用的安全漏洞。

您的系统可能具有从网络属性派生的身份,例如带有分布式身份缓存的 IP 地址, 这种缓存会跟踪身份与这些网络属性之间的映射。这些身份没有像可加密验证的身份那样得到强有力的保证, 因为 IP 地址可能会重新分配给不同的工作负载,并且身份缓存可能并不总是被更新到最新。

你的应用程序需要使用可加密验证的身份, 因为在连接建立期间为应用程序交换可加密验证的身份本质上比依赖于将 IP 地址映射到身份的系统更可靠、更安全。 后者采用的系统依赖于分布式身份缓存,最终一致性和过期都是个问题, 这可能会在 Kubernetes 中造成结构性弱点,使得 Pod 自动流失率高成为常态。

保密性

对应用程序之间传输的数据进行加密至关重要 - 因为在这样一个违规行为屡见不鲜、违规造成的成本高昂、采取的通用防范措施收效甚微的世道中, 完全依赖安全的内部环境或其他安全边界早已不再足够。 为了防止中间人攻击, 您需要有一个对源和目标配对的唯一加密通道, 因为您需要强大的身份唯一性保证用于避免混乱的代表问题。 换句话说,仅拥有加密通道是不够的 - 必须使用直接从唯一源和目标身份派生的唯一密钥进行加密, 以便只有源和目标才能解密数据。此外,您可能需要自定义加密,例如根据您安全团队的要求选择特定的加密算法。

完整性

通过网络从源发送到目标的加密数据一旦发送,就不能被除源和目标之外的任何身份进行修改。 换句话说,接收到的数据与发送的数据相同。如果您不具有数据完整性, 那么中间人可能会在源和目标之间的通信过程中修改数据的某些位或整个内容。

访问策略执行

应用程序所有者需要将访问策略实施于应用程序中,并正确、一致且明确地执行这些策略。 为了对通信通道的两端实施策略,我们需要为每一端提供一个应用程序身份。 一旦我们拥有了可加密验证的身份,并为潜在通信渠道的两端提供了明确的来源链, 我们就可以开始应用关于谁可以与什么通信的策略。标准 TLS 是一种广泛使用的加密协议, 可保护客户端(例如 Web 浏览器)和服务器(例如 Web 服务器)之间的通信安全, 它仅真正验证并强制一侧(即服务器)的身份。但对于全面的端到端策略执行, 客户端和服务器双方拥有可靠、可验证、明确的身份至关重要。 这是内部应用程序的常见要求 - 想象一下这样的场景:只有 frontend 应用程序应该调用后端 checkout 应用程序的 GET 方法,但不应允许调用 POSTDELETE 方法。 或者是这样一个场景:应用程序只有具有特定 JWT 颁发者颁发的 JWT 令牌时才能调用 checkout 应用程序的 GET 方法。 通过利用两端的加密身份,我们可以确保正确、安全、可靠地执行强大的访问策略,并具有可验证的审计跟踪。

FIPS 合规性

联邦信息处理标准(FIPS) 是由美国国家标准与技术研究院(NIST)制定的联邦计算机系统的标准和指南。 并非每个人都需要 FIPS 合规性,但 FIPS 合规性意味着满足美国政府为保护敏感信息而制定的所有必要的安全要求。 与美国联邦政府合作时合规是必需的要求。为了遵循美国政府制定的有关网络安全的指导方针, 许多民营企业自愿遵从这些 FIPS 标准。

为了说明上述安全应用程序要求(身份、保密性和完整性), 我们以 frontend 应用程序调用 checkout 应用程序为例。请记住, 您可以将图中的 ID 视为任何类型的身份证明文件,例如政府颁发的护照、带照片的身份证明:

当 frontend 调用 checkout 应用程序时的要求
当 frontend 调用 checkout 应用程序时的要求

mTLS 是如何满足上述要求的?

TLS 1.3(撰写本文时最新的 TLS 版本) 规范的主要目标是在两个通信对等点之间提供安全通道。 TLS 安全通道具有以下特征:

  1. 身份验证:通道的服务器端始终进行身份验证,客户端也可以选择进行身份验证。 当客户端也经过身份验证时,安全通道将成为双向 TLS 通道。
  2. 保密性:数据经过加密,只有客户端和服务器可见。 数据必须使用密钥进行明确加密并绑定到源和目标身份文件中,以便可靠地保护应用层流量。
  3. 完整性:在此通道上传输的数据不会在未经察觉的情况下被篡改。 这个效果的保证基于一个事实:只有源和目标具有加密和解密给定会话数据的密钥。

mTLS 内部

我们已经确定,可加密验证的身份是保护通道安全和支持访问策略执行的关键, 并且我们已经确定,mTLS 是一种经过实战检验的协议, 它要求在通道两端使用可加密验证的身份提供一些极其重要的保证。接下来让我们详细了解 mTLS 协议的实际工作原理。

握手协议

握手协议对通信对等方进行身份验证, 协商加密模式和参数,并建立共享密钥材料。换句话说, 握手的作用是验证通信对等方的身份并协商会话密钥,以便可以根据会话密钥对连接的其余部分进行加密。 当您的应用程序建立 mTLS 连接时,服务器和客户端会协商一个密码套件, 该套件决定您的应用程序将用于其余连接的加密算法,并且您的应用程序还会协商要使用的加密会话密钥。 整个握手过程采用防篡改设计 — 任何未拥有与源和/或目标相同特征、可加密验证的身份文件实体干扰都将被拒绝。 因此,在任何通信对等方继续处理应用程序数据之前,检查整个握手并验证其完整性非常重要。

根据 TLS 1.3 规范中的握手协议概述, 握手可以被认为具有三个阶段 - 让我们再次使用 frontend 应用程序调用后端 checkout 应用程序的示例:

  1. 第 1 阶段:frontendcheckout 协商可用于保护其余握手和流量数据的加密参数和加密密钥。
  2. 第 2 阶段:此阶段及之后的所有内容均被加密。在此阶段,frontendcheckout 建立其他握手参数, 以及客户端是否也经过 mTLS 等身份验证。
  3. 第 3 阶段:frontend 通过其可加密验证的身份来验证 checkout (若采用 mTLS,checkout 还会以相同的方式验证 frontend)。

与 TLS 1.2 相比,握手相关的内容存在以下几个较大差异, 请参阅 TLS 1.3 规范以了解更多详细信息

  1. 所有握手消息(第 2 阶段和第 3 阶段)均使用第 1 阶段协商的加密密钥进行加密。
  2. 传统对称加密算法已被修剪。
  3. 新增零往返时间(0-RTT)模式,节省连接建立时的往返时间。

记录协议

在握手阶段协商了 TLS 协议版本、会话密钥和 HMAC 后, 对等方现在可以安全地交换由记录协议分块的加密数据。 使用与握手中完全相同的协商参数来加密流量以确保流量的保密性和完整性至关重要(这也是此规范所要求的一部分)。

将 TLS 1.3 规范中的两个协议放在一起, 并使用 frontendcheckout 应用程序来说明流程,如下所示:

当 frontend 调用 checkout 应用程序时的 mTLS 流程
当 frontend 调用 checkout 应用程序时的 mTLS 流程

谁为 frontendcheckout 颁发身份证书? 它们通常由证书颁发机构(CA)颁发, 该机构拥有自己的根证书或使用来自其根 CA 的中间证书。 根证书基本上是一个用于标识根 CA 的公钥证书,您的组织中可能已经拥有该根 CA。 除了其自己的根签名身份证书之外,根证书还分发到 frontend(或 checkout)。 这就是日常基本公钥基础设施(PKI)的工作方式 - CA 负责验证实体的身份文件, 然后以证书的形式授予其不可伪造的身份文件。

您可以依靠您的 CA 和中间 CA 作为身份真相的来源, 以一种结构方式保持高可用性和稳定、持久可验证的身份保证, 而 IP 和身份映射的大规模分布式缓存根本无法做到这一点。 当 frontendcheckout 身份证书由同一根证书颁发时, frontendcheckout 可以一致且可靠地验证其对等身份, 无论它们以何种规模运行在哪个集群或节点。

您已经了解 mTLS 如何提供加密身份、保密性和完整性, 那么当您在多个集群中扩展到数千个或更多应用程序时,可扩展性如何? 如果您跨多个集群建立单个根证书,则系统不需要关心您的应用程序何时从另一个集群获取连接请求, 只要它受到根证书的信任即可 - 系统知道连接上的身份是已验证的加密方式。 当您的应用程序 Pod 更改 IP 或重新部署到不同的集群或网络时, 您的应用程序(或代表它的组件)只需使用 CA 生成的受信任证书将流量发送到目标。 可以是 500+ 网络跳转,也可以是直达;无论拓扑如何,您的应用程序的访问策略都会以相同的方式强制执行, 无需跟踪身份缓存并计算哪个 IP 地址映射到哪个应用程序 Pod。

FIPS 合规性怎么样?根据 TLS 1.3 规范, 符合 TLS 的应用程序必须实现 TLS_AES_128_GCM_SHA256 密码套件, 并建议实现 TLS_AES_256_GCM_SHA384,这两者也包含在由 NIST 发布的 TLS 指南中。 TLS 1.3 规范和 NIST 的 TLS 指南也推荐使用 RSA 或 ECDSA 服务器证书。 只要您为 mTLS 连接使用 mTLS 和符合 FIPS 140-2 或 140-3 的加密模块, 就能够符合 FIPS 140-2 或 140-3 验证

可能会出现什么问题

严格按照 TLS 1.3 规范的规定实施 mTLS 至关重要。 如果不使用符合 TLS 规范的正确 mTLS,可能会在未察觉的情况下出现以下问题:

如果连接中间有人静默捕获加密数据怎么办?

如果连接不完全遵循 TLS 规范中概述的握手和记录协议, 例如,连接遵循握手协议,但不使用记录协议中握手中协商的会话密钥和参数, 您可能会遇到以下情况:连接的握手与记录协议无关,其中握手和记录协议之间的身份可能不同。 TLS 要求握手和记录协议共享相同的连接,因为将它们分开会增加中间人攻击的攻击面。

mTLS 连接从握手开始到结束都具有一致的端到端安全性。 加密数据通过使用证书中的公钥协商的会话密钥进行加密。 只有源和目标才能使用私钥解密数据。换句话说, 只有拥有私钥的证书所有者才能解密数据。除非黑客控制了证书的私钥, 否则他或她没有办法扰乱 mTLS 连接来成功执行中间人攻击。

如果源身份或目标身份未以加密方式确保安全怎么办?

如果身份基于 IP 地址等网络特征(可以重新分配给其他 Pod), 则无法使用加密技术来验证身份。由于此类身份不是基于加密身份, 因此您的系统可能有一个身份缓存来跟踪身份、Pod 的网络标签、 相应的 IP 地址和部署 Pod 的 Kubernetes 节点信息之间的映射。 使用身份缓存时,当身份缓存短时间内不同步时,您可能会遇到 Pod IP 地址被重复使用和身份错误的情况,因为策略未被正确执行。 例如,如果您在对等点之间的连接上没有加密身份, 您的系统将必须从身份缓存中获取身份,该身份可能已过时或不完整。

这些将身份映射到工作负载 IP 的身份缓存不是 ACID(原子性、一致性、隔离性和持久性), 并且您希望您的安全系统应用于某些内容并具备强有力的保证。 考虑以下您可能要自省的特征和问题:

上述某几条的情况可能相对更为糟糕。您可以应用失败关闭原则,但这并不能解决上述所有问题。

身份还用于执行访问策略(例如授权策略),这些访问策略位于请求路径中, 您的系统必须快速做出决定以允许或拒绝访问。每当身份出现错误时, 访问策略就可能被绕过而不会被检测或审计。例如, 您的身份缓存可能会将您的 checkout Pod 之前分配的 IP 地址关联为 checkout 的一个身份。如果 checkout Pod 被回收, 并且相同的 IP 地址被分配给 frontend 的一个 Pod, 则该 frontend Pod 可能在缓存更新之前就拥有 checkout 的身份, 这可能会导致错误的访问策略被执行。

让我们假设以下大规模多集群部署来说明身份缓存的过时性问题:

  1. 100 个集群,每个集群有 100 个节点,每个节点有 20 个 Pod。Pod 总数为 200,000 个。
  2. 0.25% 的 Pod 始终处于搅动状态(滚动、重启、恢复、节点搅动等),每次搅动的时间窗为 10 秒。
  3. 每 10 秒将 500 个正在搅动的 Pod 被分发到 10,000 个节点(缓存)。
  4. 如果缓存同步器停止运行 5 分钟,系统的陈旧百分比 - 可能高达 7.5%

上面假设缓存同步器处于稳定状态。如果缓存同步器出现故障, 则会影响其运行状况检查,以增加流失率,从而导致级联不稳定性。

CA 也可能被呈现为其他人的攻击者妥协以及欺骗 CA 进行证书颁发。 然后,攻击者可以使用该证书与其他对等点进行通信。在这种情况下, 可以通过证书吊销使其不再有效来进行补救。 否则,攻击者可以利用受损的证书直至其过期。 将根证书的私钥保存在保持离线的 HSM 中并使用中间证书来签署工作负载证书至关重要。如果 CA 出现故障或停滞 5 分钟, 您将无法获取新的或续订的工作负载证书,但之前颁发的有效证书将继续为您的工作负载提供强大的身份保证。 为了提高颁发的可靠性,您可以将中间 CA 部署到不同的可用区和区域。

Istio 中的 mTLS

启用 mTLS

在 Istio 中为网格内应用程序启用 mTLS 非常简单。 您所需要做的就是将应用程序添加到网格中,这可以通过对命名空间打 Sidecar 注入或 Ambient 标签来完成。 对于 Sidecar,需要滚动重启才能将 Sidecar 注入到您的应用程序 Pod 中。

加密身份

在 Kubernetes 环境中,Istio 根据其服务帐户创建应用程序的身份。将应用程序添加到网格后,将向网格中的每个应用程序 Pod 提供身份证书。

默认情况下,您的 Pod 身份证书将在 24 小时后过期, 并且 Istio 每 12 小时轮换一次 Pod 身份证书,因此, 如果发生泄露(例如,CA 泄露或 Pod 私钥被盗), 泄露的证书仅在证书到期前非常有限的时间可用,因此限制了它可能造成的损害。

执行严格的 mTLS

默认的 mTLS 行为就是不严格执行的 mTLS。 若要严格强制您的应用程序仅接受 mTLS 流量,您可以使用 Istio 的 PeerAuthentication 策略(网格范围或每个命名空间或工作负载)。此外,您还可以应用 Istio 的 AuthorizationPolicy 来控制工作负载的访问。

TLS 版本

TLS 的 1.3 版是 Istio 中默认的网格内应用程序与 Envoy 的默认密码套件 (例如 Istio 1.19.0 中的 TLS_AES_256_GCM_SHA384)。如果您需要较旧的 TLS 版本, 可以为您的工作负载配置不同网格范围的最低 TLS 协议版本

总结

TLS 协议由互联网工程任务组(IETF)制定,是现有的审查最广泛、经专家认可、 得到实战检验的数据安全协议之一。TLS 在全球范围内也被广泛使用。当您访问任意安全的网站时, 您可以放心购物的部分原因是那个挂锁图标,这表明您已使用 TLS 安全地连接到受信任的网站。 TLS 1.3 协议的设计具有端到端身份验证、保密性和完整性,以确保应用程序的身份和通信不会受到损害, 并防止中间人攻击。为了实现这一点(并被视为符合标准的 TLS),正确验证通信对等方非常重要, 而且使用握手建立的密钥对流量进行加密也很重要。既然您知道 mTLS 擅长满足您的安全应用程序通信要求(加密身份、保密性、完整性和访问策略实施), 您只需使用 Istio 即可通过开箱即用的 mTLS 升级您的网格内应用程序通信 - 只需很少的配置!

非常感谢 Louis Ryan、Ben Leggett、John Howard、Christian Posta、Justin Pettit,他们在博客的审核以及更新建议方面贡献了大量时间!