Nginx - 如何访问客户端证书的主体备用名称(SAN)字段

10

我有一个 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 (Distinguished Name),因此CN等,但似乎无法访问SAN。是否有某些嵌入变量/其他模块/一般的Nginx技巧我所缺失的?我可以访问原始证书,因此是否可能手动解码并提取它?我真的想在Nginx层面做这件事,而不是将证书传递到应用程序层并在那里处理。
非常感谢任何帮助。
5个回答

13

你可以使用Nginx内置的map功能提取它们,例如针对CN:

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

2
自nginx 1.11.6版本发布以来,$ssl_client_s_dn的格式已更改为使用逗号而不是斜杠。(https://serverfault.com/q/829754)。上述版本反映了这一点,但在旧格式中有相当多的示例。 ~/CN=(?<CN>[^/]+) $CN; 您可能会轻易忽略斜杠而不是现在正确的逗号。至少我花了很长时间才找到我的配置错误。 - Arigion
3
如果只有CN存在,这将失败。 - lightsing
3
@js. 这个应该放在nginx.conf的哪里?我怎么将它作为头信息传递给后端服务器呢? - SharpCoder
3
这不会获取存储区域网络(SAN)。上述代码只会给出 CN。 - SharpCoder

5

我不是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, ':'))

这个到底是做什么的? :) 输出结果是什么样子的? - Martynas Jusevičius
1
这个如何集成到nginx.conf中? - Martynas Jusevičius
1
这只是在SAN中查找所有DNS条目并将它们与冒号连接起来并打印出来的示例。这只是一个示例,您可以使用dnsNames列表并对其进行任何操作。以下是简单lua的示例:https://blog.cloud66.com/supercharging-nginx-with-lua-part-2/ - Terry Hardie
1
最终使用 FROM openresty/openresty:bionicRUN apt-get install -y libssl-dev 在 Docker 上运行成功,然后执行 RUN luarocks install openssl。现在正在检查您的函数。x509.extension 文档有所帮助 :) - Martynas Jusevičius
由于问题明确要求如何获取SAN列表,这是唯一一个真正给出答案并应该被接受的答案,@joeweoj。 - Harmelodic
显示剩余2条评论

3

虽然这让我走了一条有趣的路(OpenResty,Nginx中的Lua,Lua-OpenSSL),但它最终失败了。这只是获取发行者信息。我无法找到一种从证书中提取SAN的方法。也许我错过了什么?我在这里没有看到任何有用的东西:https://zhaozg.github.io/lua-openssl/modules/x509.html - Carrot
这是从主题中读取CN字段。它还读取了签名证书的颁发者(实体),这可能不是您想要的。原始问题是询问如何从v3扩展中读取SAN。 - Terry Hardie

0

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

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;
}

如果有改进的地方,请告诉我。


0

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


1
正如作者所说,ngx_http_ssl_module默认情况下不会暴露SAN。 - Martynas Jusevičius

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接