You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.22 16:25:28