如何将客户端证书从Acme1 Nginx Ingress转发至Acme2实现故障转移?
问题解决:跨Ingress故障转移时原始客户端mTLS证书转发
问题背景
Acme1和Acme2区域的Nginx Ingress均启用mTLS,当Acme1触发故障转移到Acme2时,Acme2收到的是Acme1的代理证书,而非原始客户端的证书,导致无法识别原始客户端身份。
原因分析
- Acme1配置了
proxy_ssl_certificate和proxy_ssl_certificate_key,与Acme2建立mTLS连接时,Acme1会以自身证书作为客户端证书完成TLS握手,因此Acme2在TLS层面只能获取到Acme1的代理证书。 - Acme2设置了
auth-tls-verify-client: "on",强制验证TLS握手阶段的客户端证书,只会处理TLS层面的证书,忽略HTTP头中传递的原始客户端证书。
解决方案
1. 调整Acme1的Ingress配置
保留Acme1与Acme2的mTLS链路安全,同时确保原始客户端证书被正确传递:
- 保留现有代理证书配置,确保Acme1与Acme2的mTLS连接合法:
proxy_ssl_session_reuse on; proxy_ssl_certificate certs/ca.crt; proxy_ssl_certificate_key certs/key.crt; proxy_ssl_trusted_certificate certs/rootca.pem; - 明确传递原始客户端证书的HTTP头(使用更清晰的命名避免冲突):
proxy_set_header X-Original-Client-Cert $ssl_client_escaped_cert;注:
$ssl_client_escaped_cert是Nginx提供的URL编码后的客户端证书变量,确保证书内容在HTTP头中能安全传输。
2. 调整Acme2的Ingress配置
修改mTLS验证逻辑,同时验证Acme1的代理证书(确保转发来源合法)和HTTP头中的原始客户端证书:
- 修改
auth-tls-verify-client为optional,允许TLS握手时客户端(Acme1)提供证书,但不强制,同时保留对Acme1证书的验证:nginx.ingress.kubernetes.io/auth-tls-verify-client: "optional" - 保留原有验证Acme1证书的配置:
nginx.ingress.kubernetes.io/auth-tls-secret: root-ca nginx.ingress.kubernetes.io/auth-tls-verify-depth: "2" - 添加自定义配置,解码并传递原始客户端证书到上游服务:
通过Ingress注解添加Nginx配置片段,解码URL编码的证书并传递给上游:nginx.ingress.kubernetes.io/configuration-snippet: | rewrite_by_lua_block { if ngx.var.http_x_original_client_cert then local decoded_cert = ngx.unescape_uri(ngx.var.http_x_original_client_cert) ngx.var.decoded_client_cert = decoded_cert end } proxy_set_header X-Decoded-Client-Cert $decoded_client_cert; - (可选)如果需要在Acme2层面验证原始客户端证书,可在配置片段中添加证书验证逻辑:
nginx.ingress.kubernetes.io/configuration-snippet: | rewrite_by_lua_block { if ngx.var.http_x_original_client_cert then local decoded_cert = ngx.unescape_uri(ngx.var.http_x_original_client_cert) ngx.var.decoded_client_cert = decoded_cert -- 验证证书是否由信任CA签发 local openssl = require "resty.openssl" local cert, err = openssl.x509.new(decoded_cert, "PEM") if not cert then ngx.log(ngx.ERR, "Invalid client certificate: ", err) return ngx.exit(403) end local store, err = openssl.x509.store.new() store:add_file("/etc/nginx/ssl/rootca.pem") local ok, err = store:verify(cert) if not ok then ngx.log(ngx.ERR, "Certificate verification failed: ", err) return ngx.exit(403) end else ngx.exit(403) end } proxy_set_header X-Decoded-Client-Cert $decoded_client_cert;注:需确保Acme2的Ingress容器中安装了
lua-resty-openssl库,且信任的CA证书已挂载到指定路径。
3. 上游服务适配
上游服务需要读取X-Decoded-Client-Cert头中的PEM格式证书,完成身份验证逻辑。
关键注意事项
- 确保Acme1的
ssl_verify_client设置为optional或on,这样才能获取到$ssl_client_escaped_cert变量的值。 - Acme2与Acme1之间的mTLS连接仍然有效,确保只有合法的Acme1节点能转发流量到Acme2,避免未授权转发。
- 证书传递过程中使用URL编码和解码,避免证书内容中的特殊字符破坏HTTP头结构。
内容的提问来源于stack exchange,提问作者Hare Krshn
相关产品推荐
相关产品推荐

