Конфигурация Nginx: как использовать аутентификацию auth_basic, если ssl_client_certificate не предоставлено?

Я пытаюсь настроить сервер Nginx следующим образом:

Во-первых, сервер должен проверить, предоставил ли пользователь сертификат SSL клиента (через ssl_client_certificate). Если сертификат SSL предоставлен, то предоставите доступ к сайту,

Если сертификат SSL НЕ предоставляется, попросите пользователя ввести пароль и войти в систему через auth_basic.

Мне удалось настроить оба метода аутентификации одновременно. Но этот конфиг лишний.

Чтобы проверить, предоставил ли пользователь свой SSL-сертификат, я пытаюсь настроить конфигурацию следующим образом:

18:    if ($ssl_client_verify != SUCCESS) {
19:        auth_basic "Please login";
20:        auth_basic_user_file .passfile;
21:    }

Но Nginx возвращает ошибку:

Директива auth_basic не разрешена здесь... /ssl.conf: 19

Как я могу установить условие в этом случае?

Ответ 1

Вы можете установить конфигурацию auth_basic в предложении if следующим образом:

server {
    listen 443;
    auth_basic_user_file    .htpasswd;
    ssl_client_certificate  ca.cert;
    ssl_verify_client       optional;
    ...

    location / {
      ...

      if ($ssl_client_verify = SUCCESS) {
        set $auth_basic off;
      }
      if ($ssl_client_verify != SUCCESS) {
        set $auth_basic Restricted;
      }

      auth_basic $auth_basic;
    }
}

Теперь аутентификация возвращается к HTTP Basic, если сертификат клиента не предоставлен (или если проверка не выполнена).

Ответ 2

Я не могу проверить это сейчас, но что-то вроде этой работы?

server {
    listen 80;
    server_name www.example.com example.com;
    rewrite ^ https://$server_name$request_uri? permanent;
}
server {
    listen 443;
    ...

    if ($ssl_client_verify != SUCCESS) {
        rewrite ^ http://auth.example.com/ permanent;
    } 
    location / {
        ...
    }

}
server {
    listen 80;
    server_name auth.example.com;
    location / {
        auth_basic "Please login";
        auth_basic_user_file .passfile;
    }
} 

Итак, в основном:
- Примите все начальные запросы (на порт 80 для любого имени, которое вы используете) и перепишите в ssl
- Проверьте, проверен ли клиент.
- Если нет, перепишите в альтернативный домен, который использует базовый auth

Как я уже сказал, я не могу проверить это прямо сейчас, но я постараюсь обойти это! Дайте мне знать, если это поможет, мне интересно узнать, работает ли это.

Ответ 3

Nginx не может отказаться от базовой проверки подлинности, когда клиентский сертификат завершается с ошибкой. В качестве альтернативы вы можете использовать переменные для ограничения доступа:

location / { 
  if ($ssl_client_verify = "SUCCESS") {
    set $authorized 1;
  }
  if ($authorized != 1) {
    error_page 401 @basicauth;
    return 401;
  }
}

location @basicauth {
  auth_basic "Please login";
  auth_basic_user_file .passfile;
  set $authorized 1;
  rewrite /(.*) /$1;
}

* Имейте в виду, что IfIsEvil и эти правила могут работать некорректно или мешать другим частям более крупной конфигурации.

Ответ 4

Забудьте об этом, это не сработает.

Причина, по которой он терпит неудачу, заключается в том, что if он не является частью общего конфигурационного модуля, как следует полагать. if это часть модуля перезаписи, а auth_basic - еще один модуль. У вас просто не может быть динамических vhosts с базовым auth.

С другой стороны...

У вас могут быть динамические vhosts со своими страницами ошибок. Следующий пример предназначен для пользовательской страницы 404, но вы можете реализовать ее в своем коде.

server {
    listen 80;
    server_name _;

    set $site_root /data/www/$host;

    location / {
        root $site_root;
    }

    error_page 404 =404 /404.html;

    location /404.html {
        root $site_root/error_files;
        internal;

        error_page 404 =404 @fallback_404;
    }

    location @fallback_404 {
        root /var/www/;
        try_files /404.html =404;
        internal;
    }

    error_log  /var/log/nginx/error.log  info;
    access_log  /var/log/nginx/access.log;
}

Что происходит...

  • вы сообщаете Nginx использовать /404.html в случае HTTP_NOT_FOUND.
  • изменение root каталога местоположения в соответствии с error_pages веб-сайта.
  • внутреннее перенаправление
  • возвращение 404 http-кода
  • настройте резервную страницу 404 в location @fallback_404: в этом месте root изменен на /var/www/ поэтому он будет читать файлы с этого пути вместо $site_root
  • на последнем этапе код возвращает /var/www/404.html если он существует с 404 http-кодом.

ПРИМЕЧАНИЕ. Согласно документации Nginx:

Указывает, что данное местоположение может использоваться только для внутренних запросов. Для внешних запросов возвращается ошибка клиента 404 (не найдена). Внутренние запросы:

  • запросы перенаправляются директивами error_page, index, random_index и try_files;
  • запросы перенаправляются с помощью поля заголовка ответа "X-Accel-Redirect" от восходящего сервера;
  • подзапросы, сформированные командой "включить виртуальную" модуля ngx_http_ssi_module и директивами модуля ngx_http_addition_module;
  • запросы, измененные директивой rewrite.

Также:

Для каждого запроса существует ограничение на 10 внутренних перенаправлений для предотвращения циклов обработки запросов, которые могут возникать в неправильных конфигурациях. Если это ограничение достигнуто, возвращается ошибка 500 (Внутренняя ошибка сервера). В таких случаях сообщение "переписать или внутренний цикл перенаправления" можно увидеть в журнале ошибок.

Проверьте эту ссылку для получения дополнительной информации, надеюсь, что это поможет.