Вопрос или проблема
У меня есть сервер с тремя изображениями 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. Вот как можно сделать это шаг за шагом:
-
Используйте переменные для сохранения значений: Создайте переменные для хранения значения порта и схемы, чтобы избежать их потери при перенаправлении.
-
Измените конфигурацию блоков: Предлагаю внести изменения в вашу конфигурацию следующим образом:
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
, что позволяет сохранить правильный порт относительно внешнего доступа.
Проверка настройки
После внесения коррективов важно протестировать вашу конфигурацию:
- Проверьте статусы HTTP для запросов к
/secret/
. - Убедитесь, что пользователь перенаправляется именно на
http://server_name:800/api/user/login/?returnto=$request_uri
. - Проверьте логи Nginx для выявления возможных ошибок или некорректных перенаправлений.
Заключение
Следуя приведенным рекомендациям, вы сможете настроить корректное поведение перенаправлений в Nginx, сохраните внешний порт и обеспечьте плавный процесс аутентификации для пользователей. Не забывайте также периодически обновлять документацию вашего проекта, чтобы все изменения были задокументированы, что поможет избежать путаницы в будущем.