Сертификат клиента сопоставления Nginx с REMOTE_USER для uWSGI с откатом к базовой аутентификации?

Я использую 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.


person Display Name    schedule 05.05.2015    source источник
comment
Чтобы уточнить, моя проблема с proxy_pass для другого блока сервера, как в связанном примере, - это эффективность. Это беспокойство неуместно? Использует ли nginx некоторую оптимизацию при проксировании самого себя? Кроме того, в этом примере похоже, что SSL обрабатывается как блоком 443, так и отказоустойчивым сервером. Последнее обязательно? Не будет ли 127.0.0.1:80 более эффективным, не влияя на безопасность, учитывая, что проксирование является внутренним для хоста?   -  person Display Name    schedule 06.05.2015
comment
Хммм, теперь я понимаю, что вам нужно сейчас, и вам действительно нужно будет обработать это в прокси/подзапросе или в вашем приложении (проверка удаленного пользователя и пароля в коде вашего приложения). Тем не менее, следует избегать if. И если вы собираетесь добавить это в свой серверный блок: if ($ssl_client_verify != SUCCESS) { return 496; } рассмотрите возможность использования ssl_verify_client on; вместо необязательных.   -  person Capilé    schedule 25.05.2015


Ответы (2)


Я вижу два возможных решения: вы можете либо перезаписать 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 следует. А также обратите внимание, что переменные Nginx не могут быть перезаписаны.

person Capilé    schedule 22.05.2015
comment
Но могу ли я предположить, что $ssl_client_s_dn просто отсутствует (я предполагаю, что вы имеете в виду неопределенный), когда проверка сертификата не удалась? - person Display Name; 23.05.2015
comment
В первом примере, если это не удается или даже если это не https-запрос, он становится пустым, тогда директива if_not_empty запрещает его установку в параметре REMOTE_USER uwsgi. Обратите внимание, что это зависит от директивы map, которую вы указали выше (в противном случае она не будет установлена, и будет выдана ошибка). Во втором примере мы используем $remote_user в качестве значения по умолчанию для переменной $ssl_client_s_dn_cn, поэтому вам все равно нужно установить строку uwsgi_param REMOTE_USER $ssl_client_s_dn_cn;. Я обновлю свой ответ, чтобы объяснить это. - person Capilé; 25.05.2015

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;
}
person D.S. Ljungmark    schedule 07.09.2017