我正在使用Nginx和uWSGI来服务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视为用户名。 但是,当没有客户端证书时,如何使其回退到基本身份验证(当证书存在时,不但验证失败 - 在这种情况下只是错误)?我找到的一个例子就是让一个单独的服务器块监听另一个端口,我想避免它: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
。
答案 0 :(得分:6)
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;
}
答案 1 :(得分:1)
我看到两种可能的解决方案,您可以覆盖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
在定义$remote_user
时使用$ssl_client_s_dn_cn
作为map
变量的默认值:
map $ssl_client_s_dn $ssl_client_s_dn_cn
{
default $remote_user;
~/CN=(?<CN>[^/]+) $CN;
}
请注意,map
指令无法在server
上下文中使用,而location
则应该使用{{1}}。并且还要注意Nginx变量不能被覆盖。