基于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可以灵活解析头部证书并完成验证:
- 确保Nginx已编译
lua-nginx-module并安装luasec库(用于证书验证) - 修改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
相关产品推荐
相关产品推荐

