mTLS(双向 TLS)详解
普通 TLS vs mTLS
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 普通 TLS(单向): 客户端 ──────────────────────► 服务端 "你是谁?给我看你的证书" 客户端 ◄────────────────────── 服务端 "这是我的证书" 客户端验证服务端证书 ✓ (服务端不验证客户端身份)
mTLS(双向): 客户端 ──────────────────────► 服务端 "你是谁?给我看你的证书" 客户端 ◄────────────────────── 服务端 "这是我的证书,你也给我看你的" 客户端 ──────────────────────► 服务端 "这是我的证书" 双方互相验证 ✓✓
|
| 对比 |
普通 TLS |
mTLS |
| 服务端出示证书 |
是 |
是 |
| 客户端出示证书 |
否 |
是 |
| 客户端验证服务端 |
是 |
是 |
| 服务端验证客户端 |
否 |
是 |
| 典型场景 |
浏览器访问网站 |
微服务间通信 |
为什么微服务需要 mTLS
1 2 3 4 5 6 7 8 9 10 11 12
| 没有 mTLS: Pod A ──── 明文 HTTP ────► Pod B 任何能访问网络的人都可以: - 窃听流量内容 - 伪装成 Pod A 调用 Pod B - 伪装成 Pod B 响应 Pod A
有 mTLS: Pod A ──── 加密 + 双向认证 ────► Pod B - 流量加密(无法窃听) - Pod B 确认对方确实是 Pod A(身份认证) - Pod A 确认对方确实是 Pod B(身份认证)
|
Istio 中的 mTLS 实现
Istio 通过 sidecar 代理(Envoy) 在应用无感知的情况下自动实现 mTLS:
1 2 3 4 5 6 7 8 9 10 11
| Pod A Pod B ┌──────────────────┐ ┌──────────────────┐ │ app container │ │ app container │ │ curl http://B │ │ httpbin :80 │ │ │ │ │ ▲ │ │ ▼ │ │ │ │ │ ┌────────────┐ │ │ ┌────────────┐ │ │ │ istio-proxy│ │ ══ mTLS 加密 ══► │ │ istio-proxy│ │ │ │ (Envoy) │──┼────────────────────┼──│ (Envoy) │ │ │ └────────────┘ │ 证书由 istiod │ └────────────┘ │ └──────────────────┘ 自动签发和轮换 └──────────────────┘
|
应用层发出的是明文 HTTP,Envoy sidecar 自动:
- 出站:用 istiod 签发的证书加密 + 附上客户端证书
- 入站:验证对方证书 + 解密 → 转发明文给应用
证书管理(自动化)
1 2 3 4 5 6 7 8 9 10
| istiod(控制面) │ ├─ 内置 CA(证书颁发机构) │ ├─ 为每个 Pod 签发唯一的 SPIFFE 身份证书 │ 格式: spiffe://cluster.local/ns/<namespace>/sa/<service-account> │ ├─ 证书有效期短(默认 24h),自动轮换 │ └─ 通过 SDS (Secret Discovery Service) 推送给 Envoy
|
PeerAuthentication — mTLS 策略控制
PeerAuthentication 控制某个 namespace 的入站 mTLS 策略:
1 2 3 4 5 6 7 8 9 10 11 12
| apiVersion: security.istio.io/v1 kind: PeerAuthentication metadata: name: strict-mtls namespace: my-namespace spec: mtls: mode: STRICT
|
三种模式:
| 模式 |
入站明文 |
入站 mTLS |
使用场景 |
STRICT |
拒绝 |
接受 |
生产环境,强制加密 |
PERMISSIVE |
接受 |
接受 |
迁移过渡期(默认) |
DISABLE |
接受 |
拒绝 |
不推荐,部分版本不支持 |
Auto-mTLS — 出站自动升级
出站方向没有”开关”,Istio 全自动处理:
1 2 3 4 5 6 7
| 调用方 Envoy 发起出站请求时:
istiod: "目标 Pod 有 sidecar 吗?" │ ├─ 有 → 自动升级为 mTLS(附上客户端证书) │ └─ 没有 → 保持明文 HTTP(不强制加密)
|
这就是为什么在当前测试中,调用方 Pod 不需要也没有 mTLS “开关” — auto-mTLS 根据目标自动决定。
与当前测试的关系
在 values.yaml 中定义了 4 个 namespace:
1 2 3 4
| old-strict ← PeerAuthentication STRICT + 旧版 sidecar new-strict ← PeerAuthentication STRICT + 新版 sidecar old-permissive ← 默认 PERMISSIVE(无 PA) + 旧版 sidecar new-permissive ← 默认 PERMISSIVE(无 PA) + 新版 sidecar
|
mTLS 模式作用在被调方(httpbin 所在 namespace)。所有调用方 Pod 都有 sidecar,出站自动 mTLS,测试的核心是:跨版本 sidecar 在 STRICT/PERMISSIVE 被调方面前,mTLS 握手能否成功、数据能否正常传输。