土法炼钢兴趣小组的算法知识备份

容器网络加密:WireGuard、IPsec 与透明 mTLS

目录

前置阅读: - 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 密钥交换流程

先看全局对比,再逐个深入:

维度 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+ 行)。设计哲学是”只做一件事,做到极致”:

数据路径

在 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 sent

Calico 中的 WireGuard 支持

Calico 从 v3.13 开始支持 WireGuard。配置方式略有不同,通过 FelixConfiguration 启用:

apiVersion: projectcalico.org/v3
kind: FelixConfiguration
metadata:
  name: default
spec:
  wireguardEnabled: true
  wireguardListeningPort: 51820
  wireguardMTU: 1400

Calico 会为每个节点创建 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 tunnel

IKEv2 密钥协商

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 在容器环境中的主要问题:

  1. 配置复杂度。SA/SP 数量随节点数平方增长。100 个节点需要 9900 对 SA。虽然 Cilium 自动管理,但调试时 ip xfrm state 输出数千行记录,很难定位问题。
  2. 密钥轮换的原子性。更换密钥时需要保证所有节点同时切换。Cilium 通过 key-id 实现滚动更新,但如果有节点短暂不可达,可能出现密钥不一致导致的丢包。
  3. MTU 开销不确定。Tunnel 模式 + AES-GCM + UDP 封装,开销在 50-73 字节之间浮动。在已经用了 VXLAN 的集群中尤其痛苦。
  4. 调试困难。加密后的包在 tcpdump 里只能看到 ESP 头。需要配合 ip xfrm state 找到密钥才能解密。

五、透明 mTLS:应用层 per-connection 加密

什么是透明 mTLS

透明 mTLS 是指在不修改应用代码的情况下,为服务间通信自动添加双向 TLS 认证和加密。“透明”体现在应用发送明文 HTTP/gRPC,基础设施层(sidecar 或 eBPF)拦截流量并自动完成 TLS 握手。

两种实现路径:

  1. Sidecar 模式(Istio/Linkerd):通过 iptables 将 Pod 流量劫持到 Envoy sidecar,由 Envoy 完成 TLS 握手
  2. 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: 不使用 mTLS

Istio 使用 istiod 内置的 CA 签发短期证书(默认 24 小时有效),通过 SDS(Secret Discovery Service)分发给各 Envoy sidecar。

mTLS 的开销

mTLS 的主要开销来自两个方面:

  1. TLS 握手延迟。每个新连接需要 1-RTT(TLS 1.3)或 2-RTT(TLS 1.2)的握手。对于短连接密集型的服务(如 HTTP/1.1 without keep-alive),这个开销显著。
  2. 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%

几个观察:

  1. WireGuard 和 IPsec(有 AES-NI)性能接近,都在 78-81% 的基准水平。ChaCha20 不依赖硬件加速,在没有 AES-NI 的 ARM 服务器上反而更快。
  2. IPsec 没有 AES-NI 时性能断崖式下降到 39%。如果你的节点是低端 ARM 或老旧 x86,优先考虑 WireGuard。
  3. 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 的密钥轮换有两层:

# 强制重新生成 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 status

SPIFFE/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 wireguard

IPsec 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 flush

mTLS 证书过期

# Istio: 检查 proxy 证书
istioctl proxy-config secret deploy/<your-app> | head -5

# Cilium: 检查 mTLS 状态
cilium identity list
hubble observe --type policy-verdict --verdict DROPPED

MTU 问题

加密引入的额外 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 的启用成本低到没有理由不做。


By .