TT Lab
开始
学习 学习路径 课程

CKS — Kubernetes 安全专家

线上的加密、时间上的漏洞、节点上的攻击面

在 TT Lab 中继续学习

一句话总结

加密 Pod 之间的流量有两种方式——Cilium 的透明加密在节点内核中用 WireGuard 或 IPsec 封装,Istio 的 mTLS 则在Pod 内的 sidecar处终止。运行已结束支持的版本,意味着拿不到 CVE 修复,所以升级本身就是一项安全事项;节点操作系统的原则是只保留运行 kubelet、容器运行时和 CNI 所必需的内容。

为什么需要它

NetworkPolicy 只决定谁可以连接谁。连接被允许之后,在线路上传输的字节是明文。如果在同一个节点内,节点上什么都看得到,加密也就没有意义;但节点与节点之间的流量要经过物理交换机、云虚拟网络以及其他租户的设备。安全检查清单 写道,并非所有 CNI 插件都提供传输中加密,如果所选插件没有这项功能,服务网格就是替代方案。因此 CKS 会考查 Cilium 和 Istio 这两条路。

升级之所以是安全事项,原因在于时间。漏洞只有靠已修复的版本才能堵住,而项目只为最近三个 minor 版本提供补丁。超出这个窗口的集群,即使 CVE 已经公开,也拿不到可用的修复版本。节点操作系统也是同样的道理。每一个已安装的服务、软件包和内核模块,都是攻击者可以敲的一扇门,而去掉不用的门是最便宜的防御。

工作原理

Cilium 透明加密——在节点内核中

根据 Cilium 透明加密 文档,Cilium 会用 IPsec 或 WireGuard(以及处于 beta 阶段的 ztunnel)加密由 Cilium 管理的端点之间的流量。应用对此一无所知,因为加密和解密发生在节点的内核数据路径中。

启用 WireGuard 后,每个节点上的代理会生成自己的密钥对,并通过 CiliumNode 对象的 network.cilium.io/wg-pub-key 注解公布公钥。其他节点用这个公钥与该节点建立隧道。隧道端点使用 UDP 51871,因此在有防火墙的环境中,节点之间必须放行该端口;在隧道路由模式下,先用 VXLAN 或 Geneve 封装过一次的数据包,WireGuard 还会再封装一次。Helm 值是 encryption.enabled=true 和 encryption.type=wireguard,如果还要涵盖节点之间以及节点与 Pod 之间的流量,再加上 encryption.nodeEncryption=true。内核必须支持 WireGuard(Linux 5.6 及以上内置)。

IPsec 通过 Kubernetes Secret 分发密钥,节点之间会有 ESP 流量,因此需要在安全组或防火墙中放行 ESP。从 Cilium 1.18 开始,隧道模式下会在覆盖网络封装之后再应用 IPsec,连用于策略的安全标识也会在线路上被加密。文档也写明了限制——在链式叠加于其他 CNI 之上的配置中不受支持,而且 IPsec 解密对每条隧道限制为一个 CPU 核心。

这两种方式有两个共同的性质。第一,发往同一节点的数据包不会被加密。设计上认为在节点上本来就能看到原文,加密没有意义。第二,是否加密的判断方式与策略执行相同,即判断“目的地是不是远端 Cilium 端点”,所以在新端点的信息还没有传开的短暂时刻,第一个数据包可能以明文发出。文档给出的对策是用策略阻止流向 reserved:world 的 egress,或者使用 encryption.strictMode。

Istio mTLS——在 Pod 内的 sidecar 中

Istio 安全概念 文档描述的流程是这样的。客户端的请求被重定向到 Pod 内的 sidecar Envoy,客户端 Envoy 与服务端 Envoy 进行 mTLS 握手,同时通过 secure naming 检查,确认服务端证书中的 ServiceAccount 有权运行该服务。连接建立后,服务端 Envoy 对请求进行授权,通过后再经本地 TCP 交给后端。最低 TLS 版本是 1.2。也就是说,加密只在两端 sidecar 之间有效,应用容器与自己的 sidecar 之间是 Pod 内的明文 loopback。

默认模式是 PERMISSIVE,既接受明文也接受 mTLS。这样设计是为了逐步迁移没有 sidecar 的客户端。全部迁移完成后,把 PeerAuthentication 改为 STRICT,就会拒绝明文。

apiVersion: security.istio.io/v1
kind: PeerAuthentication
metadata:
  name: default
  namespace: foo
spec:
  mtls:
    mode: STRICT

要覆盖整个网格,就把它放在根命名空间;按工作负载设置时使用 selector,按端口设置例外时使用 portLevelMtls。mTLS 迁移 任务文档的结果展示了这种差别——启用 STRICT 之后,只有来自没有 sidecar 的 legacy 命名空间的 curl 会失败。在 Ambient 模式下,由节点上的 ztunnel 而不是 sidecar,通过 HBONE 协议负责 mTLS,因此不支持 DISABLE 模式。

