使用双向 TLS 和 Istio 实现安全应用程序通信

深入了解如何使用双向 TLS 和 Istio 来保护应用程序通信,从而在您的应用程序之间实现端到端的双向 TLS。

2023年10月17日 | 作者:林孙 - Solo.io,Yuval Kohavi - Solo.io

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

您需要什么来保护应用程序之间的通信?

现代云原生应用程序通常分布在多个 Kubernetes 集群或虚拟机上。新版本频繁发布,并且可以根据用户请求快速扩展和缩减。随着现代应用程序通过不依赖于共同位置来提高资源利用效率,由于增加了多个入口点导致攻击面扩大,因此能够对这些分布式应用程序之间的通信应用访问策略并确保其安全至关重要。忽略这一点就等于为数据丢失、数据盗窃、数据伪造或简单处理不当带来巨大的业务风险。

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

身份

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

以办理护照为例 - 您将向某个权威机构申请护照,该权威机构可能会要求您提供一些证明您身份的验证,例如出生证明、当前地址、医疗记录等。一旦您满足所有身份验证要求,您将(希望)获得身份证明文件。您可以将此身份证明文件提供给他人,作为证明您已满足发证机构所有身份验证要求的证据,如果他们信任发证机构(以及身份证明文件本身),他们就可以信任它关于您的信息(或者他们可以联系可信赖的权威机构并验证文件)。

身份可以采用任何形式,但是,与任何形式的身份证明文件一样,身份验证越弱,伪造就越容易,并且该身份证明文件对任何使用它来做决策的人的用处就越小。因此,在计算领域,密码学可验证身份非常重要 - 它们由可验证的权威机构签名,类似于您的护照和驾驶执照。基于其他任何内容的身份都是一个相对容易利用的安全弱点。

您的系统可能拥有从网络属性(如 IP 地址)派生的身份,以及跟踪身份与这些网络属性之间映射的分布式身份缓存。这些身份没有像密码学可验证身份那样强有力的保证,因为 IP 地址可以重新分配给不同的工作负载,并且身份缓存可能并非总是更新到最新状态。

希望为您的应用程序使用密码学可验证身份,因为在连接建立期间交换应用程序的密码学可验证身份本质上比依赖于将 IP 地址映射到身份的系统更可靠和安全。这些系统依赖于具有最终一致性和陈旧性问题的分布式身份缓存,这可能会在 Kubernetes 中造成结构性弱点,而 Kubernetes 中高频率的自动 Pod 替换是常态。

机密性

对应用程序之间传输的数据进行加密至关重要 - 因为在一个漏洞普遍存在、代价高昂且实际上微不足道的世界里,完全依赖于安全的内部环境或其他安全边界早已不再足够。为了防止中间人攻击,您需要为源-目标对建立一个唯一的加密通道,因为您希望拥有强大的身份唯一性保证以避免混淆代理问题。换句话说,仅仅加密通道是不够的 - 必须使用直接从唯一的源和目标身份派生的唯一密钥对其进行加密,以便只有源和目标才能解密数据。此外,您可能需要根据安全团队的要求自定义加密,例如选择特定的密码。

完整性

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

访问策略执行

应用程序所有者需要为其应用程序应用访问策略,并确保这些策略得到正确、一致且明确的执行。为了对通信通道的两端应用策略,我们需要每个端都有一个应用程序身份。一旦我们为潜在通信通道的两端都拥有一个具有明确出处链的密码学可验证身份,我们就可以开始应用有关谁可以与什么通信的策略。广泛使用的加密协议标准 TLS(用于保护客户端(例如,Web 浏览器)和服务器(例如,Web 服务器)之间通信的协议)实际上只验证并强制执行一侧的身份 - 服务器。但是,为了全面执行端到端策略,必须为两端(客户端和服务器)都拥有可靠、可验证、明确的身份。这是内部应用程序的常见要求 - 例如,想象一下,只有frontend应用程序才能对后端checkout应用程序调用GET方法,但不能调用POSTDELETE方法。或者,只有拥有特定 JWT 发行者颁发的 JWT 令牌的应用程序才能对checkout应用程序调用GET方法。通过利用两端的加密身份,我们可以确保强大的访问策略得到正确、安全且可靠地执行,并具有可验证的审计跟踪。

FIPS 合规性

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

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

Requirements when the frontend calls the checkout application
frontend调用checkout应用程序时的要求

双向 TLS 如何满足上述要求?

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

  1. 身份验证:通道的服务器端始终进行身份验证,客户端可以选择进行身份验证。当客户端也进行身份验证时,安全通道将变为双向 TLS 通道。
  2. 机密性:数据被加密,只有客户端和服务器可见。必须使用与源和目标身份证明文件明确密码学绑定的密钥对数据进行加密,以便可靠地保护应用程序层流量。
  3. 完整性:通过通道发送的数据无法在未检测的情况下被修改。这是因为只有源和目标拥有用于给定会话的加密和解密数据的密钥。

