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

Изначально решение виделось простым — настроить Nginx и прописать правила маршрутизации. Ключевые трудности скрывались в деталях: тонкой настройке редиректов, работе с access token и интеграции с бэкенд-сервисами, потребовавших значительного внимания.

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

Поиск решения

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

Мы искали способ, который бы перехватывал запросы на уровне Nginx, проверял авторизацию пользователя в Keycloak и, если её нет, перенаправлял на страницу входа в Keycloak, а после успешной авторизации возвращал пользователя обратно к целевому сервису. В итоге у нас получилась следующая схема.

Схема работы прокси сервера
Схема работы прокси сервера

Мы довольно быстро отказались от идеи писать собственное решение — в нашем случае это оказалось нерелевантным из-за трудозатратности и нюансов развёртывания. Поддержка кастомной реализации заняла бы слишком много времени, а хотелось получить рабочий инструмент «здесь и сейчас». Поэтому мы начали рассматривать готовые решения. 

Что сейчас есть на рынке:

  1. OAuth2-Proxy

    Популярный self-hosted reverse proxy, который применяется для реализации внешнего потока аутентификации. Он отлично интегрируется с Keycloak как OIDC-провайдером и легко подключается к Nginx (через auth_request) или Traefik (через ForwardAuth).

    Преимущества:

    • Простая и быстрая интеграция через docker-compose. 

    • Поддержка множества провайдеров «из коробки».

    • Активное сообщество и подробная документация по связке Keycloak + Nginx.

    Недостатки:

    • Тонкую авторизацию нужно реализовать на стороне Keycloak или backend.

  2. Authentik

    Современное open-source решение для SSO/IDM, которое поддерживает режим reverse proxy через собственный «Proxy Provider». Может полностью заменить связку Keycloak + oauth2-proxy или использоваться в паре.

    Преимущества:

    • Развитая ролевая модель (RBAC), MFA, policy-based доступ.

    • Удобная админпанель и возможность multi‑tenancy.

    • Можно использовать как центральный IdP или как обратный прокси.

    Недостатки:

    • Более сложная настройка.

    • Молодое решение, которое уступает oauth2-proxy по количеству кейсов в production.

  3. Authelia

    Лёгкий self‑hosted gateway для аутентификации (forward‑auth mode), интегрируемый с Keycloak или LDAP. Идеален для небольших деплоев, домашних лабораторий и внутренних систем.

    Преимущества:

    • Простая установка и настройка.

    • Хорошо подходит для небольших инфраструктур и SSO с 2FA.

    Недостатки:

    • Ограничены enterprise‑возможности и масштабируемость.

    Меньше гибкости в интеграции, чем у Authentik или OAuth2-Proxy.

  4. 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 мы описывать здесь не будем, но вы всегда можете почитать про неё у нас сайте.

А теперь для каждой прокси-мидлвары надо создать свой клиент. Для этого нужно выполнить следующие действия:

  1.  Перейти в основной реалм. В нашем случае это «Experiments».

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

  3. Создать клиент с нужными полями.

    для начала в общих настройках нужно указать:

    • 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

"https://example.fvds.ru/oauth2/callback"

URL, на который Keycloak перенаправляет пользователя после успешного логина. Должен быть добавлен в поле Valid Redirect URIs в настройках клиента Keycloak.

upstreams

["https://example.fvds.ru"]

Адрес(а) backend-приложения, куда проксируются запросы после успешной аутентификации.

http_address

"0.0.0.0:4180"

Адрес и порт, на которых слушает сам oauth2-proxy. Обычно это 4180.

oidc_issuer_url

"https://keycloak.fvds.ru/realms/your-realm"

Адрес “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.

Комментарии (0)