在过去几年中我发现,我们的数据是我们所拥有的最宝贵的资产之一。黑客深知这一点,并将想方设法窃取数据,而自动化攻击能够让其比以往更轻松地实施窃取行为。我曾经不慎将树莓派上的 SSH 端口暴露于互联网。几个月后,在查看日志时,我注意到多个 IP 地址一直在利用暴力破解密码枚举脚本对其进行攻击。我立即关闭了端口,并告诉自己一定要经常检查日志。我的树莓派漏洞不会有什么严重后果,但公司或政府的数据泄露则可能会造成数百万美元的损失,影响也会持续数年之久。
当使用微服务和 Kubernetes 时,我们也留下了一个潜在攻击面。较之在内存中执行所有通信的单体架构,微服务之间的东西向通信需要在网络上传输更多的数据。这些明文数据是攻击者监控的首选对象,即使通信服务位于防火墙后面也不例外。
采用 service mesh(服务网格)最重要的原因之一是它可以管理 service 到 service 的流量。sidecar 和控制平面的组合支持通过双向 TLS (mTLS) 对连接两端进行集中管理和控制,以便加密和验证网络上的数据。mTLS 扩展了标准 TLS,让客户端及服务器出示证书并相互完成身份验证。凭借 service mesh,mTLS 能够零接触式实施,这意味着开发人员不必改造其应用,甚至无需知道双向身份验证是否正在进行。
本文概述了我们如何在 NGINX Service Mesh 中实施 mTLS。
概述
NGINX Service Mesh 采用开源 SPIRE 软件作为其中央证书颁发机构 (CA)。SPIRE 可以处理证书的整个生命周期:创建、分发和轮换。在服务网格中,NGINX Plus 使用 SPIRE 颁发的证书来建立 mTLS 会话,并加密流量。
图中关键组件包括:
-
SPIRE 服务器 – SPIRE 服务器作为 Kubernetes StatefulSet 运行。它由两个容器组成,即实际 spire-server 和 k8s-workload-registrar:
- spire-server – 作为 NGINX Service Mesh mTLS 架构的核心,spire-server 是为工作负载颁发证书并将其推送至 SPIRE 代理(见下文)的 CA。它可以是网格中所有服务的根 CA 或信任链中的中间 CA。
- k8s-workload-registrar – 当创建新的 Pod 时,k8s-workload-registrar 会利用 API 调用来请求该 spire-server 生成新证书。它通过 Unix socket 与 spire-server 进行通信。k8s-workload-registrar 容器是基于 Kubernetes 自定义资源定义 (CRD)的。
-
Spire 代理 – 作为 Kubernetes DaemonSet 运行,这意味着每个node节点只运行一个副本。SPIRE 代理具有两个主要功能:
- 从 SPIRE 服务器接收证书,并将其存储在缓存中。
- “认证”出现的每个 Pod,这意味着它要求 Kubernetes 系统提供有关 Pod 的信息(包括其 UID、名称和命名空间),然后使用这些信息查找相应的证书。
- NGINX Plus sidecar – 使用 SPIRE 生成和分发的证书,并处理整个 mTLS 工作流,在其中交换和验证证书。
NGINX Service Mesh 中的证书生命周期
在了解了 NGINX Service Mesh 的 mTLS 架构中的关键组件后,我们来看看它们如何协同创建自包含的 mTLS 系统。
创建
mTLS 工作流的第一阶段是创建实际证书。下面我们逐步了解一下将 Kubernetes Pod 部署至 NGINX Service Mesh 后的流程步骤:
- Pod 已部署。
- NGINX Service Mesh 控制平面使用 mutating webhook 将 sidecar 注入到 Pod Spec 中。
- k8s-workload-registrar 收集创建证书所需的信息,例如 ServiceAccount 名称, 来响应“pod created”事件通知。
- k8s-workload-registrar 通过 Unix socket 对 spire-server 进行 API 调用,为 Pod 请求证书。
- spire-server 为 Pod 颁发证书。
分发
新证书需要被安全地分发到正确的 Pod:
- SPIRE 代理获取新证书,并将其存储在缓存中。SPIRE 代理和 SPIRE 服务器使用 gRPC 进行通信,详见下文“Bootstrapping引导”部分。
- 被注入的 Pod 出现,并注入了 NGINX Plus sidecar。
- sidecar 通过 Unix socket 连接到在同一节点上运行的 SPIRE 代理。
- SPIRE 代理将认证 Pod、收集正确的证书,并通过 Unix socket 将其发送至 sidecar(有关详细信息,请参阅下文“Pod 认证”部分)。
执行
NGINX Plus 现在可以访问证书,并将其用于执行 mTLS。下面我们逐步了解一下当 Pod 尝试连接到同样拥有 SPIRE 颁发证书的服务器时会如何使用证书。
- 在 Pod 中运行的应用发起到 service 的连接。
- NGINX Plus sidecar 使用
iptables
NAT 重定向拦截连接。 - NGINX Plus 发起到目标 service 的 sidecar 的 TLS 连接。
- 服务器端 NGINX Plus 将其证书发送至客户端,并请求客户端证书。
- 客户端 NGINX Plus 验证服务器证书(直至信任根),并将自己的证书发送到服务器。
- 服务器验证客户端证书(直至信任根)。
- 双方均已进行双向身份验证,标准 TLS 密钥交换现在可以进行了。
轮换
为了保持流量顺畅流动,必须在证书到期日前进行轮换。当证书即将到期时,spire-server 会颁发新证书,并触发轮换流程。新证书将被推送至 SPIRE 代理,后者通过 Unix socket 将其推送到 NGINX Plus sidecar。
公钥基础架构
SPIRE 既可以是 NGINX Service Mesh 部署中的信任根,也可以集成到您的现有公钥基础架构 (PKI) 中。想获取更多信息,请参阅 NGINX Service Mesh 文档中的“使用上游根 CA 进行部署”。
结语
过去单体架构的应用是在内存中执行大部分通信,而如今微服务架构下的应用是利用网络进行通信。为了防止攻击者侦听这些数据,您需要对其进行加密。NGINX Service Mesh 提供了零接触的 mTLS 架构,让您无需修改 service 即可加密 service 到 service 的通信。如要开始使用 NGINX Service Mesh,请参阅我们的博文《NGINX Service Mesh 简介》。
附录
引导
SPIRE 服务器和代理使用 SSL/TLS 进行通信,这就提出了一个问题,即我们如何使用证书在本身负责生成并分发证书的 service 之间建立安全通信。SPIRE 服务器可通过名为 k8sbundle 的通知器插件做到这一点。
当 SPIRE 服务器启动时,它将最新的根 CA 证书推送至 Kubernetes ConfigMap 资源。ConfigMap 用于存储 Pod 可作为文件卷挂载的数据,如 SPIRE 代理所做的一样。当 SPIRE 代理启动时,它会找到根 CA 证书,并将其用于验证 SPIRE 服务器出示的证书。
SPIRE 服务器使用节点认证流程来验证 SPIRE 代理。
选择器
如需为 Pod 颁发特定证书,它必须拥有一组名为 selectors(选择器)的属性。SPIRE 颁发的每个证书均与一组选择器相关联,以标识其所属的 Pod。NGINX Service Mesh 依赖 Pod UID 来唯一标识 Pod。Pod 生成后,在 Pod 认证过程中,SPIRE 代理使用 UID 作为选择器快速查找 Pod 的唯一证书。
Pod 认证
认证只是将证书分发到正确 Pod 的流程中的一步(分发中的第 4 步),但却是整个 mTLS 架构的关键部分。认证意味着 SPIRE 代理向 Kubernetes 发出请求,以获取有关 Pod 的所有可用信息。SPIRE 代理日志中的代码段显示了其在 Pod 上收集的信息。
"PID attested to have selectors" pid=1226861 selectors="[type:\"unix\" value:\"uid:2102\" type:\"unix\" value:\"gid:1000\" type:\"k8s\" value:\"sa:bookinfo-productpage\" type:\"k8s\" value:\"ns:default\" type:\"k8s\" value:\"pod-uid:5beeffac-756e-11ea-91e1-42010a8a008f\" type:\"k8s\" value:\"pod-name:productpage-v1-c7765c886-fvqqj\" type:\"k8s\" value:\"container-name:proxy\" type:\"k8s\" value:\"pod-label:spire-workload:productpage-v1\" type:\"k8s\" value:\"pod-label:version:v1\" type:\"k8s\" value:\"pod-label:app:productpage\" ]"
这里,SPIRE 代理收集了 Pod 的 UID(pod‑uid
,此处为 5b33...008f
)、命名空间(ns
,此处为默认值
)以及 ServiceAccount (sa
,此处为 bookinfo‑productpage
)。然后,它们将成为选择器,以查找要颁发给 Pod 的正确证书。更多信息,请参阅 SPIRE 文档中的“工作负载认证”。