Nginx 将客户端证书映射到 REMOTE_USER for uWSGI 并回退到基本身份验证?

Nginx map client certificate to REMOTE_USER for uWSGI with fallback to basic auth?

我正在使用带有 uWSGI 的 Nginx 来为 Mercurial 提供服务;它通过 SSL 进行基本身份验证(Nginx 是 SSL 终结器;它不会传递给 Hg),但是由于基本身份验证的安全性有限,即使通过 SSL,正如在包括本网站在内的各个地方所讨论的,我想允许用户也可以连接客户端证书,例如 TortoiseHg 支持的东西。

ssl_verify_client optional;
...
map $ssl_client_s_dn $ssl_client_s_dn_cn
{
    default "";
    ~/CN=(?<CN>[^/]+) $CN;
};
...
location /
{
    uwsgi_pass unix:/run/uwsgi/app/hgweb/socket;
    include uwsgi_params;
    uwsgi_param SERVER_ADDR $server_addr;
    uwsgi_param REMOTE_USER $ssl_client_s_dn_cn;
    #uwsgi_param REMOTE_USER $remote_user;
    #auth_basic "Mercurial repositories";
    #auth_basic_user_file /srv/hg/.htpasswd;

}

所以我将 CN 视为用户名。 但是,当没有客户端证书时(最好是 not 当有证书但验证失败时,我该如何回退到基本身份验证 - 在这种情况下只是错误)?我发现的一个例子是通过让一个单独的服务器块监听另一个端口来实现的,我想避免这种情况:https://github.com/winne27/nginx-cert-and-basic-auth/blob/master/nginx-example.conf

此外,在某些示例中,我在 location 中看到了以下检查;他们有必要吗? if ($ssl_client_verify != SUCCESS) { return 496; } if ($ssl_client_s_dn_cn !~ "^[a-z0-9]{1,10}$") { return 496; } 鉴于 http://wiki.nginx.org/IfIsEvil 我认为最好避免使用 if.

我看到两个可能的解决方案,您可以覆盖 uwsgi_param 或使用 $remote_user 变量 $ssl_client_s_dn_cn.

的默认值

要覆盖 uwsgi_param(这也适用于 fastcgi_param),请按照您的建议使用 map 指令(只需删除“}”之后的“;”),并为指令使用 if_not_empty 参数:

uwsgi_param REMOTE_USER $remote_user;
uwsgi_param REMOTE_USER $ssl_client_s_dn_cn if_not_empty;

$ssl_client_s_dn_cn 应该覆盖 $remote_user(如果存在)。这种做法的好处是可以在其他地方(例如日志格式)分别使用两个不同的变量名。

见: http://nginx.org/en/docs/http/ngx_http_uwsgi_module.html#uwsgi_param


在定义 map 时使用 $remote_user 作为 $ssl_client_s_dn_cn 变量的默认值:

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

请注意 map 指令不能在 server 上下文中使用,而 location 应该。还要注意 Nginx 变量不能被覆盖。

Nginx 1.11 和 1.12 更改了 $ssl_client_s_dn_cn.

的引用

如果你来这里头疼,试试这个正则表达式:

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