在传统的 TLS 握手中,只有客户端验证服务器的身份——服务器发送证书,客户端验证证书的有效性。但在微服务架构中,服务 A 调用服务 B 时,服务 B 也需要知道”调用方是谁”。这就是 mTLS(Mutual TLS,双向 TLS)的核心价值:双方互相验证身份。
mTLS 不是新技术。它在 TLS 协议中从一开始就被定义(RFC 2246 的 CertificateRequest 消息)。但它在微服务和零信任架构中的大规模应用,是最近几年才开始的工程实践。
一、mTLS 与单向 TLS 的区别
1.1 握手流程对比
单向 TLS(标准 HTTPS):
客户端 服务器
├── ClientHello ──────────────────→│
│←── ServerHello ─────────────────┤
│←── Certificate ─────────────────┤ ← 服务器发送证书
│←── ServerKeyExchange ───────────┤
│←── ServerHelloDone ─────────────┤
│ │
│ [客户端验证服务器证书] │
│ │
├── ClientKeyExchange ────────────→│
├── ChangeCipherSpec ─────────────→│
├── Finished ─────────────────────→│
│←── ChangeCipherSpec ────────────┤
│←── Finished ────────────────────┤
客户端知道服务器的身份 ✓
服务器不知道客户端的身份 ✗
双向 TLS(mTLS):
客户端 服务器
├── ClientHello ──────────────────→│
│←── ServerHello ─────────────────┤
│←── Certificate ─────────────────┤ ← 服务器证书
│←── CertificateRequest ──────────┤ ← 新增: 要求客户端证书
│←── ServerKeyExchange ───────────┤
│←── ServerHelloDone ─────────────┤
│ │
│ [客户端验证服务器证书] │
│ │
├── Certificate ──────────────────→│ ← 新增: 客户端发送证书
├── ClientKeyExchange ────────────→│
├── CertificateVerify ────────────→│ ← 新增: 客户端证明持有私钥
├── ChangeCipherSpec ─────────────→│
├── Finished ─────────────────────→│
│ │
│ [服务器验证客户端证书] │
│ │
│←── ChangeCipherSpec ────────────┤
│←── Finished ────────────────────┤
客户端知道服务器的身份 ✓
服务器知道客户端的身份 ✓
1.2 CertificateRequest 消息
CertificateRequest 消息内容:
服务器在此消息中告诉客户端:
1. 接受的证书类型:
- rsa_sign (RSA 签名)
- ecdsa_sign (ECDSA 签名)
2. 接受的签名算法:
- sha256WithRSAEncryption
- ecdsa-with-SHA256
- ...
3. 接受的 CA 列表(certificate_authorities):
- 客户端证书必须由这些 CA 签发
- 列表为空 = 接受任何 CA
TLS 1.3 中的变化:
CertificateRequest 被加密传输
增加了 extensions 字段(如 oid_filters)
1.3 CertificateVerify 消息
CertificateVerify 的作用:
证明客户端确实持有证书对应的私钥
而不是从别处复制来的证书
工作原理:
1. 客户端对之前所有握手消息计算哈希
2. 用自己的私钥对哈希值签名
3. 发送签名给服务器
4. 服务器用客户端证书中的公钥验证签名
如果签名验证成功:
→ 客户端确实持有私钥 → 身份可信
如果签名验证失败:
→ 证书是伪造的 → 握手中止
二、mTLS 的工程实现
2.1 使用 OpenSSL 手动配置 mTLS
# 前置: 已有私有 CA(参考上一篇)
# 1. 签发服务器证书
openssl req -new -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 \
-nodes -keyout server.key -out server.csr \
-subj "/CN=payment-service"
openssl x509 -req -days 365 \
-in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
-out server.crt \
-extfile <(echo -e "subjectAltName=DNS:payment-service,DNS:payment-service.default.svc.cluster.local\nextendedKeyUsage=serverAuth\nbasicConstraints=CA:FALSE")
# 2. 签发客户端证书
openssl req -new -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 \
-nodes -keyout client.key -out client.csr \
-subj "/CN=order-service"
openssl x509 -req -days 365 \
-in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
-out client.crt \
-extfile <(echo -e "subjectAltName=URI:spiffe://cluster.local/ns/default/sa/order-service\nextendedKeyUsage=clientAuth\nbasicConstraints=CA:FALSE")
# 3. 测试 mTLS 连接
# 启动服务器(要求客户端证书)
openssl s_server -accept 8443 \
-cert server.crt -key server.key \
-CAfile ca.crt -Verify 1
# 客户端连接(提供客户端证书)
openssl s_client -connect localhost:8443 \
-cert client.crt -key client.key \
-CAfile ca.crt
# 不提供客户端证书 → 握手失败
openssl s_client -connect localhost:8443 \
-CAfile ca.crt
# SSL routines:ssl3_read_bytes:tlsv13 alert certificate required2.2 Nginx mTLS 配置
server {
listen 8443 ssl;
server_name payment-service;
# 服务器证书
ssl_certificate /etc/nginx/ssl/server.crt;
ssl_certificate_key /etc/nginx/ssl/server.key;
# mTLS 配置
ssl_client_certificate /etc/nginx/ssl/ca.crt; # 信任的 CA
ssl_verify_client on; # 强制验证客户端证书
# ssl_verify_client optional; # 可选验证(部分接口需要)
ssl_verify_depth 2; # 证书链验证深度
# 将客户端证书信息传递给后端
location / {
proxy_pass http://backend;
proxy_set_header X-Client-CN $ssl_client_s_dn_cn;
proxy_set_header X-Client-Cert $ssl_client_escaped_cert;
proxy_set_header X-Client-Verify $ssl_client_verify;
}
# 部分接口不需要客户端证书
location /health {
ssl_verify_client off;
return 200 "ok";
}
}
2.3 Go 语言 mTLS 实现
package main
import (
"crypto/tls"
"crypto/x509"
"fmt"
"io"
"log"
"net/http"
"os"
)
// mTLS 服务器
func startServer() {
// 加载 CA 证书(用于验证客户端证书)
caCert, _ := os.ReadFile("ca.crt")
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)
tlsConfig := &tls.Config{
ClientCAs: caCertPool,
ClientAuth: tls.RequireAndVerifyClientCert,
MinVersion: tls.VersionTLS13,
}
server := &http.Server{
Addr: ":8443",
TLSConfig: tlsConfig,
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 从已验证的客户端证书中提取身份
if len(r.TLS.PeerCertificates) > 0 {
clientCN := r.TLS.PeerCertificates[0].Subject.CommonName
fmt.Fprintf(w, "Hello, %s!\n", clientCN)
}
}),
}
log.Fatal(server.ListenAndServeTLS("server.crt", "server.key"))
}
// mTLS 客户端
func startClient() {
// 加载客户端证书和私钥
clientCert, _ := tls.LoadX509KeyPair("client.crt", "client.key")
// 加载 CA 证书(用于验证服务器证书)
caCert, _ := os.ReadFile("ca.crt")
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)
tlsConfig := &tls.Config{
Certificates: []tls.Certificate{clientCert},
RootCAs: caCertPool,
MinVersion: tls.VersionTLS13,
}
client := &http.Client{
Transport: &http.Transport{
TLSClientConfig: tlsConfig,
},
}
resp, err := client.Get("https://payment-service:8443/")
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
}三、证书分发的工程挑战
mTLS 最大的工程挑战不是协议本身,而是证书的分发和轮换。
3.1 三种证书分发模式
模式一: 静态证书(手动分发)
┌──────┐ 手动复制证书 ┌──────┐
│ 运维 ├──────────────────→│ 服务 │
└──────┘ └──────┘
优点: 简单,无额外基础设施
缺点: 不可扩展,轮换困难
适用: 少量服务,内部系统
模式二: 集中式 CA(Secret Store 分发)
┌──────┐ ┌──────────┐ ┌──────┐
│ CA ├───→│ Vault / ├───→│ 服务 │
└──────┘ │ K8s Secret│ └──────┘
└──────────┘
优点: 集中管理,可审计
缺点: Secret Store 成为单点
适用: 中等规模,已有 Vault
模式三: Sidecar 代理(Service Mesh)
┌──────┐ ┌─────────┐ ┌──────────────┐
│ CA ├───→│ Control ├───→│ Sidecar │
│(SPIRE│ │ Plane │ │ (Envoy) │
│ /Istio) └─────────┘ │ ↕ mTLS │
└──────┘ │ App Container│
└──────────────┘
优点: 对应用透明,自动轮换
缺点: 基础设施复杂,sidecar 开销
适用: 大规模微服务
3.2 证书轮换的挑战
为什么证书轮换在 mTLS 中更复杂:
单向 TLS:
只需要轮换服务器证书
→ 更新证书文件 → reload 服务器
→ 客户端不需要任何变更
mTLS:
需要同时轮换服务器证书和客户端证书
→ 如果先轮换服务器的 CA → 旧客户端证书不被信任 → 中断
→ 如果先轮换客户端的 CA → 旧服务器 CA 不信任新证书 → 中断
安全的轮换流程(双 CA 信任期):
1. 服务器同时信任旧 CA 和新 CA
ssl_client_certificate: [old-ca.crt, new-ca.crt]
2. 逐步为客户端签发新 CA 的证书
3. 所有客户端都使用新证书后
移除对旧 CA 的信任
4. 完成轮换
四、SPIFFE 与 SPIRE
4.1 SPIFFE 标准
SPIFFE(Secure Production Identity Framework for Everyone)是 CNCF 毕业项目,定义了一套标准化的服务身份框架。
SPIFFE 的核心概念:
1. SPIFFE ID — 服务的唯一身份标识
格式: spiffe://<trust-domain>/<workload-path>
例如: spiffe://cluster.local/ns/production/sa/payment-service
trust-domain: 信任域(通常是集群或组织)
workload-path: 工作负载的路径
2. SVID (SPIFFE Verifiable Identity Document) — 身份文档
- X.509-SVID: 将 SPIFFE ID 放入证书的 SAN URI 字段
- JWT-SVID: 将 SPIFFE ID 放入 JWT 的 sub 字段
3. Trust Bundle — 信任包
一组根 CA 证书,用于验证 SVID
每个 trust-domain 有自己的 Trust Bundle
4. Workload API — 工作负载 API
Unix Domain Socket 接口
工作负载通过此 API 获取自己的 SVID
不需要在应用中硬编码证书路径
4.2 SPIRE 架构
SPIRE (SPIFFE Runtime Environment) 架构:
┌───────────────────────────────────────────┐
│ SPIRE Server │
│ ┌────────────┐ ┌──────────┐ ┌────────┐│
│ │ Registration│ │ CA │ │ Data ││
│ │ API │ │ (签发 │ │ Store ││
│ │ │ │ SVID) │ │ ││
│ └────────────┘ └──────────┘ └────────┘│
└─────────────┬─────────────────────────────┘
│ Node Attestation
│ + SVID 签发
┌─────────────┼─────────────────────────────┐
│ Node │ │
│ ┌──────────┴──────────┐ │
│ │ SPIRE Agent │ │
│ │ ┌────────────────┐ │ │
│ │ │ Workload API │ │ │
│ │ │ (Unix Socket) │ │ │
│ │ └───────┬────────┘ │ │
│ └──────────┼──────────┘ │
│ │ │
│ ┌──────────┴──┐ ┌───────────────┐ │
│ │ Workload A │ │ Workload B │ │
│ │ (获取 SVID) │ │ (获取 SVID) │ │
│ └─────────────┘ └───────────────┘ │
└────────────────────────────────────────────┘
# SPIRE 部署示例
# 1. 安装 SPIRE Server
kubectl apply -f https://raw.githubusercontent.com/spiffe/spire/main/support/k8s/spire-server.yaml
# 2. 安装 SPIRE Agent(DaemonSet)
kubectl apply -f https://raw.githubusercontent.com/spiffe/spire/main/support/k8s/spire-agent.yaml
# 3. 注册工作负载
kubectl exec -n spire spire-server-0 -- \
/opt/spire/bin/spire-server entry create \
-spiffeID spiffe://cluster.local/ns/default/sa/payment-service \
-parentID spiffe://cluster.local/ns/spire/sa/spire-agent \
-selector k8s:ns:default \
-selector k8s:sa:payment-service
# 4. 查看注册的工作负载
kubectl exec -n spire spire-server-0 -- \
/opt/spire/bin/spire-server entry show
# 5. 工作负载通过 Workload API 获取 SVID
# 应用代码中使用 go-spiffe 库:
# import "github.com/spiffe/go-spiffe/v2/workloadapi"
# source, _ := workloadapi.NewX509Source(ctx)
# svid, _ := source.GetX509SVID()
# fmt.Println(svid.ID) // spiffe://cluster.local/ns/default/sa/payment-service4.3 SPIFFE 身份 vs 传统证书身份
| 特性 | 传统 X.509 CN | SPIFFE ID |
|---|---|---|
| 身份格式 | CN=payment-service | spiffe://cluster.local/ns/default/sa/payment-service |
| 标准化 | 无统一标准 | CNCF 标准(RFC 待定) |
| 多租户 | 依赖 CN 命名约定 | trust-domain 天然隔离 |
| 跨集群 | 手动配置信任 | Federation API 自动 |
| 证书有效期 | 通常 1 年 | 默认 1 小时(短期证书) |
| 轮换方式 | 手动/Cron | SPIRE Agent 自动轮换 |
五、Service Mesh 中的 mTLS
5.1 Istio 的 mTLS 实现
# Istio mTLS 配置
# 全局启用 mTLS(STRICT 模式)
apiVersion: security.istio.io/v1
kind: PeerAuthentication
metadata:
name: default
namespace: istio-system
spec:
mtls:
mode: STRICT # STRICT: 只接受 mTLS
# PERMISSIVE: 同时接受 mTLS 和明文(迁移期)
# DISABLE: 禁用 mTLS# 特定命名空间的策略
apiVersion: security.istio.io/v1
kind: PeerAuthentication
metadata:
name: default
namespace: production
spec:
mtls:
mode: STRICT
# 排除特定端口(如 Prometheus 指标端口)
---
apiVersion: security.istio.io/v1
kind: PeerAuthentication
metadata:
name: payment-service
namespace: production
spec:
selector:
matchLabels:
app: payment-service
portLevelMtls:
9090:
mode: PERMISSIVE # 指标端口允许明文Istio mTLS 内部实现:
┌────────────────────────────────────────────┐
│ Pod A │
│ ┌─────────┐ localhost ┌─────────────┐│
│ │ App ├──────────────→│ Envoy ││
│ │Container│ (明文 HTTP) │ Sidecar ││
│ └─────────┘ │ ┌────────┐ ││
│ │ │ mTLS │ ││
│ │ │ 加密 │ ││
│ │ └────┬───┘ ││
│ └──────┼──────┘│
└───────────────────────────────────┼───────┘
│ mTLS (加密)
┌───────────────────────────────────┼───────┐
│ Pod B │ │
│ ┌─────────────┐ │ │
│ │ Envoy │ ┌────────┐ │ │
│ │ Sidecar ├───│ mTLS │─────┘ │
│ │ │ │ 解密 │ │
│ └──────┬──────┘ └────────┘ │
│ │ │
│ ┌──────┴──────┐ │
│ │ App │ │
│ │ Container │ (收到明文 HTTP) │
│ └─────────────┘ │
└────────────────────────────────────────────┘
对应用完全透明:
App A 发送明文 HTTP → Envoy A 加密 → 网络 → Envoy B 解密 → App B
应用代码不需要任何 TLS 配置
# 检查 Istio mTLS 状态
istioctl x describe pod payment-service-xxx -n production
# mTLS status: STRICT
# 查看 Envoy 的证书信息
istioctl proxy-config secret payment-service-xxx -n production
# RESOURCE NAME TYPE STATUS VALID CERT SERIAL NUMBER NOT AFTER
# default Cert Chain ACTIVE true xxx 2025-08-06T00:00:00Z
# ROOTCA CA ACTIVE true xxx 2035-01-01T00:00:00Z
# 检查 mTLS 连接
kubectl exec -it debug-pod -- \
curl -v http://payment-service.production:8080/
# 如果 STRICT 模式 + 没有 Sidecar → 连接失败
# 如果 PERMISSIVE 模式 → 明文也能通5.2 Linkerd 的 mTLS 实现
Linkerd vs Istio mTLS 的差异:
Linkerd:
- mTLS 默认启用(安装即生效)
- 使用内置 CA(identity controller)
- 证书有效期: 默认 24 小时
- 自动轮换,无需配置
- 不使用 SPIRE(有自己的身份系统)
Istio:
- mTLS 默认 PERMISSIVE(需要手动切 STRICT)
- 使用 istiod 内的 CA(或外部 CA)
- 证书有效期: 默认 24 小时
- 支持 SPIRE 集成
- 更灵活的策略控制
# Linkerd mTLS 验证
linkerd check --proxy
# 查看证书信息
linkerd identity -n production
# 查看 mTLS 连接状态
linkerd viz edges deployment -n production
# SRC DST SRC_P DST_P SECURED
# order payment 100% 100% true
# → SECURED=true 表示使用了 mTLS六、mTLS 的性能开销
6.1 性能影响分析
mTLS 的额外开销(相比单向 TLS):
1. 握手阶段:
- 额外传输: 客户端证书(~1 KB)+ CertificateVerify 签名
- 额外计算: 服务器验证客户端证书签名
- 影响: 握手延迟增加约 5-15%
2. 数据传输阶段:
- 无额外开销(加密方式完全相同)
3. 证书轮换:
- 短期证书(1-24 小时)需要频繁轮换
- SPIRE Agent 或 Sidecar 处理,应用无感知
# 测量 mTLS 握手延迟开销
# 单向 TLS
for i in $(seq 1 100); do
start=$(date +%s%N)
echo | openssl s_client -connect server:8443 \
-CAfile ca.crt 2>/dev/null >/dev/null
end=$(date +%s%N)
echo $(( (end - start) / 1000000 ))
done | awk '{sum+=$1; n++} END{print "单向 TLS 平均:", sum/n, "ms"}'
# mTLS
for i in $(seq 1 100); do
start=$(date +%s%N)
echo | openssl s_client -connect server:8443 \
-cert client.crt -key client.key \
-CAfile ca.crt 2>/dev/null >/dev/null
end=$(date +%s%N)
echo $(( (end - start) / 1000000 ))
done | awk '{sum+=$1; n++} END{print "mTLS 平均:", sum/n, "ms"}'
# 典型结果 (局域网, ECDSA P-256):
# 单向 TLS 平均: 2.1 ms
# mTLS 平均: 2.4 ms
# 额外开销: ~0.3 ms (~14%)6.2 Service Mesh Sidecar 开销
| 指标 | 无 Sidecar | Istio Envoy | Linkerd proxy |
|---|---|---|---|
| P50 延迟 | 基准 | +2-3 ms | +1-2 ms |
| P99 延迟 | 基准 | +5-10 ms | +3-5 ms |
| CPU | 基准 | +50-100m/pod | +20-50m/pod |
| 内存 | 基准 | +40-60 MB/pod | +20-30 MB/pod |
这些数值在不同负载下差异较大。建议在实际业务场景中做基准测试,而不是依赖通用数据。高 QPS 场景下,Sidecar 的 CPU 开销可能比低 QPS 场景高出 2-3 倍。
七、mTLS 故障排查
7.1 常见错误
# 错误一: 客户端证书不被信任
# 症状: SSL alert number 48 (unknown_ca)
openssl s_client -connect server:8443 \
-cert client.crt -key client.key \
-CAfile ca.crt 2>&1 | grep "alert"
# → 检查服务器的 ssl_client_certificate 是否包含签发客户端证书的 CA
# 错误二: 证书用途不匹配
# 症状: certificate verify failed
# → 检查客户端证书的 Extended Key Usage
openssl x509 -in client.crt -noout -text | grep -A 1 "Extended Key"
# 客户端证书需要: clientAuth
# 服务器证书需要: serverAuth
# 错误三: SAN 不匹配
# 症状: hostname mismatch
# → 检查服务器证书的 SAN 是否包含连接使用的域名
openssl x509 -in server.crt -noout -ext subjectAltName
# 错误四: 证书过期
openssl x509 -in client.crt -noout -dates
# 短期证书(如 SPIRE 的 1 小时有效期)过期很常见
# → 检查证书轮换机制是否正常工作
# 错误五: 私钥不匹配
# 检查证书和私钥是否匹配
openssl x509 -in client.crt -noout -modulus | openssl md5
openssl rsa -in client.key -noout -modulus | openssl md5
# 两个 MD5 值必须相同7.2 Wireshark 分析 mTLS 握手
# 抓取 mTLS 握手包
tcpdump -i eth0 -w mtls.pcap \
'tcp port 8443' -c 100
# 在 Wireshark 中分析:
# 过滤: tls.handshake.type == 13
# → 显示 CertificateRequest 消息
# 字段: Certificate Authorities → 查看服务器信任的 CA
# 过滤: tls.handshake.type == 11 && ip.src == <client_ip>
# → 显示客户端发送的 Certificate 消息
# 如果 Certificate Length = 0 → 客户端没有合适的证书
# 过滤: tls.handshake.type == 15
# → 显示 CertificateVerify 消息
# 如果没有这个消息 → 客户端没有发送证书
# 用 tshark 快速检查
tshark -r mtls.pcap -Y 'tls.handshake' \
-T fields \
-e frame.number \
-e ip.src \
-e tls.handshake.type \
-e tls.handshake.certificates_length
# 常用的 Wireshark 显示过滤器:
# tls.handshake.type == 13 → CertificateRequest
# tls.handshake.type == 11 → Certificate (客户端或服务器)
# tls.handshake.type == 15 → CertificateVerify
# tls.alert_message.desc == 42 → bad_certificate
# tls.alert_message.desc == 48 → unknown_ca
# tls.handshake.certificates_length == 0 → 空证书(客户端没有合适的证书)八、mTLS 部署策略
8.1 mTLS 与授权的关系
mTLS 解决的是认证(Authentication)——确认”你是谁”。但认证之后还需要授权(Authorization)——决定”你能做什么”。
认证 vs 授权:
mTLS (认证):
"这个请求来自 payment-service"
→ 确认身份
授权策略:
"payment-service 可以访问 /api/charge 但不能访问 /admin"
→ 控制权限
# Istio 的授权策略示例
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
name: payment-policy
namespace: production
spec:
selector:
matchLabels:
app: payment-service
rules:
- from:
- source:
principals: ["cluster.local/ns/production/sa/order-service"]
to:
- operation:
methods: ["POST"]
paths: ["/api/charge"]
- from:
- source:
principals: ["cluster.local/ns/production/sa/admin-service"]
to:
- operation:
methods: ["GET", "POST"]
paths: ["/api/*"]认证和授权必须一起部署才有意义——只有 mTLS 认证而不做授权,等于给每个服务发了身份证但不检查通行证。在 Service Mesh 中,mTLS 提供认证,AuthorizationPolicy 提供授权,二者缺一不可。
8.3 渐进式迁移
阶段一: PERMISSIVE 模式
所有服务同时接受 mTLS 和明文
→ 不影响现有流量
→ 有 Sidecar 的服务之间自动使用 mTLS
阶段二: 监控 mTLS 覆盖率
观察 mTLS 流量的比例
→ 识别未接入 Service Mesh 的服务
→ 确保所有通信都能用 mTLS
阶段三: STRICT 模式(逐步)
按命名空间逐步切换到 STRICT
→ 先从非关键服务开始
→ 监控是否有连接失败
阶段四: 全局 STRICT
所有命名空间都要求 mTLS
→ 拒绝明文通信
→ 零信任网络目标达成
8.4 何时使用 mTLS
| 场景 | 是否需要 mTLS | 原因 |
|---|---|---|
| 微服务内部通信 | ✓ 推荐 | 零信任原则 |
| 数据库连接 | ✓ 推荐 | 防止未授权访问 |
| 第三方 API 集成 | 看情况 | 对方是否支持 |
| 前端到后端 | 通常不需要 | 浏览器无法管理客户端证书 |
| 公网 API | 通常不需要 | 用 API Key/OAuth 代替 |
| IoT 设备通信 | ✓ 推荐 | 设备身份认证 |
mTLS 最适合的场景是”基础设施对基础设施”的通信——服务之间、数据库连接、消息队列。对于面向终端用户的场景(浏览器、移动 App),通常用 OAuth2/JWT 做应用层认证,因为用户设备上管理客户端证书的体验很差。
一个有用的判断标准:如果通信双方都是你控制的基础设施,用 mTLS;如果有一方是终端用户,用应用层认证。两者也可以结合使用——mTLS 保护传输层,JWT 承载业务层的用户身份和权限信息。
九、总结
mTLS 解决的核心问题是”你是谁”——在网络层面建立双方的身份互信:
mTLS 的工程复杂度在于证书管理,不在于协议。 协议层面 mTLS 只是多了 CertificateRequest 和客户端的 Certificate 两个消息。但管理成百上千个服务的证书签发、分发和轮换是真正的工程挑战。
短期证书 + 自动轮换是最佳实践。 SPIRE 默认 1 小时证书有效期,Istio 和 Linkerd 默认 24 小时。短期证书的优势是:即使证书泄露,影响窗口很短。而自动轮换消除了人工干预的风险。
Service Mesh 是大规模 mTLS 的最佳载体。 通过 Sidecar 代理处理 mTLS,应用代码完全不需要关心证书。但 Sidecar 引入了额外的延迟和资源开销——P50 约增加 1-3 ms,每个 Pod 额外消耗 20-60 MB 内存。
PERMISSIVE 模式是迁移的关键。 不要试图一步到位切换到 STRICT 模式。先 PERMISSIVE 让新旧流量共存,监控 mTLS 覆盖率,确认所有服务都已接入后再切 STRICT。
SPIFFE 是服务身份的未来标准。 它定义了统一的服务身份格式(SPIFFE ID)、验证文档(SVID)和 API(Workload API)。即使你现在不用 SPIRE,理解 SPIFFE 的身份模型对设计服务间认证方案也有指导意义。
下一篇:TLS 性能优化:会话恢复、OCSP Stapling 与硬件加速
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
【网络工程】CDN 与 HTTPS:边缘 TLS、证书管理与安全
CDN 的 HTTPS 部署涉及边缘 TLS 终止、证书托管、回源加密等多个工程环节。本文系统拆解 CDN HTTPS 的架构模式、证书管理方案、安全最佳实践与常见故障排查方法。
【网络工程】证书工程:PKI 体系、ACME 与自动化管理
证书过期宕机是最常见也最可避免的生产事故。本文从工程角度剖析 X.509 证书结构、PKI 信任链的工作原理、ACME 协议与 Let's Encrypt 的自动化部署、私有 CA 的搭建实践,以及 cert-manager 在 Kubernetes 中的证书管理方案。覆盖证书监控、轮换策略与过期告警的完整工程体系。
【网络工程】网络隔离与微分段:VLAN、SDN 策略与零信任
网络隔离是安全架构的基石。本文从传统 VLAN 的 4096 限制、VXLAN 的 Overlay 隔离机制、SDN 下的 Calico/Cilium Network Policy 工程实践、微分段的设计方法论,到零信任网络架构的分段策略,系统讲解从物理隔离到软件定义隔离的演进和工程落地。
【网络工程】反向代理模式:TLS 终止、透传与重加密
系统解剖反向代理的三种 TLS 处理模式——终止、透传与重加密。从架构对比到 SNI 路由、证书管理、性能影响与安全权衡,给出生产环境的工程选型依据。