双向TLS(mTLS)认证
约 1416 字大约 5 分钟
mtlssecurity
2025-08-22
概述
双向 TLS(mutual TLS, mTLS)在标准 TLS 的基础上增加了客户端证书验证。不仅服务端向客户端证明身份(标准 TLS),客户端也必须向服务端提供证书证明自身身份。mTLS 广泛应用于微服务间通信、API 网关认证和零信任网络架构。
TLS 与 mTLS 对比
证书体系
CA 搭建与证书生成
使用 OpenSSL 搭建私有 CA
# 1. 创建 Root CA
mkdir -p ca/{root,intermediate,server,client}
# 生成 Root CA 私钥
openssl genrsa -aes256 -out ca/root/ca.key 4096
# 输入密码保护私钥
# 生成 Root CA 自签名证书(有效期 10 年)
openssl req -x509 -new -nodes -key ca/root/ca.key \
-sha256 -days 3650 \
-out ca/root/ca.crt \
-subj "/C=CN/ST=Beijing/O=MyOrg/OU=Security/CN=MyOrg Root CA"
# 2. 创建 Intermediate CA(推荐:不直接使用 Root CA 签发终端证书)
openssl genrsa -out ca/intermediate/intermediate.key 4096
openssl req -new -key ca/intermediate/intermediate.key \
-out ca/intermediate/intermediate.csr \
-subj "/C=CN/ST=Beijing/O=MyOrg/OU=Security/CN=MyOrg Intermediate CA"
openssl x509 -req -in ca/intermediate/intermediate.csr \
-CA ca/root/ca.crt -CAkey ca/root/ca.key \
-CAcreateserial -sha256 -days 1825 \
-extfile <(echo "basicConstraints=CA:TRUE,pathlen:0
keyUsage=critical,digitalSignature,keyCertSign,cRLSign") \
-out ca/intermediate/intermediate.crt
# 创建证书链
cat ca/intermediate/intermediate.crt ca/root/ca.crt > ca/intermediate/chain.crt生成服务端证书
# 3. 服务端证书
openssl genrsa -out ca/server/server.key 2048
# 创建 SAN 配置(Subject Alternative Name)
cat > ca/server/server.cnf << EOF
[req]
distinguished_name = req_dn
req_extensions = v3_req
[req_dn]
[v3_req]
subjectAltName = @alt_names
[alt_names]
DNS.1 = api.example.com
DNS.2 = *.internal.example.com
IP.1 = 10.0.0.100
EOF
openssl req -new -key ca/server/server.key \
-out ca/server/server.csr \
-subj "/C=CN/ST=Beijing/O=MyOrg/CN=api.example.com" \
-config ca/server/server.cnf
openssl x509 -req -in ca/server/server.csr \
-CA ca/intermediate/intermediate.crt \
-CAkey ca/intermediate/intermediate.key \
-CAcreateserial -sha256 -days 365 \
-extensions v3_req -extfile ca/server/server.cnf \
-out ca/server/server.crt生成客户端证书
# 4. 客户端证书
openssl genrsa -out ca/client/client.key 2048
openssl req -new -key ca/client/client.key \
-out ca/client/client.csr \
-subj "/C=CN/ST=Beijing/O=MyOrg/OU=ServiceTeam/CN=payment-service"
openssl x509 -req -in ca/client/client.csr \
-CA ca/intermediate/intermediate.crt \
-CAkey ca/intermediate/intermediate.key \
-CAcreateserial -sha256 -days 365 \
-out ca/client/client.crt
# 验证证书链
openssl verify -CAfile ca/intermediate/chain.crt ca/client/client.crt
# ca/client/client.crt: OKNginx mTLS 配置
server {
listen 443 ssl;
server_name api.example.com;
# 服务端证书
ssl_certificate /etc/nginx/certs/server.crt;
ssl_certificate_key /etc/nginx/certs/server.key;
# CA 证书链(用于验证客户端证书)
ssl_client_certificate /etc/nginx/certs/chain.crt;
# 要求客户端证书
ssl_verify_client on;
# 可选值: on | off | optional | optional_no_ca
# optional: 有证书就验证,没有也允许(可在应用层处理)
# 验证深度(证书链层数)
ssl_verify_depth 2;
# TLS 协议版本
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
# CRL 检查(可选)
ssl_crl /etc/nginx/certs/crl.pem;
location /api/ {
# 将客户端证书信息传递给后端
proxy_set_header X-Client-CN $ssl_client_s_dn_cn;
proxy_set_header X-Client-DN $ssl_client_s_dn;
proxy_set_header X-Client-Verify $ssl_client_verify;
proxy_set_header X-Client-Serial $ssl_client_serial;
proxy_set_header X-Client-Fingerprint $ssl_client_fingerprint;
proxy_pass http://backend:8080;
}
}Envoy mTLS 配置
# Envoy 代理 mTLS 配置
static_resources:
listeners:
- name: mtls_listener
address:
socket_address:
address: 0.0.0.0
port_value: 8443
filter_chains:
- transport_socket:
name: envoy.transport_sockets.tls
typed_config:
"@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
require_client_certificate: true
common_tls_context:
tls_certificates:
- certificate_chain:
filename: /etc/envoy/certs/server.crt
private_key:
filename: /etc/envoy/certs/server.key
validation_context:
trusted_ca:
filename: /etc/envoy/certs/ca-chain.crt
# 只允许特定 CN 的客户端证书
match_typed_subject_alt_names:
- matcher:
exact: "payment-service"
san_type: DNS应用层 mTLS 实现
# Python 客户端发起 mTLS 请求
import requests
response = requests.get(
'https://api.example.com/data',
cert=('/path/to/client.crt', '/path/to/client.key'), # 客户端证书和私钥
verify='/path/to/ca-chain.crt', # CA 证书链(验证服务端)
)
# Python 服务端接收 mTLS(Flask + gunicorn)
# gunicorn --certfile server.crt --keyfile server.key \
# --ca-certs ca-chain.crt --cert-reqs 2 app:app// Go 服务端 mTLS
package main
import (
"crypto/tls"
"crypto/x509"
"net/http"
"os"
)
func main() {
caCert, _ := os.ReadFile("ca-chain.crt")
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)
tlsConfig := &tls.Config{
ClientCAs: caCertPool,
ClientAuth: tls.RequireAndVerifyClientCert,
MinVersion: tls.VersionTLS12,
}
server := &http.Server{
Addr: ":8443",
TLSConfig: tlsConfig,
Handler: http.HandlerFunc(handler),
}
server.ListenAndServeTLS("server.crt", "server.key")
}
func handler(w http.ResponseWriter, r *http.Request) {
// 获取客户端证书信息
if r.TLS != nil && len(r.TLS.PeerCertificates) > 0 {
clientCN := r.TLS.PeerCertificates[0].Subject.CommonName
// 基于 CN 做授权
}
}证书轮换
# cert-manager 自动管理证书
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: api-server-cert
namespace: production
spec:
secretName: api-server-tls
duration: 90d
renewBefore: 30d
subject:
organizations:
- MyOrg
commonName: api.example.com
dnsNames:
- api.example.com
- "*.internal.example.com"
issuerRef:
name: internal-ca
kind: ClusterIssuer
usages:
- server auth
- client auth认证方式对比
| 特性 | API Key | OAuth2 Token | mTLS |
|---|---|---|---|
| 认证强度 | 低(共享密钥) | 中(Bearer Token) | 高(PKI 证书) |
| 传输安全 | 依赖 TLS | 依赖 TLS | 内置双向 TLS |
| 密钥分发 | 手动/API | 授权流程 | 证书签发 |
| 轮换难度 | 中 | 低(自动续期) | 低(cert-manager) |
| 吊销机制 | 删除 Key | Token 黑名单 | CRL/OCSP |
| 适用场景 | 简单 API | Web/移动应用 | 服务间/零信任 |
| 性能开销 | 几乎无 | Token 验证 | TLS 握手(可缓存) |
最佳实践
- 使用中间 CA 签发终端证书,Root CA 私钥离线保存
- 证书有效期控制在 90 天以内,使用 cert-manager 自动续签
- 客户端证书中使用有意义的 CN/SAN,便于访问控制和审计
- 配置 CRL 或 OCSP 实现证书吊销检查
- TLS 最低版本设为 1.2,优先使用 TLS 1.3
- 在服务网格(Istio/Linkerd)中,mTLS 可自动管理,无需应用层配置
- 监控证书过期时间,设置告警避免证书过期导致服务中断
- 将客户端证书信息传递给后端应用,用于细粒度授权
贡献者
更新日志
2026/3/14 13:09
查看所有更新日志
9f6c2-feat: organize wiki content and refresh site setup于