【问题标题】:Nginx - how to access Client Certificate's Subject Alternative Name (SAN) fieldNginx - 如何访问客户端证书的主题备用名称(SAN)字段
【发布时间】:2018-05-30 21:47:18
【问题描述】:

我有一个 Nginx 服务器,客户端使用包含特定 CN 和 SAN 的客户端证书向其发出请求。我希望能够提取该客户端证书的 CN(通用名称)和 SAN(主题备用名称)字段。

粗略的示例配置:

server {
listen 443 ssl;
ssl_client_certificate /etc/nginx/certs/client.crt;
ssl_verify_client on; #400 if request without valid cert

location / {
    root    /usr/share/nginx/html;

}
location /auth_test {
    # do something with the CN and SAN.
    # tried these embedded vars so far, to no avail
    return 200 "
    $ssl_client_s_dn 
    $ssl_server_name
    $ssl_client_escaped_cert
    $ssl_client_cert
    $ssl_client_raw_cert";
}
}

使用作为ngx_http_ssl_module 模块的一部分公开的嵌入式变量,我可以访问 DN(专有名称)和 CN 等,但我似乎无法访问 SAN。

我是否缺少一些嵌入式 var/其他模块/通用 Nginx foo?我可以访问原始证书,那么是否可以手动解码并提取它?

我真的宁愿在 Nginx 层执行此操作,而不是将证书向下传递到应用程序层并在那里执行。

非常感谢任何帮助。

【问题讨论】:

    标签: nginx ssl-certificate x509 client-certificates


    【解决方案1】:

    您可以使用 Nginx 内置 map 提取它们,例如中国:

    map $ssl_client_s_dn $ssl_client_s_dn_cn {
        default "";
        ~,CN=(?<CN>[^,]+) $CN;
    }
    

    【讨论】:

    • 自 nginx 版本 1.11.6 起,$ssl_client_s_dn 的格式已更改为使用逗号而不是斜杠。 (serverfault.com/q/829754)。上面的版本反映了这一点,但是在野外有相当多的例子使用旧格式 ~/CN=(?[^/]+) $CN;您可以轻松地忽略斜线而不是现在正确的逗号。至少我花了很长时间才找到我的配置错误。
    • 如果只有 CN 会失败。
    • @js。它在哪里,位于 nginx.conf 中?然后我如何将它作为标头传递给后端服务器?
    • 这不会获取 SAN。上面的代码只会给CN
    【解决方案2】:

    我不是 lua 专家,但这是我的工作:

    local openssl = require('openssl')
    
    dnsNames = {}
    for k,v in pairs(openssl.x509.read(ngx.var.ssl_client_raw_cert):extensions()) do
        for k1,v1 in pairs(v:info()) do
            if(type(v1)=='table') then
                for k2,v2 in pairs(v1) do
                    if(type(v2)=='table') then
                        for k3,v3 in pairs(v2) do
                            if(k3=='dNSName') then
                                table.insert(dnsNames, v3:toprint())
                            end
                        end
                    end
                end
            end
        end
    end
    ngx.say(table.concat(dnsNames, ':'))
    

    【讨论】:

    • 这到底是做什么的? :) 输出看起来如何?
    • 而这又是如何集成到 nginx.conf 中的呢?
    • 这只会找到 SAN 中的所有 DNS 条目,并用冒号连接它们并打印它们。这只是一个示例,您可以在dnsNames 中获取列表,然后随心所欲地使用它。下面是一个简单的 lua 示例:blog.cloud66.com/supercharging-nginx-with-lua-part-2
    • 我很难在 Docker 中设置它。官方的 nginx 镜像没有 Lua 支持。 This image 可以,但是找不到 openssl 模块...这是模块吗? github.com/zhaozg/lua-openssl
    • 在使用RUN luarocks install openssl 之前,终于使用FROM openresty/openresty:bionicRUN apt-get install -y libssl-dev 在Docker 上运行了它。现在开始你的功能。 x509.extension 文档有点帮助:)
    【解决方案3】:

    你可以通过 OpenResty + Lua-OpenSSL 实现,并解析原始证书得到它。

    请参考:https://github.com/Seb35/nginx-ssl-variables/blob/master/COMPATIBILITY.md#ssl_client_s_dn_x509

    就像这样:

    local varibleName = string.match(require("openssl").x509.read(ngx.var.ssl_client_raw_cert):issuer():oneline(),"/C=([^/]+)")
    

    【讨论】:

    • 虽然这使我走上了一条有趣的道路(OpenResty、Nginx 中的 Lua、Lua-OpenSSL),但它最终没有奏效。这只是获取发行者信息。我想不出一种从证书中提取 SAN 的方法。也许我错过了什么?我在这里没有看到任何有用的东西:zhaozg.github.io/lua-openssl/modules/x509.html
    【解决方案4】:

    当我尝试通过上游服务器检索“主题 DN”时,遇到了同样的问题。 有人可能会发现以下建议很有用。因此,有一个访问 到(“主题DN”等)之类的字段-您必须查看link1。除此之外,我必须通过这些数据进入请求标头,所以我通过'proxy_set_header' (link2) 完成了它。没有任何额外的 Nginx 扩展是可能的(不需要使用 --modules 重建它们,只需默认模块)

    【讨论】:

    • 如作者所说,ngx_http_ssl_module默认不暴露SAN。
    【解决方案5】:

    这是一个示例,如何从客户端证书扩展中提取 URI 值,然后将其作为标头转发到上游服务器。例如,这在实现WebID over TLS 身份验证时很有用。

    location / {
        proxy_pass http://upstream;
    
        set_by_lua_block $webid_uri {
            local openssl = require('openssl')
    
            webIDs = {}
            for k,v in pairs(openssl.x509.read(ngx.var.ssl_client_raw_cert):extensions()) do
                for k1,v1 in pairs(v:info()) do
                    if(type(v1)=='table') then
                        for k2,v2 in pairs(v1) do
                            if(type(v2)=='table') then
                                for k3,v3 in pairs(v2) do
                                    if(k3=='uniformResourceIdentifier') then
                                        table.insert(webIDs, v3:data())
                                    end
                                end
                            end
                        end
                    end
                end
            end
    
            return webIDs[1]
        }
    
        proxy_set_header X-WebID-URI $webid_uri;
    }
    

    让我知道是否可以改进。

    【讨论】:

      猜你喜欢
      • 2016-04-13
      • 1970-01-01
      • 2014-01-25
      • 2017-06-08
      • 1970-01-01
      • 1970-01-01
      • 2011-08-21
      • 2023-01-18
      • 2012-10-19
      相关资源
      最近更新 更多