Kubernetes (RKE2) 下 Traefik 与 Nginx Ingress 双向 TLS (mTLS) 配置
本文介绍了在Kubernetes环境中实现双向TLS(mTLS)认证的两种方案。首先概述了mTLS的工作原理及其在零信任安全架构中的核心优势,包括绝对防绕过和物理身份隔离特性。然后详细说明了环境准备步骤,包括CA证书创建和Kubernetes Secret配置。针对RKE2默认Traefik Ingress,文章提供了TLSOption资源配置和Ingress关联的具体方法,特别强调了跨命名空间引
目录
- 零信任安全与双向 TLS (mTLS) 概述
- 环境准备:CA 证书与 K8s Secret 创建
- 方案实战 A:RKE2 默认 Traefik Ingress 配置
- 方案实战 B:Nginx Ingress Controller 配置
- 用户侧:客户端证书生成与 PKCS#12 (.p12) 打包
- 客户端授信导入与多终端测试
- 生产级故障排查与最佳实践 (Troubleshooting)
1. 零信任安全与双向 TLS (mTLS) 概述
1.1 什么是双向 TLS(mTLS)?
在传统的**单向 TLS(One-way TLS)**中,只有服务端(网站)需要出示证书来向客户端(浏览器)证明自己是真实合法的,客户端校验证书通过后建立加密通道。
而在**双向 TLS(mTLS / Mutual TLS)**中,除了上述步骤,服务端也会强制要求客户端出示身份证书。只有当双方证书都经过彼此信任的 CA 机构验证无误后,加密通道才能成功建立。
1.2 mTLS 的核心优势与应用场景
- 绝对防绕过:单向自签证书中,用户可以通过在浏览器点击“高级 -> 继续访问”强行绕过证书警告;但在 mTLS 握手阶段,如果客户端证书缺失或无效,Ingress 会在 TCP 握手层面直接强行中断连接,完全没有绕过机制,安全性达到 100%。
- 物理身份隔离:不依赖用户的用户名和密码。证书保存在设备的安全区域中,极大地防范了钓鱼攻击与凭证泄露。
- 主要应用场景:企业核心敏感资产(财务、内网管理后台)、微服务间通信(如 Istio 等服务网格)、物联网(IoT)智能设备安全通信接入、银行金融级的 B2B API 对接。
2. 环境准备:CA 证书与 K8s Secret 创建
无论使用 Traefik 还是 Nginx Ingress,服务器都必须加载自建 CA 的公钥证书**,用于对来访的客户端证书进行数字签名解密与合法性确认。
[!CAUTION]
安全防护:在任何 Kubernetes 部署场景下,我们都只需要上传 CA 的公钥证书(ca.crt)。
绝对不要将 CA 的私钥(ca.key)上传至集群中。私钥应始终离线妥善保存在安全的物理媒介或硬件加密钥匙箱(HSM)中。
步骤 2.1:在 Kubernetes 中创建 CA 证书的 Secret
在业务应用所在的 Namespace(如 my-namespace)中执行以下命令,将 CA 公钥证书转化为 Kubernetes Secret:
kubectl create secret generic mtls-ca-secret \
--from-file=tls.ca=ca.crt \
-n <your-namespace>
- 注意:命令中
--from-file=tls.ca=ca.crt明确将 Secret 内部的键(Key)定义为tls.ca。Traefik 及 Nginx 控制器会自动识别并提取该键下的内容作为受信任的根 CA 列表。
3. 方案 A:RKE2 默认 Traefik Ingress 配置
步骤 3.1:定义并部署 TLSOption 资源
创建一个名为 tls-option.yaml 的清单文件。请确保 TLSOption 的 namespace 与上一步创建的 CA Secret 保持一致:
apiVersion: traefik.io/v1alpha1
kind: TLSOption
metadata:
name: mtls-option
namespace: <your-namespace>
spec:
clientAuth:
secretNames:
- mtls-ca-secret # 引用步骤 2.1 创建的包含 CA 公钥的 Secret
clientAuthType: RequireAndVerifyClientCert # 强制要求并验证客户端证书
- 参数释义:
RequireAndVerifyClientCert:这是最安全的模式。要求连接请求中必须包含客户端证书,且该证书必须由secretNames列表中的 CA 签名,否则直接阻断连接并抛出HTTP 400 Bad Request。
部署资源:
kubectl apply -f tls-option.yaml
步骤 3.2:配置 Ingress 关联 TLSOption
对于标准的 Kubernetes Ingress 资源,需要通过特定注解挂载 Traefik 的 TLSOption。
[!WARNING]
引用命名空间的重要陷阱:
在 Kubernetes Ingress 注解中引用 Traefik CRD 资源时,其值必须遵循独特的跨命名空间命名规范,语法为:命名空间-资源名称@kubernetescrd。
假设命名空间为my-namespace,资源名称为mtls-option,则注解的配置值必须为my-namespace-mtls-option@kubernetescrd,否则 Traefik 将无法在路由树上加载对应的 TLS 设置!
Ingress YAML 清单文件:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-traefik-app-ingress
namespace: <your-namespace>
annotations:
kubernetes.io/ingress.class: traefik
# 核心关联注解:绑定 Traefik 的双向认证规则
traefik.ingress.kubernetes.io/router.tls.options: <your-namespace>-mtls-option@kubernetescrd
spec:
tls:
- hosts:
- your-website.com
secretName: your-server-cert # 网站自身的域名 HTTPS 证书 (由公网授信 CA 签发)
rules:
- host: your-website.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: your-service-name
port:
number: 80
4. 方案实战 B:Nginx Ingress Controller 配置
如果集群为了兼容性改用了社区版 Nginx Ingress Controller (ingress-nginx),其实现 mTLS 显得更加轻量化——无需定义复杂的自定义 CRD,直接在 Ingress 的元数据注解(Annotations)中做一步式声明即可。
步骤 4.1:创建 Nginx CA Secret
Nginx 要求 CA 证书在 Secret 中的 key 最好为 ca.crt。请执行以下命令:
kubectl create secret generic nginx-mtls-ca \
--from-file=ca.crt=ca.crt \
-n <your-namespace>
步骤 4.2:在 Ingress 资源中直接添加配置注解
使用以下三个核心注解控制 Nginx 的双向认证行为:
nginx.ingress.kubernetes.io/auth-tls-verify-client:设为"on"以在 TLS 握手层启用强制客户端认证。nginx.ingress.kubernetes.io/auth-tls-secret:指向信任的 CA 证书,格式必须是"命名空间/Secret名称"(例如my-namespace/nginx-mtls-ca)。nginx.ingress.kubernetes.io/auth-tls-pass-certificate-to-upstream:设为"true",Nginx 会将完整的 PEM 格式客户端证书通过 HTTP Header(X-ARR-ClientCert或ssl-client-cert)透传给后端的业务容器。
Ingress YAML 清单文件:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-nginx-app-ingress
namespace: <your-namespace>
annotations:
kubernetes.io/ingress.class: nginx
# 1. 启动客户端 TLS 双向校验
nginx.ingress.kubernetes.io/auth-tls-verify-client: "on"
# 2. 绑定 CA Secret
nginx.ingress.kubernetes.io/auth-tls-secret: "<your-namespace>/nginx-mtls-ca"
# 3. 透传证书信息,供后端业务程序实现应用层 RBAC 细粒度审计/认证
nginx.ingress.kubernetes.io/auth-tls-pass-certificate-to-upstream: "true"
spec:
tls:
- hosts:
- your-website.com
secretName: your-server-cert # 服务端 HTTPS 证书
rules:
- host: your-website.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: your-service-name
port:
number: 80
5. 用户侧:客户端证书生成与 PKCS#12 (.p12) 打包
服务器端配置就绪后,直接访问该网站应当会收到 HTTP 400 握手错误。现在,我们需要利用 CA 私钥在本地生成并签发一张合法的客户端证书。
步骤 5.1:生成客户端私钥与证书签名请求 (CSR)
在本地主机的终端中执行 OpenSSL 命令,生成非对称密码对以及证书属性申请文件:
# 1. 生成高强度客户端私钥 (client.key)
openssl genrsa -out client.key 2048
# 2. 生成证书签名请求文件 (client.csr)
# 这里的 Common Name (CN) 建议设定为可读性强的具体用户名,例如 client-user-01
openssl req -new -key client.key -subj "/CN=client-user-01" -out client.csr
步骤 5.2:使用 CA 签名生成客户端公钥证书 (client.crt)
扮演 CA 的角色,使用离线持有的 CA 私钥(ca.key)签署客户端请求,生成拥有过期时效的有效客户端证书:
openssl x509 -req \
-in client.csr \
-CA ca.crt \
-CAkey ca.key \
-CAcreateserial \
-out client.crt \
-days 365
- 执行完毕后,当前目录下会生成
client.crt文件。
步骤 5.3:将证书打包为 PKCS#12 (.p12) 二进制格式
大部分移动设备和操作系统(如 Windows/macOS/Chrome/Safari)为了防篡改和数据完整性,无法直接接收散开的私钥和证书。必须使用 PKCS#12 标准将其整合打包为一个包含证书链和私钥的单一文件。
openssl pkcs12 -export \
-out client.p12 \
-inkey client.key \
-in client.crt \
-certfile ca.crt
[!IMPORTANT]
警告:该命令在运行期间会强制要求设定一个 “导出密码(Export Password)”。
请妥善记录此密码,客户端用户在将.p12证书导入个人 PC 或手机操作系统时,必须输入此密码才可以解开打包文件进行安装。
6. 客户端授信导入与多终端测试
6.1 导入操作系统/浏览器
- macOS 系统:直接双击
client.p12文件,系统会唤起 “钥匙串访问(Keychain Access)”。将证书导入至 “登录 (login)” 钥匙串,并输入导出密码。 - Windows 系统:双击
client.p12文件,根据证书导入向导,将其部署至“当前用户”下的 “个人” 证书存储区。
6.2 浏览器访问测试
- 彻底关闭浏览器以清空历史 TLS Session 缓存(也可以直接打开“隐身/无痕”标签页)。
- 访问配置了 mTLS 的域名:
https://your-website.com。 - 此时,浏览器会自动探测到服务端的 Certificate Request,并在屏幕上弹窗询问您要提交哪张证书进行校验。
- 选中刚刚导入的
client-user-01证书,确认后即可绕过 400 阻断,顺利进入系统!
7. 生产级故障排查与最佳实践 (Troubleshooting)
在实际的生产环境中,mTLS 很容易由于各种配置细节或缓存导致握手失败。
7.1 使用 openssl 命令行深度调试
当浏览器访问报错且无法判断原因时,使用以下命令能够看到最底层的 TLS 握手细节,帮助诊断证书是否有效:
openssl s_client -connect your-website.com:443 \
-cert client.crt \
-key client.key \
-CAfile ca.crt \
-debug
- 如果连接成功:您将在最后看到终端打印出
HTTP的调试数据。 - 如果服务端配置有误:
s_client会具体抛出是哪一方的验证链(Verification error)断裂。
7.2 踩坑排查一览表
| 故障现象 | 可能原因 | 解决办法 |
|---|---|---|
访问直接返回 HTTP 400 Bad Request |
客户端没有提供证书,或者提供的证书已经过期、无效。 | 1. 确保 .p12 文件已正确安装。2. 重新关闭并彻底重启浏览器,清除已缓存的空 TLS 握手状态。 3. 检查客户端证书的过期时间是否满足 -days 限制。 |
| 导入证书后依然没有弹窗提示选择证书 | 证书的 Common Name (CN) 或者别名不符合规则,或浏览器没有正确读取系统钥匙串。 | 1. 用 openssl x509 -in client.crt -noout -text 检查 CN 是否正确。2. 部分浏览器(如 Firefox)使用自己独立的证书管理器,需在设置中手动导入。 |
Ingress 报错:Secret not found 或没有任何拦截效果 |
[最易踩坑] 命名空间或注解格式不正确。 | 1. Traefik:确认注解是否包含了命名空间前缀,即 <namespace>-<name>@kubernetescrd。2. Nginx:确认注解 auth-tls-secret 的值是否是 "namespace/name" 格式,且 Secret 内部的文件名 key 命名为 ca.crt。 |
服务器端证书报错 Certificate expired |
K8s 内部 CA 证书签名过期。 | 1. 确认 RKE2 服务端或您自建 CA 的寿命。 2. 如果更换了 CA,必须执行 kubectl apply 重新覆盖上传 CA Secret。 |
更多推荐



所有评论(0)