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

基于ALB mTLS透传,配置Nginx使用X-Amzn-Mtls-Clientcert证书

解决ALB mTLS透传下Nginx验证客户端证书的问题

我们的架构是Application Load Balancer(ALB)后端挂载EC2实例,实例上运行Nginx。ALB已配置启用2023年11月新增的mTLS透传功能,经确认客户端证书会通过X-Amzn-Mtls-Clientcert请求头部传递到Nginx,但当前Nginx配置中启用的ssl_verify_client无法识别该头部中的证书,尝试手动设置ssl-client-cert头部也未生效。

当前Nginx服务器配置如下:

server {
    error_log /var/log/nginx/error.log debug;
    listen 443 ssl;
    server_name ~abc.com;

    ssl_certificate /etc/ssl/certs/nginx.crt;
    ssl_certificate_key /etc/ssl/private/nginx.key;

    ssl_verify_client on;
    ssl_client_certificate /etc/ssl/client_certs/publicapi.crt;
    ssl_verify_depth 1;

    location /mtls-statuscheck {
        access_log /var/log/nginx/access-mtls.log main;
        return 200 "$ssl_client_verify - $http_Content_Type $http_User_Agent $http_X_Amzn_Mtls_Clientcert";
        add_header Content-Type text/plain;
    }
}

我们考虑两个解决方向:

  • 配置Nginx直接读取X-Amzn-Mtls-Clientcert头部获取客户端证书并完成验证
  • 将X-Amzn-Mtls-Clientcert的值映射到Nginx可用于验证的逻辑中

解决方案

方向1:使用Lua模块手动解析验证证书

Nginx原生的ssl_verify_client仅针对直接SSL握手场景生效,无法读取请求头部的证书内容。借助lua-nginx-module可以灵活解析头部证书并完成验证:

  1. 确保Nginx已编译lua-nginx-module并安装luasec库(用于证书验证)
  2. 修改Nginx配置如下:
server {
    error_log /var/log/nginx/error.log debug;
    listen 443 ssl;
    server_name ~abc.com;

    ssl_certificate /etc/ssl/certs/nginx.crt;
    ssl_certificate_key /etc/ssl/private/nginx.key;

    # 关闭原生SSL验证,改用手动逻辑
    ssl_verify_client off;
    ssl_client_certificate /etc/ssl/client_certs/publicapi.crt;
    ssl_verify_depth 1;

    ssl_certificate_by_lua_block {
        # 获取ALB传递的客户端证书头部
        local client_cert_header = ngx.var.http_x_amzn_mtls_clientcert
        if not client_cert_header then
            return ngx.exit(403)
        end

        # 解码Base64格式的证书内容
        local cert_data = ngx.decode_base64(client_cert_header)
        if not cert_data then
            return ngx.exit(403)
        end

        # 加载信任的CA证书并验证客户端证书
        local ca_cert = io.open("/etc/ssl/client_certs/publicapi.crt", "r"):read("*a")
        local ok, err = require("ssl").verify(cert_data, ca_cert, { depth = 1 })
        if not ok then
            ngx.log(ngx.ERR, "证书验证失败: ", err)
            return ngx.exit(403)
        end
    }

    location /mtls-statuscheck {
        access_log /var/log/nginx/access-mtls.log main;
        return 200 "证书验证通过 - $http_X_Amzn_Mtls_Clientcert";
        add_header Content-Type text/plain;
    }
}

方向2:调用OpenSSL命令行验证证书

如果不想依赖Lua模块,可以通过access_by_lua_block或exec调用OpenSSL命令完成证书验证:

server {
    error_log /var/log/nginx/error.log debug;
    listen 443 ssl;
    server_name ~abc.com;

    ssl_certificate /etc/ssl/certs/nginx.crt;
    ssl_certificate_key /etc/ssl/private/nginx.key;

    ssl_verify_client off;
    ssl_client_certificate /etc/ssl/client_certs/publicapi.crt;
    ssl_verify_depth 1;

    # 存储证书头部到变量
    set $client_cert $http_x_amzn_mtls_clientcert;

    location /mtls-statuscheck {
        access_log /var/log/nginx/access-mtls.log main;
        
        # 空证书直接拒绝
        if ($client_cert = "") {
            return 403;
        }

        # 调用OpenSSL验证证书
        access_by_lua_block {
            local cert = ngx.var.client_cert
            local cmd = string.format("echo '%s' | base64 -d | openssl verify -CAfile /etc/ssl/client_certs/publicapi.crt -", cert)
            local handle = io.popen(cmd)
            local result = handle:read("*a")
            handle:close()
            
            if not string.find(result, "OK") then
                ngx.exit(403)
            end
        }

        return 200 "证书验证通过 - $http_X_Amzn_Mtls_Clientcert";
        add_header Content-Type text/plain;
    }
}

关键注意点

  • ALB传递的X-Amzn-Mtls-Clientcert内容是Base64编码的PEM格式证书,验证前必须解码
  • 原生ssl_verify_client无法在ALB透传场景下生效,因为Nginx与ALB之间无客户端证书握手流程,必须通过自定义逻辑完成验证

内容的提问来源于stack exchange,提问作者Steve

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 21:22:49