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

如何让Nginx验证X-Forwarded-Client-Cert中的客户端证书?

解决方案

一、无需Lua的替代方案:使用auth_request+Shell验证脚本

这是最适合不熟悉Lua场景的方案,依托Nginx的auth_request指令配合简单Shell脚本完成证书验证,无需额外复杂组件。

1. 编写证书验证脚本

创建verify_client_cert.sh脚本(放入容器内/usr/local/bin/路径),负责验证证书的CA签名与元数据:

#!/bin/bash
set -e

# 提取并清理X-Forwarded-Client-Cert头中的证书内容
RAW_CERT="$HTTP_X_FORWARDED_CLIENT_CERT"
CERT=$(echo "$RAW_CERT" | sed -E 's/^cert=//; s/%0A/\n/g')

# 验证证书是否由信任CA签发
if ! openssl verify -CAfile /etc/nginx/ca/rootCA.pem <(echo "$CERT"); then
    exit 403
fi

# 验证证书元数据(示例:检查CN字段前缀)
CERT_CN=$(openssl x509 -in <(echo "$CERT") -noout -subject | awk -F'CN=' '{print $2}' | awk '{print $1}')
if [[ ! "$CERT_CN" =~ ^allowed-cn- ]]; then
    exit 403
fi

# 验证通过返回200
exit 200

给脚本添加执行权限:chmod +x /usr/local/bin/verify_client_cert.sh

2. Nginx配置调整

通过auth_request将请求转发到内部验证端点,调用上述脚本:

server {
    listen 80; # Azure容器应用内部端口,外部TLS由Envoy终止

    # 信任Azure内部代理,获取真实客户端信息
    set_real_ip_from 10.0.0.0/8; # 根据Azure容器应用内部网段调整
    real_ip_header X-Forwarded-For;
    real_ip_recursive on;

    location / {
        # 先执行证书验证
        auth_request /verify-client-cert;
        # 验证通过后处理静态文件/反向代理
        root /usr/share/nginx/html;
        # 或反向代理后端服务:proxy_pass http://backend-service;
    }

    # 内部验证端点,仅Nginx内部调用
    location = /verify-client-cert {
        internal;
        # 执行验证脚本
        exec /usr/local/bin/verify_client_cert.sh;
        # 传递客户端证书头到脚本
        proxy_set_header X-Forwarded-Client-Cert $http_x_forwarded_client_cert;
    }
}

注意:需使用支持ngx_http_exec_module的Nginx镜像,官方镜像可在构建时添加该模块,或直接选用第三方预编译镜像。


二、Lua方案:利用成熟开源库

若追求更高性能或需要复杂验证逻辑,可使用lua-nginx-module配合lua-resty-openssl(生产环境广泛使用的成熟库),无需从零编写核心验证逻辑。

1. 准备依赖

通过Dockerfile构建包含所需模块的Nginx镜像:

FROM nginx:1.27

# 安装依赖并编译Lua模块
RUN apt-get update && apt-get install -y libssl-dev luarocks
RUN luarocks install lua-resty-openssl

2. Nginx配置中的Lua验证逻辑

在配置中添加access_by_lua_block,在请求阶段完成验证:

server {
    listen 80;

    set_real_ip_from 10.0.0.0/8;
    real_ip_header X-Forwarded-For;
    real_ip_recursive on;

    location / {
        access_by_lua_block {
            local cert_header = ngx.var.http_x_forwarded_client_cert
            if not cert_header then
                ngx.exit(ngx.HTTP_FORBIDDEN)
            end

            # 清理证书内容
            local cert = string.gsub(cert_header, "^cert=", "")
            cert = ngx.decode_base64(cert)

            # 加载CA证书并验证签名
            local ssl = require("resty.openssl.ssl")
            local ctx, err = ssl.new_ctx()
            if not ctx then
                ngx.log(ngx.ERR, "Failed to create SSL context: ", err)
                ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
            end
            local ok, err = ctx:load_verify_locations("/etc/nginx/ca/rootCA.pem")
            if not ok then
                ngx.log(ngx.ERR, "Failed to load CA cert: ", err)
                ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
            end
            local ok, err = ctx:verify(cert)
            if not ok then
                ngx.log(ngx.ERR, "Cert verification failed: ", err)
                ngx.exit(ngx.HTTP_FORBIDDEN)
            end

            # 验证证书元数据(示例:检查CN字段前缀)
            local x509 = require("resty.openssl.x509")
            local cert_obj, err = x509.new(cert)
            if not cert_obj then
                ngx.log(ngx.ERR, "Failed to parse cert: ", err)
                ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
            end
            local subject = cert_obj:get_subject_name()
            local cn = subject:find("CN")
            if not cn or not string.match(cn, "^allowed-cn-") then
                ngx.exit(ngx.HTTP_FORBIDDEN)
            end
        }

        # 正常的静态文件/反向代理配置
        root /usr/share/nginx/html;
    }
}

总结

  • 若不熟悉Lua,优先选择auth_request+Shell脚本方案,实现简单、易维护,无额外学习成本。
  • 若需要高性能或复杂逻辑,使用Lua+lua-resty-openssl方案,该库经过大量生产环境验证,稳定性有保障。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 20:45:11