前置阅读: - PKI 证书体系 —— X.509 证书链、CA 信任模型 - 国密 TLS(RFC 8998)vs 标准 TLS 1.3 —— TLS 握手原理
你的 Kubernetes 集群里,Pod 之间的流量默认是明文的。
不是”大部分明文”,是完全明文。任何能在节点上抓包的人,都能看到你的 gRPC payload、数据库查询、甚至 JWT token。在同一个二层网络里,ARP 欺骗就能把别人的流量引到自己这里。跨可用区的流量走的是云厂商的物理网络,你信不信得过那根光纤上不会有第三方设备?
这个问题在单租户、内网环境下可以忍。但一旦涉及多租户隔离、金融合规(PCI-DSS 3.2.1 要求传输加密)、医疗数据保护(HIPAA)、或者等保三级,“默认明文”就变成了不可接受的风险。
这篇文章对比三种主流的容器网络加密方案:WireGuard、IPsec(ESP)和透明 mTLS。不讲概念堆砌,讲清楚每种方案在内核中的数据路径、真实的性能开销、密钥怎么管、什么场景该选哪个。
一、为什么需要容器网络加密
威胁模型
容器网络面临的加密需求来自三个层面:
多租户隔离。Kubernetes 的 NetworkPolicy
只做 L3/L4 访问控制,不做加密。两个租户的 Pod
跑在同一台宿主机上,一个 tcpdump -i any
就能看到对方的流量。即使用了 namespace 隔离和
RBAC,网络层的明文传输仍然是一个真实的攻击面。
合规要求。PCI-DSS 3.2.1 Section 4.1 要求”对在开放公共网络上传输的持卡人数据进行加密”。HIPAA 要求对 ePHI 传输加密。等保三级明确要求”通信保密性”。这些不是建议,是审计红线。
跨 AZ / 跨 Region 流量。即使在同一个云厂商内部,跨可用区的流量走的是物理网络。AWS 在 2023 年才开始默认对跨 AZ 流量加密(Nitro 系统),但 GCP 和 Azure 的实现各有不同。如果你跑混合云或多云架构,节点之间的流量必然经过不受信任的网络。
Pod A (namespace: tenant-a) Pod B (namespace: tenant-b)
| |
| gRPC: {"user":"alice","card":"4111..."} |
| ---- 明文 TCP over VXLAN ------------> |
| |
+--- 同一台 Node,tcpdump 可见 -------------+
加密的位置选择
从协议栈的角度,加密可以发生在三个位置:
| 层级 | 方案 | 加密粒度 | 对应用是否透明 |
|---|---|---|---|
| L3(网络层) | WireGuard、IPsec | per-node 或 per-tunnel | 完全透明 |
| L4(传输层) | TLS/mTLS | per-connection | 需要配置或 sidecar |
| L7(应用层) | 应用自行加密 | per-message | 需要改代码 |
L3 加密的优势是对应用完全透明,缺点是粒度粗——同一对节点之间的所有流量用同一个密钥。L4/L7 加密可以做到 per-connection 甚至 per-request 的细粒度控制,但需要额外基础设施。
二、三种加密方案总览
先看全局对比,再逐个深入:
| 维度 | WireGuard | IPsec (ESP) | 透明 mTLS |
|---|---|---|---|
| 协议层 | L3 (UDP 封装) | L3 (ESP 封装) | L4/L7 (TLS) |
| 加密算法 | ChaCha20-Poly1305 | AES-GCM-128/256 (可选) | TLS 1.3 cipher suites |
| 密钥交换 | Noise IK (1-RTT) | IKEv2 (2-RTT) | ECDHE (TLS handshake) |
| 内核态/用户态 | 内核态 (Linux 5.6+) | 内核态 (XFRM) | 用户态 (Envoy) 或 eBPF |
| 加密粒度 | per-node-pair | per-SA (可 per-subnet) | per-connection |
| MTU 开销 | ~60 bytes | ~50-73 bytes | ~25-40 bytes (TLS record) |
| CNI 支持 | Cilium, Calico | Cilium, Calico, Canal | Cilium, Istio, Linkerd |
| 配置复杂度 | 低 | 高 | 中 |
| 硬件加速 | 无 (ChaCha20 软件快) | AES-NI 支持 | AES-NI (取决于 cipher) |
三种方案不是互相替代的关系。WireGuard 和 IPsec 解决的是”节点之间的网络层加密”,mTLS 解决的是”连接级别的端到端加密”。你可以同时启用 WireGuard(节点间)和 mTLS(服务间),它们工作在不同的层。
三、WireGuard:内核态高性能加密
为什么 WireGuard 成为首选
WireGuard 在 2020 年被合入 Linux 5.6 内核主线,代码量只有约 4000 行(对比 IPsec 的 40000+ 行)。设计哲学是”只做一件事,做到极致”:
- 固定算法:ChaCha20-Poly1305(加密)、Curve25519(密钥交换)、BLAKE2s(哈希)、HKDF(密钥派生)
- 无算法协商:不像 TLS 和 IPsec 需要 cipher suite negotiation,省掉了一大类降级攻击
- 1-RTT 密钥交换:基于 Noise Protocol Framework 的 IK 模式,一个来回完成握手
数据路径
在 Cilium 中启用 WireGuard 后,数据路径如下:
Pod A (10.0.1.100)
|
| 原始 IP 包
v
cilium_host (BPF routing)
|
| 判断目标 Pod 在远端节点,需要加密
v
cilium_wg0 (WireGuard interface)
|
| ChaCha20-Poly1305 加密
| 封装为 UDP:51871
v
eth0 (Node A: 192.168.1.10)
|
| Outer: 192.168.1.10:random -> 192.168.1.20:51871 (UDP)
| Inner: 加密的原始 IP 包
v
======= 物理网络(加密传输)=======
|
v
eth0 (Node B: 192.168.1.20)
|
v
cilium_wg0 (解密)
|
v
cilium_host (BPF routing)
|
v
Pod B (10.0.2.200)
关键点:加密和解密都发生在内核态,不需要经过用户态。这是 WireGuard 性能优势的核心原因。
Noise IK 握手协议
WireGuard 使用 Noise Protocol Framework 的 IK 模式。IK 代表 Initiator 预知 Responder 的静态公钥(在 Cilium 中通过 CiliumNode CRD 分发)。
Initiator (Node A) Responder (Node B)
| |
| 已知: PubB (通过 CiliumNode CRD) |
| |
|-- Handshake Initiation ---------------------->|
| sender_index |
| unencrypted_ephemeral (EphA_pub) |
| encrypted_static (PubA, 用 DH 密钥加密) |
| encrypted_timestamp |
| |
|<-- Handshake Response -----------------------|
| sender_index |
| receiver_index |
| unencrypted_ephemeral (EphB_pub) |
| encrypted_nothing (确认密钥一致) |
| |
| 双方各自派生: |
| HKDF(DH(PrivA,PubB), |
| DH(EphA,PubB), |
| DH(EphA,EphB)) |
| => Tx Key, Rx Key |
| |
|== Transport Data (ChaCha20-Poly1305) ======>|
|<= Transport Data (ChaCha20-Poly1305) =======|
密钥每 2 分钟自动轮换(rekey),无需人工干预。如果 120 秒没有收到合法数据包,会话静默过期,没有”断开连接”的概念。
Cilium 中的密钥分发
Cilium 利用 Kubernetes CRD 来分发 WireGuard 公钥:
apiVersion: cilium.io/v2
kind: CiliumNode
metadata:
name: worker-01
spec:
encryption:
key: 0
wireguard:
publicKey: "aB3dEfGhIjKlMnOpQrStUvWxYz0123456789ABC="每个节点的 Cilium Agent 启动时生成密钥对,将公钥写入对应的 CiliumNode 资源。其他节点通过 Watch 机制获取远端公钥,配置本地 WireGuard peer。
# 查看节点上的 WireGuard 接口状态
cilium status --verbose | grep -A5 Encryption
# 查看 WireGuard 接口详情
wg show cilium_wg0
# 输出示例
# interface: cilium_wg0
# public key: aB3dEfGhIjKlMnOpQrStUvWxYz0123456789ABC=
# private key: (hidden)
# listening port: 51871
#
# peer: xY9zAbCdEfGhIjKlMnOpQrStUvWxYz0123456789D=
# endpoint: 192.168.1.20:51871
# allowed ips: 10.0.2.0/24
# latest handshake: 47 seconds ago
# transfer: 1.24 GiB received, 856.32 MiB sentCalico 中的 WireGuard 支持
Calico 从 v3.13 开始支持 WireGuard。配置方式略有不同,通过 FelixConfiguration 启用:
apiVersion: projectcalico.org/v3
kind: FelixConfiguration
metadata:
name: default
spec:
wireguardEnabled: true
wireguardListeningPort: 51820
wireguardMTU: 1400Calico 会为每个节点创建 wireguard.cali
接口,密钥通过 Node 资源的 annotation 分发。
四、IPsec(ESP):传统方案的得与失
ESP 封装格式
IPsec 的 Encapsulating Security Payload(ESP)有两种模式:
Tunnel 模式(容器网络常用):
+------------------+----------+------------------+----------+---------+-----+
| Outer IP Header | ESP Hdr | Inner IP Header | Payload | ESP Trl | ICV |
| (Node A->Node B) | SPI+Seq | (Pod A->Pod B) | (data) | Pad+NH | |
+------------------+----------+------------------+----------+---------+-----+
|<------------ 加密范围 ------------>|
|<------------------ 认证范围 -------------------->|
Transport 模式:
+------------------+----------+----------+---------+-----+
| Original IP Hdr | ESP Hdr | Payload | ESP Trl | ICV |
| (modified) | SPI+Seq | (data) | Pad+NH | |
+------------------+----------+----------+---------+-----+
|<-- 加密 -->|
|<--------- 认证 ----------->|
ESP Header 关键字段: - SPI(Security Parameters Index):标识使用哪个 SA(Security Association) - Sequence Number:防重放攻击 - ICV(Integrity Check Value):认证标签,通常是 AES-GCM 的 tag
XFRM 框架
Linux 内核通过 XFRM(Transform)框架实现 IPsec。核心数据结构是 SA(Security Association)和 SP(Security Policy):
# 查看 Security Association
ip xfrm state
# 输出示例
# src 192.168.1.10 dst 192.168.1.20
# proto esp spi 0xc3a4f521 reqid 1 mode tunnel
# replay-window 0 flag af-unspec
# auth-trunc hmac(sha256) 0x... 128
# enc cbc(aes) 0x...
# encap type espinudp sport 4500 dport 4500 addr 0.0.0.0
# 查看 Security Policy
ip xfrm policy
# 输出示例
# src 10.0.1.0/24 dst 10.0.2.0/24
# dir out priority 2975
# tmpl src 192.168.1.10 dst 192.168.1.20
# proto esp spi 0xc3a4f521 reqid 1 mode tunnelIKEv2 密钥协商
IPsec 使用 IKEv2(Internet Key Exchange v2)进行密钥协商。相比 WireGuard 的 1-RTT,IKEv2 需要 2 个阶段:
IKE_SA_INIT (Phase 1 - 建立 IKE SA):
Initiator -> Responder: SAi1, KEi, Ni
Responder -> Initiator: SAr1, KEr, Nr
IKE_AUTH (Phase 2 - 认证 + 建立 Child SA):
Initiator -> Responder: SK{IDi, AUTH, SAi2, TSi, TSr}
Responder -> Initiator: SK{IDr, AUTH, SAr2, TSi, TSr}
在 Cilium 中,IPsec 密钥通过 Kubernetes Secret 管理:
# 生成 IPsec 密钥并创建 Secret
KEY=$(dd if=/dev/urandom count=20 bs=1 2>/dev/null | xxd -p -c 40)
kubectl create -n kube-system secret generic cilium-ipsec-keys \
--from-literal=keys="3 rfc4106(gcm(aes)) $KEY 128"
# 格式: <key-id> <algorithm> <key-hex> <key-size>IPsec 的痛点
IPsec 在容器环境中的主要问题:
- 配置复杂度。SA/SP
数量随节点数平方增长。100 个节点需要 9900 对 SA。虽然 Cilium
自动管理,但调试时
ip xfrm state输出数千行记录,很难定位问题。 - 密钥轮换的原子性。更换密钥时需要保证所有节点同时切换。Cilium 通过 key-id 实现滚动更新,但如果有节点短暂不可达,可能出现密钥不一致导致的丢包。
- MTU 开销不确定。Tunnel 模式 + AES-GCM + UDP 封装,开销在 50-73 字节之间浮动。在已经用了 VXLAN 的集群中尤其痛苦。
- 调试困难。加密后的包在 tcpdump
里只能看到 ESP 头。需要配合
ip xfrm state找到密钥才能解密。
五、透明 mTLS:应用层 per-connection 加密
什么是透明 mTLS
透明 mTLS 是指在不修改应用代码的情况下,为服务间通信自动添加双向 TLS 认证和加密。“透明”体现在应用发送明文 HTTP/gRPC,基础设施层(sidecar 或 eBPF)拦截流量并自动完成 TLS 握手。
两种实现路径:
- Sidecar 模式(Istio/Linkerd):通过 iptables 将 Pod 流量劫持到 Envoy sidecar,由 Envoy 完成 TLS 握手
- eBPF 模式(Cilium):在内核态拦截 socket 操作,在 L4 层完成 TLS 加密
Sidecar (Istio): App -> iptables REDIRECT -> Envoy:15001 -> TLS 1.3 -> Remote Envoy
eBPF (Cilium): App -> BPF_PROG_TYPE_SOCK_OPS -> kTLS 加密 -> Remote Node
SPIFFE / SPIRE 身份体系
透明 mTLS 的核心问题是:双方怎么互相认证身份?答案是 SPIFFE(Secure Production Identity Framework For Everyone)。
SPIFFE 定义了一种通用的工作负载身份格式——SPIFFE ID:
spiffe://cluster.local/ns/production/sa/payment-service
| | | |
协议 信任域 命名空间 服务账号
SVID (SPIFFE Verifiable Identity Document):
- 就是一个 X.509 证书
- Subject Alternative Name (SAN) 里放 SPIFFE ID
- 有效期短(通常 1-24 小时)
- 由 SPIRE Server 签发
SPIRE(SPIFFE Runtime Environment)是 SPIFFE 的参考实现,负责证书的签发和轮换。架构上由中心化的 SPIRE Server 签发 SVID,通过每个节点上的 SPIRE Agent 分发给 Workload。Workload 通过 Unix Domain Socket 调用 Workload API 获取自己的 SVID 和 Trust Bundle。
Cilium 的 mTLS 实现
Cilium 从 1.14 版本开始支持透明 mTLS(Mutual Authentication),使用 SPIFFE 作为身份框架:
# CiliumNetworkPolicy 启用 mTLS 认证
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: require-mtls
namespace: production
spec:
endpointSelector:
matchLabels:
app: payment-api
ingress:
- fromEndpoints:
- matchLabels:
app: order-service
authentication:
mode: "required" # 强制 mTLS 认证Cilium 的 mTLS 与 WireGuard 可以同时启用。WireGuard 提供节点间的全量加密(L3),mTLS 提供服务间的身份认证(L4/L7)。两者是互补关系。
Istio mTLS 的 PeerAuthentication
Istio 通过 PeerAuthentication 资源控制 mTLS 策略:
apiVersion: security.istio.io/v1
kind: PeerAuthentication
metadata:
name: default
namespace: production
spec:
mtls:
mode: STRICT # STRICT | PERMISSIVE | DISABLE
# STRICT: 只接受 mTLS 连接
# PERMISSIVE: 同时接受明文和 mTLS(迁移过渡期用)
# DISABLE: 不使用 mTLSIstio 使用 istiod 内置的 CA 签发短期证书(默认 24 小时有效),通过 SDS(Secret Discovery Service)分发给各 Envoy sidecar。
mTLS 的开销
mTLS 的主要开销来自两个方面:
- TLS 握手延迟。每个新连接需要 1-RTT(TLS 1.3)或 2-RTT(TLS 1.2)的握手。对于短连接密集型的服务(如 HTTP/1.1 without keep-alive),这个开销显著。
- Sidecar 的内存和 CPU。每个 Pod 的 Envoy sidecar 占用 50-100 MiB 内存,TLS 加解密消耗额外 CPU。在大规模集群中(5000+ Pod),sidecar 的资源开销是一个现实问题。
六、性能开销实测
测试环境
硬件: 2x Intel Xeon Gold 6348 (Ice Lake, 支持 AES-NI)
内存: 256 GiB DDR4-3200
网卡: Intel E810 25GbE (双端口)
内核: Linux 6.6.18
K8s: v1.29.2
CNI: Cilium 1.15.3
测试工具: iperf3 (TCP/UDP), netperf (延迟), wrk2 (HTTP)
吞吐量对比(iperf3 TCP,单流)
方案 吞吐量 (Gbps) 相对基准
─────────────────────────────────────────────────────
无加密 (baseline) 23.8 100%
WireGuard (ChaCha20) 18.6 78%
IPsec (AES-GCM-128, AES-NI) 19.2 81%
IPsec (AES-GCM-256, AES-NI) 18.5 78%
IPsec (AES-CBC-256, 无 NI) 9.4 39%
mTLS (Envoy, AES-GCM-128) 12.1 51%
mTLS (Cilium eBPF, kTLS) 16.8 71%
几个观察:
- WireGuard 和 IPsec(有 AES-NI)性能接近,都在 78-81% 的基准水平。ChaCha20 不依赖硬件加速,在没有 AES-NI 的 ARM 服务器上反而更快。
- IPsec 没有 AES-NI 时性能断崖式下降到 39%。如果你的节点是低端 ARM 或老旧 x86,优先考虑 WireGuard。
- Envoy sidecar 的 mTLS 吞吐量最低(51%),因为数据需要在用户态和内核态之间多次拷贝。Cilium 的 eBPF + kTLS 方案好很多(71%)。
延迟对比(netperf TCP_RR,P99)
方案 P50 (us) P99 (us) P99.9 (us)
───────────────────────────────────────────────────────────────────
无加密 28 45 82
WireGuard 32 52 96
IPsec (AES-GCM-128) 34 58 110
mTLS (Envoy) 78 145 320
mTLS (Cilium eBPF) 38 65 125
延迟的差异更能说明问题。WireGuard 只增加了约 4us(P50),因为加密完全在内核态完成。Envoy mTLS 增加了约 50us(P50),这 50us 是数据从内核拷贝到 Envoy 用户态、完成加密、再拷回内核的代价。
CPU 开销(wrk2 HTTP 固定 QPS)
在 100K RPS 的 HTTP 请求下,各方案的 CPU 使用率:
方案 CPU (cores) 相对基准
──────────────────────────────────────────────────────
无加密 2.1 100%
WireGuard 2.8 133%
IPsec (AES-GCM-128, AES-NI) 2.6 124%
mTLS (Envoy) 5.2 248%
mTLS (Cilium eBPF) 3.4 162%
Envoy sidecar 在 100K RPS 下消耗了 2.5 倍于基准的 CPU。如果你的服务本身就是 CPU 密集型的,这个开销需要认真评估。
硬件加速的影响
AES-NI 对 IPsec 的影响最大,对 WireGuard 无影响(ChaCha20 是纯软件算法):
# 检查 CPU 是否支持 AES-NI
grep -o aes /proc/cpuinfo | head -1
# 检查内核是否使用了硬件加速的 AES
cat /proc/crypto | grep -A4 "name.*gcm(aes)"
# 应该看到 "driver: generic" (软件) 或 "driver: aesni" (硬件)
# 简单的 OpenSSL 性能测试
openssl speed -evp aes-128-gcm
openssl speed -evp chacha20-poly1305在 Intel Ice Lake 上的 OpenSSL 测试结果:
算法 16 bytes 256 bytes 1024 bytes 8192 bytes
────────────────────────────────────────────────────────────────────────
aes-128-gcm (AES-NI) 1.2 GB/s 6.8 GB/s 12.4 GB/s 14.8 GB/s
chacha20-poly1305 0.8 GB/s 3.2 GB/s 5.1 GB/s 5.6 GB/s
aes-128-gcm (无 NI) 0.3 GB/s 0.9 GB/s 1.2 GB/s 1.4 GB/s
结论:在有 AES-NI 的现代 x86 服务器上,AES-GCM 的吞吐量是 ChaCha20 的 2.5 倍。但 ChaCha20 在没有硬件加速时仍有 5.6 GB/s,而 AES-GCM 无硬件加速时只有 1.4 GB/s。
七、密钥管理与轮换
WireGuard 密钥管理
WireGuard 的密钥管理在 Cilium 和 Calico 中已经高度自动化:
生命周期:
1. 节点启动 -> Cilium Agent 生成 Curve25519 密钥对
2. 公钥写入 CiliumNode CRD (或 Calico Node annotation)
3. 其他节点通过 Watch 获取公钥,配置 WireGuard peer
4. Noise IK 握手建立会话
5. 会话密钥每 2 分钟自动 rekey
6. 节点下线 -> CRD 删除 -> 其他节点移除 peer
WireGuard 的密钥轮换有两层:
- 会话密钥(对称密钥):每 2 分钟自动轮换,由 Noise 协议内建机制完成,无需外部干预
- 静态密钥(非对称密钥对):目前 Cilium 在 Agent 重启时重新生成。手动轮换需要重启 Agent
# 强制重新生成 WireGuard 密钥(重启 Agent)
kubectl -n kube-system rollout restart daemonset/cilium
# 验证密钥已更新
kubectl get ciliumnodes -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.wireguard.publicKey}{"\n"}{end}'IPsec SA/SP 管理
IPsec 的密钥管理更复杂,因为 SA 是有状态的:
SA 生命周期:
1. 管理员创建 Kubernetes Secret (PSK 或证书)
2. Cilium 通过 IKEv2 或手动配置建立 SA
3. 每对节点之间建立双向 SA (inbound + outbound)
4. SA 有 lifetime (默认 8h),接近过期时自动 rekey
5. 密钥轮换: 更新 Secret 中的 key-id
Cilium IPsec 密钥轮换的具体步骤:
# 1. 生成新密钥
NEW_KEY=$(dd if=/dev/urandom count=20 bs=1 2>/dev/null | xxd -p -c 40)
# 2. 查看当前 key-id
kubectl -n kube-system get secret cilium-ipsec-keys -o jsonpath='{.data.keys}' | base64 -d
# 输出: 3 rfc4106(gcm(aes)) <old-key> 128
# 3. 更新为新 key-id (递增)
kubectl -n kube-system create secret generic cilium-ipsec-keys \
--from-literal=keys="4 rfc4106(gcm(aes)) $NEW_KEY 128" \
--dry-run=client -o yaml | kubectl apply -f -
# 4. Cilium 会自动检测 Secret 变化:
# - 先用新 key-id=4 建立新 SA (inbound)
# - 等所有节点都有了 key-id=4 的 inbound SA
# - 切换 outbound SA 到 key-id=4
# - 删除旧 key-id=3 的 SA
# 5. 监控轮换进度
cilium encrypt statusSPIFFE/SPIRE 证书轮换
SPIRE 管理的 X.509 证书(SVID)轮换是全自动的:证书有效期通常为 1-24 小时,Workload 在剩余 50% 有效期时自动续签,旧证书在 grace period 内仍有效。
Istio 的证书管理类似,使用 istiod 内置 CA:
# 查看 workload 证书信息
istioctl proxy-config secret deploy/payment-api -o json | \
jq '.dynamicActiveSecrets[0].secret.tlsCertificate.certificateChain' | \
base64 -d | openssl x509 -text -noout
# Subject Alternative Name:
# URI:spiffe://cluster.local/ns/production/sa/payment-api密钥管理对比
| 维度 | WireGuard | IPsec | mTLS (SPIRE/Istio) |
|---|---|---|---|
| 密钥类型 | Curve25519 密钥对 | PSK 或 X.509 | X.509 (SVID) |
| 分发机制 | CiliumNode CRD | K8s Secret | Workload API / SDS |
| 会话密钥轮换 | 每 2 分钟自动 | SA lifetime (8h) | TLS session (连接级) |
| 静态密钥轮换 | Agent 重启 | Secret 更新 + key-id | 自动续签 (1-24h) |
| 轮换停机 | 无 (Noise 支持无缝切换) | 短暂丢包风险 | 无 (grace period) |
| 密钥泄露影响 | 单节点对 | 全集群 (共享 PSK) | 单 workload |
八、加密方案选型决策树
选型不是”哪个最好”的问题,而是”你的场景需要什么”。
Q1: 你需要加密所有 Pod 间流量,还是只加密特定服务间流量?
│
├─ 所有流量 ──> Q2: 你的节点 CPU 是否支持 AES-NI?
│ │
│ ├─ 支持 ──> Q3: 你是否已经在用 IPsec(遗留系统)?
│ │ │
│ │ ├─ 是 ──> 继续用 IPsec (AES-GCM)
│ │ │ 配合 Cilium 自动化管理
│ │ │
│ │ └─ 否 ──> 用 WireGuard
│ │ 配置简单、性能好、维护少
│ │
│ └─ 不支持 (ARM/旧 x86) ──> 用 WireGuard
│ ChaCha20 无需硬件加速
│
└─ 特定服务 ──> Q4: 你是否需要服务级别的身份认证?
│
├─ 是 ──> Q5: 你是否已经有服务网格(Istio/Linkerd)?
│ │
│ ├─ 是 ──> 用服务网格的 mTLS
│ │ 已有基础设施,直接启用
│ │
│ └─ 否 ──> Cilium mTLS (eBPF)
│ 无 sidecar 开销
│
└─ 否 ──> WireGuard 全量加密
最简单的"一刀切"方案
几个常见的组合:
| 场景 | 推荐方案 | 理由 |
|---|---|---|
| 小型集群,快速合规 | WireGuard | 一个配置搞定,性能损失可控 |
| 金融/医疗,需要审计 | WireGuard + mTLS | L3 全量加密 + L7 身份审计 |
| 已有 Istio 的存量集群 | Istio mTLS | 利用现有基础设施 |
| 混合云/多云 | WireGuard | 跨云 IPsec 兼容性差,WireGuard 更通用 |
| 大量 ARM 节点 | WireGuard | ChaCha20 在 ARM 上性能优于 AES 软件实现 |
| 对延迟极度敏感 | WireGuard (非 Envoy mTLS) | 内核态加密,P99 增量最小 |
九、实验:Cilium 集群启用 WireGuard 加密
环境准备
# 使用 kind 创建测试集群(至少 2 个 worker 节点)
cat <<'EOF' > kind-config.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
- role: worker
networking:
disableDefaultCNI: true
podSubnet: "10.244.0.0/16"
serviceSubnet: "10.96.0.0/12"
EOF
kind create cluster --name encryption-lab --config kind-config.yaml
# 安装 Cilium(启用 WireGuard)
helm repo add cilium https://helm.cilium.io/
helm repo update
helm install cilium cilium/cilium --version 1.15.3 \
--namespace kube-system \
--set encryption.enabled=true \
--set encryption.type=wireguard \
--set encryption.wireguard.userspaceFallback=false \
--set kubeProxyReplacement=true \
--set k8sServiceHost=encryption-lab-control-plane \
--set k8sServicePort=6443验证 WireGuard 已启用
# 等待 Cilium 就绪
cilium status --wait
# 检查加密状态
cilium encrypt status
# 输出:
# Encryption: Wireguard
# Wireguard interfaces: cilium_wg0
# Keys in use: 1
# Errors: 0
# 查看 WireGuard 接口
kubectl -n kube-system exec -it ds/cilium -- wg show cilium_wg0
# interface: cilium_wg0
# public key: <base64-key>
# private key: (hidden)
# listening port: 51871
#
# peer: <base64-key>
# endpoint: 172.18.0.3:51871
# allowed ips: 10.244.1.0/24
# latest handshake: 12 seconds ago
# transfer: 45.21 KiB received, 38.67 KiB sent
# 检查 CiliumNode 资源中的公钥
kubectl get ciliumnodes -o wide部署测试 Pod
# 在两个不同节点上部署测试 Pod
cat <<'EOF' | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: client
labels:
app: client
spec:
nodeName: encryption-lab-worker
containers:
- name: toolbox
image: nicolaka/netshoot
command: ["sleep", "infinity"]
---
apiVersion: v1
kind: Pod
metadata:
name: server
labels:
app: server
spec:
nodeName: encryption-lab-worker2
containers:
- name: iperf
image: nicolaka/netshoot
command: ["sleep", "infinity"]
EOF
# 等待 Pod 就绪
kubectl wait --for=condition=Ready pod/client pod/server --timeout=60s
# 获取 server Pod 的 IP
SERVER_IP=$(kubectl get pod server -o jsonpath='{.status.podIP}')
echo "Server IP: $SERVER_IP"tcpdump 验证加密
在节点上抓包,确认看不到明文数据:
# 在 server 上启动 iperf3
kubectl exec server -- iperf3 -s -p 5201 &
# 进入 Cilium Agent 容器抓包
kubectl -n kube-system exec -it ds/cilium -- bash
# 在 eth0 上抓 Pod 子网的流量——应该抓不到(已被 WireGuard 封装)
tcpdump -i eth0 -c 20 -nn 'host 10.244.0.0/16'
# 抓 WireGuard 封装后的流量
tcpdump -i eth0 -c 20 -nn 'udp port 51871'
# 16:23:45.123456 IP 172.18.0.2.51871 > 172.18.0.3.51871: UDP, length 148
# 看到的是加密数据,无法看到原始 payload
# 抓 WireGuard 接口上的解密流量
tcpdump -i cilium_wg0 -c 10 -nn
# 16:23:45.123456 IP 10.244.0.5.43210 > 10.244.1.8.5201: TCP ...
# 解密后的原始流量验证逻辑:eth0 上只有 UDP:51871
的加密包,cilium_wg0 上能看到解密后的原始 IP
包。
性能测试
# TCP 吞吐量测试
kubectl exec server -- iperf3 -s -p 5201 -D
kubectl exec client -- iperf3 -c $SERVER_IP -p 5201 -t 30 -P 4
# TCP 延迟测试
kubectl exec server -- netserver -p 12865 &
kubectl exec client -- netperf -H $SERVER_IP -p 12865 -t TCP_RR -l 30 -- \
-o min_latency,mean_latency,p99_latency,max_latency
# 对比: 禁用 WireGuard 再测(仅限测试环境)
helm upgrade cilium cilium/cilium --version 1.15.3 \
--namespace kube-system \
--set encryption.enabled=false \
--reuse-values
# 重新运行相同测试,对比结果监控加密指标
# Cilium WireGuard Prometheus 指标
kubectl -n kube-system exec -it ds/cilium -- \
curl -s localhost:9962/metrics | grep wireguard
# 关键指标:
# cilium_wireguard_peers - peer 数量(应等于节点数-1)
# cilium_wireguard_bytes_sent - 发送的加密字节数
# cilium_wireguard_bytes_received - 接收的加密字节数十、常见问题与调试
WireGuard 握手失败
# 症状: 跨节点 Pod 不通
# 检查 WireGuard 接口状态
wg show cilium_wg0
# 如果 "latest handshake" 显示 "never",说明握手未完成
# 可能原因:
# 1. UDP:51871 被防火墙/安全组阻挡
# 2. 节点之间的 NAT 不支持 UDP
# 3. CiliumNode 公钥未同步
# 排查步骤:
# 1. 检查端口连通性
kubectl -n kube-system exec ds/cilium -- \
bash -c "echo test | nc -u -w1 <remote-node-ip> 51871"
# 2. 检查 CiliumNode 公钥
kubectl get ciliumnodes -o jsonpath='{range .items[*]}{.metadata.name}: {.spec.wireguard.publicKey}{"\n"}{end}'
# 3. 查看 Cilium Agent 日志
kubectl -n kube-system logs ds/cilium | grep -i wireguardIPsec SA 不一致
# 症状: 部分节点之间丢包
# 检查 SA 状态
ip xfrm state | head -40
ip xfrm policy | head -20
# 检查 xfrm 计数器中的错误
ip -s xfrm state | grep -E "(replay|error|invalid)"
# 强制重建 SA
cilium encrypt flushmTLS 证书过期
# Istio: 检查 proxy 证书
istioctl proxy-config secret deploy/<your-app> | head -5
# Cilium: 检查 mTLS 状态
cilium identity list
hubble observe --type policy-verdict --verdict DROPPEDMTU 问题
加密引入的额外 header 会减小有效 MTU:
# 检查各接口的 MTU
ip link show eth0 # 通常 1500
ip link show cilium_wg0 # 应该是 1500 - 60 = 1440
# 在 Cilium Helm values 中设置:
# --set MTU=1440
# 或让 Cilium 自动检测:
# --set enableAutoMTU=true
# 验证 Path MTU Discovery
kubectl exec client -- ping -M do -s 1400 $SERVER_IP十一、总结
三种容器网络加密方案,各有适用场景:
WireGuard 是当前的首选方案。内核态实现,配置极简(Cilium 一个参数开启),密钥自动管理和轮换,性能开销约 20%。唯一的限制是加密粒度是 per-node-pair,无法做到 per-service 的细粒度控制。
IPsec 适合有遗留系统或需要 FIPS 合规的场景。AES-GCM 有 FIPS 140-2 认证(ChaCha20 没有),在有 AES-NI 的硬件上性能与 WireGuard 相当。但配置和调试复杂度高一个量级。
透明 mTLS 解决的是不同层面的问题——服务身份认证和 per-connection 加密。如果你需要知道”是谁在跟我通信”而不仅仅是”通信是否加密”,mTLS 是必须的。Cilium eBPF 方案比 Envoy sidecar 性能好很多,值得关注。
实际生产中,最常见的组合是 WireGuard(L3 全量加密)+ mTLS(关键服务间身份认证)。两者互补,不冲突。
推荐路径:
1. 先启用 WireGuard ──> 最小改动,立即合规
2. 对关键服务启用 mTLS ──> 添加身份认证维度
3. 用 Hubble/Prometheus 监控加密指标 ──> 确保持续生效
如果你还没有加密容器网络流量,现在就开始。WireGuard 的启用成本低到没有理由不做。