auth_request в nginx: Как можно учитывать внешний порт при пересылке к error_page?

Вопрос или проблема

У меня есть сервер с тремя изображениями Docker (управляемыми docker compose), которые работают.

  • Обратный прокси на базе nginx
  • API (PHP+Symfony), который обрабатывает аутентификацию, внутренне используя порт 80
  • Служба на Python, которая должна повторно использовать аутентификацию из контейнера API, внутренне используя порт 80

Обратный прокси является точкой входа как для API, так и для службы Python.
Поскольку есть еще одна, не связанная служба, работающая на порту 80/443, обратный прокси доступен извне на порту 800. Однако я не хочу жестко кодировать порт, может быть другая настройка, где порт 80 будет доступен.

Мой желаемый контрольный поток при доступе к службе Python следующий:

  • Проверить, аутентифицирован ли пользователь, вызвав конечную точку внутри контейнера API (user/login/state)
  • Если пользователь аутентифицирован (код ответа 200), проксировать к службе Python
  • Если пользователь еще не аутентифицирован (код ответа 401), перенаправить пользователя на страницу входа внутри контейнера API
  • Если пользователь успешно вошел, перенаправить обратно на изначально запрашиваемую страницу

Мой файл docker compose выглядит так:

services:
  rproxy:
    build: ...
    ports:
      - 800:80
  api:
    build: ...
  secret:
    build: ...

Конфигурация nginx выглядит так:

server {
  listen 80;

  # Логирование ошибок и доступа
  error_log /dev/stderr debug;
  access_log /dev/stdout combined;

  location /api/ {
      proxy_pass http://api/api/;
      proxy_set_header Host $host:$server_port;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header X-Forwarded-Proto $scheme;
  }

  location @error401 {
      return 302 $scheme://$host:$server_port/api/user/login/?returnto=$request_uri;
  }

  location /secret/ {
      # Проверка, аутентифицирован ли пользователь, обращаясь к API
      auth_request /api/user/login/state;

      # Перенаправление на страницу входа при 401 Неавторизовано
      error_page 401 403 = @error401;

      # Если аутентифицирован, проксировать запрос как обычно
      rewrite ^/secret(/.*)$ $1 break;
      proxy_pass http://secret/;
      proxy_set_header Host $host:$server_port;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header X-Forwarded-Proto $scheme;
  }
}

Проверка состояния входа работает как ожидалось. Однако перенаправление на страницу входа после 401 ответа от проверки состояния не работает. Я всегда перенаправляюсь на http://server_name:80/api/user/login на порту 80 вместо правильного http://server_name:800/api/user/login.

Что я попробовал:

  • Сохранение переменной $server_port перед вызовом auth_request -> никаких изменений в поведении
location @error401 {
    return 302 $auth_request_host/api/user/login/?returnto=$request_uri;
}
location /secret/ {
    # Проверка, аутентифицирован ли пользователь, обращаясь к API
    auth_request_set $auth_request_host $scheme://$host:$server_port;
    auth_request /api/user/login/state;
}
  • Жесткое кодирование порта в обработчике ошибок: удивительно, но это тоже не сработало. Сначала я получаю 302 редирект на страницу входа на правильном порту, а затем сразу же получаю еще один 301 редирект на несуществующую страницу входа на порту 80.
location @error401 {
   return 302 $scheme://$host:800/api/user/login/?returnto=$request_uri;
}

У кого-нибудь есть идеи, как сохранить порт в этой настройке?

Ответ или решение

Как сохранить внешний порт при перенаправлении на error_page в Nginx

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

Анализ проблемы

Вы используете auth_request для проверки аутентификации пользователей, обратившись к API. В случае, если пользователь не аутентифицирован, вы получаете ответ 401, который приводит к перенаправлению на страницу входа. Однако ваше перенаправление происходит на неверный порт (80 вместо 800), что затрудняет доступ к странице входа.

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

Решение

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

  1. Используйте переменные для сохранения значений: Создайте переменные для хранения значения порта и схемы, чтобы избежать их потери при перенаправлении.

  2. Измените конфигурацию блоков: Предлагаю внести изменения в вашу конфигурацию следующим образом:

server {
    listen 80;

    error_log /dev/stderr debug;
    access_log /dev/stdout combined;

    # Сохранение значений портов и схемы в переменных
    set $external_port $server_port;
    set $external_scheme $scheme;

    location /api/ {
        proxy_pass http://api/api/;
        proxy_set_header Host $host:$external_port;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $external_scheme;
    }

    location @error401 {
        return 302 $external_scheme://$host:$external_port/api/user/login/?returnto=$request_uri;
    }

    location /secret/ {
        auth_request /api/user/login/state;

        # Перенаправление на страницу входа при 401
        error_page 401 403 = @error401;

        rewrite ^/secret(/.*)$ $1 break;
        proxy_pass http://secret/;
        proxy_set_header Host $host:$external_port;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $external_scheme;
    }
}

Объяснение изменений

  • Переменные: Мы добавили переменные $external_port и $external_scheme для хранения значений порта и схемы. Это позволяет избежать путаницы, когда вы делаете запрос на аутентификацию.

  • Использование переменных: При перенаправлении к странице входа, теперь используется заранее сохраненное значение $external_scheme и $external_port, что позволяет сохранить правильный порт относительно внешнего доступа.

Проверка настройки

После внесения коррективов важно протестировать вашу конфигурацию:

  1. Проверьте статусы HTTP для запросов к /secret/.
  2. Убедитесь, что пользователь перенаправляется именно на http://server_name:800/api/user/login/?returnto=$request_uri.
  3. Проверьте логи Nginx для выявления возможных ошибок или некорректных перенаправлений.

Заключение

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

Оцените материал
Добавить комментарий

Капча загружается...