Istio 的 安全最佳实践 明确写出了 mTLS 的局限。mTLS 是认证而不是授权,所以任何持有有效证书的人都能访问到服务,要锁定访问就必须以 default-deny 模式配置 AuthorizationPolicy。sidecar 只拦截 TCP,UDP 和 ICMP 会直接通过;22 号等少数端口不在 inbound 捕获范围内;而且应用和 sidecar 位于同一个网络和进程命名空间,应用可以删除重定向规则来绕过 sidecar。因此文档建议同时配置 NetworkPolicy,形成纵深防御。

对比 Cilium 透明加密 Istio mTLS
终止位置 节点内核(WireGuard/IPsec) Pod 内的 sidecar Envoy(Ambient 为节点上的 ztunnel)
应用改动 无 无(需要注入 sidecar)
身份 节点密钥(以节点为单位) 工作负载 ServiceAccount 证书(以工作负载为单位)
与授权的结合 与 NetworkPolicy 相互独立 可通过 AuthorizationPolicy 按工作负载、按请求授权
范围 Cilium 管理的端点之间的所有 L3 流量 被 sidecar 拦截的 TCP 流量

升级为什么是安全事项

根据 补丁发布 文档,补丁通常每月发布一次,每个 minor 版本约获得 14 个月的支持——在 12 个月的标准期之后,进入 2 个月的维护模式,此时只修复已分配 CVE 的漏洞、依赖项以及核心组件的问题。版本偏差策略 写道,安全修复只会反向移植(backport)到最近三个 minor 分支。超出支持窗口的集群,即使漏洞已经公开,也拿不到可用的修复版本,而停留在这种状态本身就是缺陷。保护集群 文档要求订阅 kubernetes-announce 邮件组以接收安全公告,kubeadm 证书文档则写道,立即升级到最新补丁并保持在受支持的 minor 版本上,才是保障安全的办法。

偏差策略决定升级顺序,这一点也与安全相关。minor 版本不能跳过,所以落后两个版本的集群必须经历两次升级,拖得越久,追赶的成本就越高。这就是必须把升级当作例行工作的原因。

最小化宿主机操作系统

Kubernetes 文档直接谈到节点操作系统的,是关于边界的内容。保护集群 文档指出,kubelet 默认允许未经认证的访问,因此生产集群必须启用 kubelet 的认证与授权;etcd 要隔离在防火墙之后,只让 API 服务器能够访问;要限制 Pod 访问云元数据 API;关闭不使用的 alpha 和 beta 功能;凭据则要使用较短的有效期并经常轮换。安全检查清单 写道,不要把 API 服务器、kubelet API 和 etcd 暴露到互联网,敏感程度不同的工作负载要拆分到不同的节点上。

在此之上叠加的操作系统加固,原则是凡是运行 kubelet、容器运行时和 CNI 不需要的,一律不留。不需要的服务和软件包要删掉——没有运行的守护进程即使有漏洞也不构成攻击面,没有安装的软件包也不需要打补丁。SSH 方面,根据 OpenSSH 的 sshd_config 手册,PermitRootLogin 的默认值是 prohibit-password,PasswordAuthentication 的默认值是 yes,所以在节点上,关闭密码认证、只保留密钥认证是通常的配置。内核模块可以通过 内核 sysctl 文档 中的 kernel.modules_disabled 禁止加载,但一旦设为 1,就既不能加载也不能卸载模块,而且无法恢复,因此只有在把所需模块(容器运行时和 CNI 用到的模块)全部加载完之后才能启用。本节关于 SSH 和内核模块的内容,依据的是各工具的官方手册,而不是 Kubernetes 文档;至于该删除哪些软件包,取决于发行版和 CNI,所以这里不列出清单。

在现场相遇的样子

启用 STRICT 之后,监控中断了。如果没有 sidecar 的 Prometheus 一直在抓取网格内的工作负载,切换到 STRICT 的同时抓取就会失败。这是跳过了 Istio 迁移文档所说的流程造成的——在 PERMISSIVE 状态下通过仪表板找出以明文进来的客户端,全部迁移完成后再按命名空间逐个锁定。

启用了 Cilium WireGuard,节点之间的通信却断了。云安全组中如果没有放行 UDP 51871,隧道就建立不起来;如果是 IPsec,则要放行 ESP。启用加密只需要两行 Helm 值,但如果不与网络团队对齐这些值所要求的端口,整个 Pod 网络都会停摆。

下一项测验要确认什么

测验会考查这两种加密方式的终止位置差异、同一节点内流量的处理方式、PERMISSIVE 与 STRICT 的区别、mTLS 无法替代的功能、补丁支持期限,以及 modules_disabled 的性质。