mTLS(双向 TLS)详解

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 自动:

  1. 出站:用 istiod 签发的证书加密 + 附上客户端证书
  2. 入站:验证对方证书 + 解密 → 转发明文给应用

证书管理(自动化)

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
# STRICT:只接受 mTLS,拒绝明文
apiVersion: security.istio.io/v1
kind: PeerAuthentication
metadata:
name: strict-mtls
namespace: my-namespace
spec:
mtls:
mode: STRICT # ← 入站必须是 mTLS

# PERMISSIVE:同时接受 mTLS 和明文(默认行为)
# 不创建 PeerAuthentication 即为 PERMISSIVE

三种模式:

模式 入站明文 入站 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 握手能否成功、数据能否正常传输。