
Рядовые инфраструктурные задачи иногда преподносят неожиданные сложности. Нашей целью была организация защищённого доступа к внутренним сервисам через единую точку входа с авторизацией в Keycloak, чтобы приложения получали проверенные пользовательские данные без необходимости реализации собственной логики аутентификации.
Изначально решение виделось простым — настроить Nginx и прописать правила маршрутизации. Ключевые трудности скрывались в деталях: тонкой настройке редиректов, работе с access token и интеграции с бэкенд-сервисами, потребовавших значительного внимания.
В статье расскажем о нашем пути построения схемы обратного прокси с авторизацией через Keycloak: от поиска решения до создания стабильного и масштабируемого результата.
Поиск решения
В нашей инфраструктуре в качестве главного веб-сервера используется Nginx. Поэтому решение должно быть лёгким, подниматься рядом с основным приложением, например, в том же docker-compose, и при этом не требовать глубокого вмешательства в логику сервиса.
Мы искали способ, который бы перехватывал запросы на уровне Nginx, проверял авторизацию пользователя в Keycloak и, если её нет, перенаправлял на страницу входа в Keycloak, а после успешной авторизации возвращал пользователя обратно к целевому сервису. В итоге у нас получилась следующая схема.

Мы довольно быстро отказались от идеи писать собственное решение — в нашем случае это оказалось нерелевантным из-за трудозатратности и нюансов развёртывания. Поддержка кастомной реализации заняла бы слишком много времени, а хотелось получить рабочий инструмент «здесь и сейчас». Поэтому мы начали рассматривать готовые решения.
Что сейчас есть на рынке:
-
OAuth2-Proxy
Популярный self-hosted reverse proxy, который применяется для реализации внешнего потока аутентификации. Он отлично интегрируется с Keycloak как OIDC-провайдером и легко подключается к Nginx (через
auth_request) или Traefik (черезForwardAuth).Преимущества:
Простая и быстрая интеграция через docker-compose.
Поддержка множества провайдеров «из коробки».
Активное сообщество и подробная документация по связке Keycloak + Nginx.
Недостатки:
Тонкую авторизацию нужно реализовать на стороне Keycloak или backend.
-
Authentik
Современное open-source решение для SSO/IDM, которое поддерживает режим reverse proxy через собственный «Proxy Provider». Может полностью заменить связку Keycloak + oauth2-proxy или использоваться в паре.
Преимущества:
Развитая ролевая модель (RBAC), MFA, policy-based доступ.
Удобная админпанель и возможность multi‑tenancy.
Можно использовать как центральный IdP или как обратный прокси.
Недостатки:
Более сложная настройка.
Молодое решение, которое уступает oauth2-proxy по количеству кейсов в production.
-
Authelia
Лёгкий self‑hosted gateway для аутентификации (forward‑auth mode), интегрируемый с Keycloak или LDAP. Идеален для небольших деплоев, домашних лабораторий и внутренних систем.
Преимущества:
Простая установка и настройка.
Хорошо подходит для небольших инфраструктур и SSO с 2FA.
Недостатки:
Ограничены enterprise‑возможности и масштабируемость.
Меньше гибкости в интеграции, чем у Authentik или OAuth2-Proxy.
-
Nginx с Lua‑модулями (lua‑resty‑oidc / OpenResty)
Расширение возможностей Nginx за счёт Lua‑скриптов, позволяющее реализовать авторизацию по OIDC без внешнего прокси.
Преимущества:
Максимальная гибкость и возможность кастомных решений прямо на уровне Nginx.
Нет внешнего компонента-прокси — минимальная задержка и зависимостей.
Недостатки:
Требуется знание Lua и ручная реализация логики авторизации.
Меньше готовых примеров для интеграции с Keycloak.
Среди доступных вариантов остановились на oauth2-proxy: он предлагает множество интеграций с различными провайдерами «из коробки», если вдруг нужно будет уйти от Keycloak, активно используется сообществом и хорошо документирован.
Принцип работы
Рассмотрим логику работы сервиса в нашем сценарии, где прокси будет работать по протоколу OIDC и мидлвару на стороне сервиса.
1. Пользователь обращается к приложению
Пользователь открывает страницу по адресу https://example.fvds.ru.
Перед приложением работает oauth2-proxy, который принимает входящие запросы и проверяет, имеет ли клиент действующую авторизационную сессию.
2. Проверка локальной сессии
oauth2-proxy проверяет наличие валидной куки (например, oauth2proxy), где в зашифрованном виде хранится информация о пользователе — email, user_id, access_token и т. п.
Если кука отсутствует или токен истёк, прокси выполняет редирект на страницу авторизации Keycloak.
3. Редирект на Keycloak для логина
Для аутентификации пользователя oauth2-proxy перенаправляет запрос к Keycloak — например, на адрес:
https://keycloak.example.com/realms/myrealm/protocol/openid-connect/auth
В запросе передаются параметры:
client_id— идентификатор клиента (oauth2-proxy) в Keycloak;redirect_uri— адрес, на который Keycloak вернёт пользователя после успешного входа;scope— список запрошенных данных (чаще всего openid email profile).
4. Ввод логина/пароля в Keycloak
Пользователь вводит логин и пароль на форме Keycloak.
После успешной проверки Keycloak возвращает пользователя по redirect_uri, указанному ранее, и добавляет authorization code — временный код для обмена на токены.
5. Обмен authorization code на токены
Oauth2-proxy обращается к токен‑эндпоинту Keycloak:
https://keycloak.example.com/realms/myrealm/protocol/openid-connect/token
И получает набор токенов:
ID Token— JWT со сведениями о пользователе (sub, name, email и т. д.).Access Token— токен доступа к защищённым ресурсам.Refresh Token— для автоматического продления сессии.
6. Создание и хранение сессии
Oauth2-proxy извлекает нужные поля из ID Token (например, email), шифрует данные и сохраняет их в куке пользователя (cookie_secret из конфига отвечает за подпись и шифрование).
После этого все последующие запросы проходят без повторного входа, пока сессия действительна.
7. Проксирование в backend
При каждом запросе oauth2-proxy:
проверяет корректность и срок действия токенов;
при необходимости сверяет подпись через публичные ключи Keycloak (JWKS);
X-Auth-Request-Email: user@example.com
X-Auth-Request-User: 12345678
Authorization: Bearer <access_token>
Бэкенд‑приложение получает готовые данные о пользователе и не обязано само реализовывать логику OIDC или OAuth2‑аутентификации.
Реализация
В соответствии с этой логикой реализуем базовый сервис, используя докер.
Создание клиента в Keycloak
Саму установку keykloaka мы описывать здесь не будем, но вы всегда можете почитать про неё у нас сайте.
А теперь для каждой прокси-мидлвары надо создать свой клиент. Для этого нужно выполнить следующие действия:
-
Перейти в основной реалм. В нашем случае это «Experiments».