双向 TLS 内部机制

我们已经确定,密码学可验证身份是保护通道和支持访问策略执行的关键,并且我们已经确定双向 TLS 是一种经过实战检验的协议,它强制执行一些对于在通道两端使用密码学可验证身份至关重要的保证 - 让我们深入了解双向 TLS 协议在幕后是如何工作的。

握手协议

握手协议对通信对等体进行身份验证,协商加密模式和参数,并建立共享密钥材料。换句话说,握手的作用是验证通信对等体的身份并协商会话密钥,以便其余连接可以基于会话密钥进行加密。当您的应用程序建立双向 TLS 连接时,服务器和客户端将协商一个密码套件,该套件决定您的应用程序在连接的剩余时间内将使用什么加密算法,并且您的应用程序还将协商要使用的加密会话密钥。整个握手设计用于抵抗篡改 - 任何不拥有与源和/或目标相同的唯一密码学可验证身份证明文件的实体的干扰都将被拒绝。因此,在任何通信对等体继续使用应用程序数据之前,检查整个握手并验证其完整性非常重要。

根据 TLS 1.3 规范中的握手协议概述,可以将握手视为包含三个阶段 - 让我们再次以frontend应用程序调用后端checkout应用程序为例

  1. 阶段 1:frontendcheckout协商可用于保护其余握手和流量数据的加密参数和密钥。
  2. 阶段 2:此阶段及之后的所有内容都已加密。在此阶段,frontendcheckout建立其他握手参数,以及客户端是否也经过身份验证 - 即双向 TLS。
  3. 阶段 3:frontend通过其密码学可验证身份对checkout进行身份验证(并且,在双向 TLS 中,checkout以相同的方式对frontend进行身份验证)。

自 TLS 1.2 以来,握手方面存在一些主要差异,有关更多详细信息,请参阅 TLS 1.3 规范

  1. 所有握手消息(阶段 2 和 3)都使用**在阶段 1 协商的加密密钥**进行加密。
  2. 已删除旧版对称加密算法。
  3. 添加了零往返时间 (0-RTT) 模式,在连接设置时节省了一次往返。

记录协议

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

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

mTLS flows when the frontend calls the checkout application
frontend 调用 checkout 应用程序时的 mTLS 流量

谁为frontendcheckout颁发身份证书?它们通常由证书颁发机构 (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 以及符合 FIPS 140-2 或 140-3 的加密模块用于您的 mTLS 连接,您将走在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(原子性、一致性、隔离性和持久性),并且您希望将安全系统应用于具有强大保证的内容。考虑以下属性和您可能想问自己的问题

上述某些情况比其他情况更糟糕。您可以应用**故障关闭**原则,但这并不能解决上述所有问题。

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

让我们说明身份缓存陈旧性问题,假设以下大规模多集群部署

  1. 100 个集群,每个集群有 100 个节点,每个节点有 20 个 Pod。Pod 的总数为 200,000 个。
  2. 始终有 0.25% 的 Pod 处于 churn 状态(推出、重启、恢复、节点 churn 等),每次 churn 都是一个 10 秒的窗口。
  3. 500 个正在 churn 的 Pod 每 10 秒分布到 10,000 个节点(缓存)
  4. 如果缓存同步器停滞,5 分钟后系统有多少百分比是陈旧的 - 潜在地高达**7.5%**!

以上假设缓存同步器处于稳定状态。如果缓存同步器出现短暂故障,它将影响其运行状况检查,从而导致 churn 率增加,并导致级联不稳定。

CA 也可能被攻击者入侵,该攻击者声称代表其他人并欺骗 CA 颁发证书。然后,攻击者可以使用该证书与其他对等方通信。这就是证书吊销可以通过吊销证书使其不再有效来弥补这种情况的地方。否则,攻击者可以利用被入侵的证书直到过期。将根证书的私钥保存在保持脱机状态的 HSM 中并使用中间证书签名工作负载证书至关重要。在 CA 出现短暂故障或停滞 5 分钟的情况下,您将无法获取新的或更新的工作负载证书,但之前已颁发且有效的证书将继续为您的工作负载提供强大的身份保证。为了提高签发的可靠性,您可以将中间 CA 部署到不同的区域和区域。

Istio 中的 mTLS

启用 mTLS

在 Istio 中为网内应用程序启用 mTLS 非常简单。您只需要将您的应用程序添加到网格中,这可以通过为 sidecar 注入或环境标记您的命名空间来完成。在 sidecar 的情况下,需要进行 rollout 重启才能将 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,他们在审查和提出博客更新方面投入了大量时间!

分享此文章