如何在Nginx TLS代理中复用客户端证书?
如何在Nginx TLS代理中复用客户端证书?
这个问题确实戳中了TCP mTLS负载均衡的痛点——HTTP里靠自定义头传证书信息的路子在纯TCP场景完全走不通,不过还是有几种可行的方案,我来给你拆解下:
方案一:用Proxy Protocol v2传递证书元数据(推荐)
Nginx从1.19.0版本开始支持Proxy Protocol v2的TLV(类型-长度-值)扩展,可以把客户端证书的关键信息(比如DN、完整证书内容)打包进Proxy Protocol头部,转发给上游节点。这种方式不需要修改TCP流量本身,只要你的上游应用能解析Proxy Protocol v2的TLV数据就行。
配置示例:
stream { # 定义上游集群 upstream tcp_backend { server 192.168.1.10:8000; server 192.168.1.11:8000; } server { listen 443 ssl; # 服务端证书配置 ssl_certificate /etc/nginx/certs/server.crt; ssl_certificate_key /etc/nginx/certs/server.key; # CA证书,用于验证客户端证书 ssl_client_certificate /etc/nginx/certs/ca.crt; ssl_verify_client on; # 强制校验客户端证书 proxy_pass tcp_backend; proxy_protocol on; # 启用Proxy Protocol # 将客户端证书DN和完整证书内容打包进TLV proxy_protocol_tlv ssl_client_s_dn $ssl_client_s_dn; proxy_protocol_tlv ssl_client_cert $ssl_client_cert; } }
上游应用需要解析Proxy Protocol v2头部,从中提取ssl_client_s_dn(客户端证书的主体DN)或ssl_client_cert(PEM格式的完整客户端证书)字段,以此获取客户端身份。
方案二:用OpenResty Lua脚本前置发送证书信息
如果上游不支持Proxy Protocol,你可以借助OpenResty的Lua扩展能力,在转发真实业务流量前,先把客户端证书的信息发送给上游节点。这种方式需要你的应用做少量修改,先读取前置的身份信息,再处理后续业务数据。
配置示例:
stream { lua_package_path "/usr/local/openresty/lualib/?.lua;;"; upstream tcp_backend { server 127.0.0.1:9000; } server { listen 443 ssl; ssl_certificate /etc/nginx/certs/server.crt; ssl_certificate_key /etc/nginx/certs/server.key; ssl_client_certificate /etc/nginx/certs/ca.crt; ssl_verify_client on; content_by_lua_block { -- 获取TCP连接套接字 local sock, err = ngx.req.socket() if not sock then ngx.log(ngx.ERR, "获取套接字失败: ", err) return end -- 获取客户端证书的主体DN(也可以用$ssl_client_cert获取完整证书) local client_identity = ngx.var.ssl_client_s_dn -- 发送身份信息,这里用自定义格式(比如换行分隔),需要和上游约定 local send_ok, err = sock:send("CLIENT_ID: " .. client_identity .. "\r\n\r\n") if not send_ok then ngx.log(ngx.ERR, "发送客户端身份失败: ", err) return end -- 开始转发真实业务流量 while true do local data, err = sock:receive(1024) if not data then break end local forward_ok, err = sock:send(data) if not forward_ok then break end end } proxy_pass tcp_backend; } }
这种方案的核心是在业务流量前插入一段身份信息,上游应用需要先读取这段信息并解析,再处理后续的业务数据。
方案三:双重TLS隧道(不推荐)
你也可以让Nginx在终止客户端mTLS后,以客户端身份和上游建立新的mTLS连接,同时通过TLS扩展字段传递原客户端的证书信息。但这种方案实现成本很高,需要上游应用支持解析自定义TLS扩展,一般只在特殊场景下考虑。
备注:内容来源于stack exchange,提问作者Florian Braun
相关产品推荐
相关产品推荐