-
Перейти в клиенты и нажать на «Create client».

-
Создать клиент с нужными полями.
для начала в общих настройках нужно указать:
Client type -— OpenID Connect.
Client ID — советую называть по паттерну — Oauth2-Proxy-<название приложения>, чтобы легче различать в списке.
Name и Description — Прокси для приложения <название приложения>
Затем нажимаем Next.

В «Capability config» выставляем настройки:
Client authentication — On.
Standard flow — проставляем галку.
Direct access grants — отключаем.

Перейдём в «Login settings»: в Valid redirect URIs: ставим ваш домен и /oauth2/callback.

Конфигурация docker-compose
Самый быстрый способ поднять инфраструктуру — добавить oauth2-proxy в существующий docker-compose.yml.
Он запускается рядом с вашим приложением и берёт на себя всю авторизацию.
version: '3.9'
services:
....код ваших сервисов....
oauth2-proxy:
image: quay.io/oauth2-proxy/oauth2-proxy:latest
container_name: oauth2-proxy
depends_on:
- backend
- frontend
ports:
- "4180:4180"
command: ["--config", "/etc/oauth2-proxy.cfg"]
volumes:
- ./oauth2-proxy.cfg:/etc/oauth2-proxy.cfg:ro
В данном примере oauth2-proxy будет слушать порт 4180, используя конфигурацию из локального файла oauth2-proxy.cfg.
Основная логика прокси — принимать запросы от Nginx, проверять наличие авторизационной сессии и при необходимости перенаправлять пользователя на Keycloak для входа.
Правим конфигурационный файл
Теперь заполним файл oauth2-proxy.cfg, в котором указываются параметры взаимодействия с Keycloak и настройки сессий:
# === Основные параметры аутентификации ===
provider = "keycloak-oidc"
client_id = "client"
client_secret = "secret"
# === Настройки редиректов и точек входа ===
redirect_url = "https://url-yours-client.fvds.ru/oauth2/callback"
upstreams = [ "https://url-yours-client.fvds.ru" ]
http_address = "0.0.0.0:4180"
# === Адрес Keycloak Issuer ===
oidc_issuer_url = "https://keycloak.fvds.ru/realms/your-realm"
# === Ключи и cookie ===
cookie_secret = "secret"
cookie_secure = false
email_domains = [ "*" ]
whitelist_domains = [ ".fvds.ru" ]
# === Дополнительные настройки безопасности ===
code_challenge_method = "S256"
skip_jwt_bearer_tokens = false
cookie_expire = "6m"
cookie_refresh = "5m"
# Пример ограничений по ролям/группам (при необходимости)
allowed_groups = [ "Developers" ]
allowed_roles = [ "AppUser" ]
где:
Параметр |
Значение (пример) |
Описание |
provider |
"keycloak-oidc" |
Тип поставщика аутентификации — Keycloak через протокол OpenID Connect. |
client_id |
"client" |
Идентификатор клиента, зарегистрированный в Keycloak (Client ID в настройках клиента). |
client_secret |
"secret" |
Секрет клиента из Keycloak (раздел Credentials → Client Secret). Используется при обмене кода на токены. |
redirect_url |
URL, на который Keycloak перенаправляет пользователя после успешного логина. Должен быть добавлен в поле Valid Redirect URIs в настройках клиента Keycloak. |
|
upstreams |
Адрес(а) backend-приложения, куда проксируются запросы после успешной аутентификации. |
|
http_address |
"0.0.0.0:4180" |
Адрес и порт, на которых слушает сам oauth2-proxy. Обычно это 4180. |
oidc_issuer_url |
Адрес “Issuer” — корневая точка Keycloak Realm; по ней oauth2-proxy ищет все нужные OAuth2/OIDC эндпоинты. |
|
cookie_secret |
"secret" |
Секретный ключ для подписи и шифрования cookies. В продакшне должен быть 32 байта base64. |
cookie_secure |
false |
При true cookie передаются только по HTTPS. Для продакшена ставится true, для локальной разработки можно false. |
email_domains |
["*"] |
Разрешённые домены email. "*" — все email. ["example.com"] — только указанные домены. |
whitelist_domains |
[ ".fvds.ru" ] |
Список доменов, для которых прокси будет устанавливать cookie. Точка в начале (.) означает, что кука действительна для поддоменов (api.fvds.ru, app.fvds.ru). |
code_challenge_method |
"S256" |
Метод PKCE для обмена кодом авторизации: S256 — безопасный способ (SHA-256‑хэш verifier-а). Защищает от подмены кода. |
skip_jwt_bearer_tokens |
false |
Если true, запросы с валидным JWT проходят без проверки cookie. При false (рекомендуется) все запросы проверяются через oauth2-proxy. |
cookie_expire |
"6m" |
Срок жизни cookie-сессии после аутентификации — здесь 6 минут. |
cookie_refresh |
"5m" |
Интервал обновления cookie — например, каждые 5 минут обновлять подпись и данные, пока пользователь активен. |
allowed_groups (опционально) |
[ "Developers" ] |
Ограничение доступа по группам из токена Keycloak (поля groups). Пропускаются только участники указанных групп. |
allowed_roles (опционально) |
[ "SecuretyUsers" ] |
Ограничение по ролям (например, роли realm или client-level из токена Keycloak). |
Дополнительные параметры можно посмотреть в документации.
Настраиваем веб-сервер
Для полноценной работы в Nginx нужно добавить маршруты, которые направляют все запросы /oauth2/* к прокси, а остальные — к вашему приложению.
server {
server_name example.fvds.ru; # ваш домен
access_log /var/log/nginx/example.access.log;
error_log /var/log/nginx/example.error.log;
# увеличенные размеры буферов (полезно для авторизационных заголовков)
proxy_buffers 8 16k;
proxy_buffer_size 32k;
proxy_busy_buffers_size 64k;
large_client_header_buffers 4 16k;
# --- OAuth2 Proxy ---
location /oauth2/ {
proxy_pass http://127.0.0.1:4180;
proxy_set_header Host $host;
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;
proxy_buffers 8 16k;
proxy_buffer_size 32k;
proxy_busy_buffers_size 64k;
}
# --- Основное приложение ---
location / {
auth_request /oauth2/auth;
error_page 401 = @oauth2_signin;
proxy_connect_timeout 3600;
proxy_send_timeout 3600;
proxy_read_timeout 3600;
send_timeout 3600;
proxy_pass http://127.0.0.1:8112; # адрес вашего приложения
proxy_http_version 1.1;
proxy_set_header Host $host;
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 = /oauth2/auth {
proxy_pass http://127.0.0.1:4180;
proxy_set_header Host $host;
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;
proxy_buffers 8 16k;
proxy_buffer_size 32k;
proxy_busy_buffers_size 64k;
}
# Редирект на login, если не авторизован
location @oauth2_signin {
return 302 /oauth2/start?rd=$scheme://$host$request_uri;
}
listen 80;
}
Проверяем код с помощью команды:
nginx -t
И в случае успеха перезапускаем nginx:
nginx -s reload
Если всё работает, выпускаем и подключаем сертификат Let’s Encrypt через certbot, например так:
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d example.fvds.ru
sudo systemctl reload nginx
Certbot автоматически добавит SSL‑блоки в ваш конфиг и настроит редиректы с HTTP на HTTPS.
После этого сайт будет доступен только после входа через Keycloak.
Подводя итоги
Связка Nginx + OAuth2‑Proxy + Keycloak позволила организовать удобную и безопасную схему авторизации для всех внутренних сервисов. Мы получили централизованное управление пользователями, поддержку SSO и минимальные изменения на стороне самих приложений.
Главное преимущество подхода — простота интеграции: достаточно добавить отдельный контейнер oauth2-proxy в docker-compose и прописать несколько директив в конфигурации Nginx (auth_request, proxy_pass, redirect). Это решение масштабируется, хорошо документировано и активно используется сообществом, что делает его надёжным выбором для production‑окружений внутренних сервисов. Если потребуется расширение функциональности, то к этой схеме можно безболезненно добавить проверки ролей через Keycloak или внедрить дополнительные сервисы мониторинга авторизационных событий.
Такой стек стал для нас универсальной точкой входа в экосистему защищённых веб‑приложений и позволяет внедрять современные стандарты авторизации без избыточной сложности в инфраструктуре.
НЛО прилетело и оставило здесь промокод для читателей нашего блога:
-15% на заказ любого VDS (кроме тарифа Прогрев) — HABRFIRSTVDS.