Новинка Виртуальный VDS сервер в Нидерландах от 390р
Выберите продукт

OAuth2-Proxy + Nginx: SSO через Google/Microsoft и auth_request

Разбираем, как превратить Nginx в шлюз SSO с помощью oauth2-proxy и OIDC: защита админок, маршрутов и бэкендов через Google/Microsoft. Даем реальные конфиги, оптимальные таймауты, анти‑петли редиректов, работу с доменами/группами и cookie.
OAuth2-Proxy + Nginx: SSO через Google/Microsoft и auth_request

Когда хочется единой точки входа в админку и внутренние панели, но затевать полноценный IdP (Keycloak и т. п.) — избыточно, на помощь приходит связка oauth2-proxy + Nginx. Мы получаем SSO на базе OIDC с провайдерами Google или Microsoft Entra ID (Azure AD), а авторизацию в приложениях делаем прозрачно: Nginx спрашивает «кто ты?» у локального oauth2-proxy через auth_request, а дальше пробрасывает атрибуты пользователя в заголовках.

Подход масштабируется, легко дебажится и хорошо ложится на привычную схему обратного прокси. В статье собрал практический рецепт для продакшена: быстрый старт, защита выбранных локаций, надёжные таймауты, группы/домены, антициклы редиректов, обработка 401/403, нюансы cookie и проблемы, которые чаще всего бьют по бою.

Архитектура и поток аутентификации

Компоненты простые:

  • Nginx — основной reverse proxy перед приложениями. Для защищаемых локаций выполняет подзапрос auth_request к локальному /oauth2/auth.
  • oauth2-proxy — маленький сервис, который делает OIDC-танец с провайдером (Google/Microsoft), хранит зашифрованные сессионные cookie и по запросу /oauth2/auth отвечает 200/401/403, а также отдаёт заголовки с данными пользователя.
  • Провайдер OIDC — Google или Microsoft Entra ID. Вы создаёте приложение, получаете client_id/client_secret, настраиваете redirect URI.

Высокоуровневый поток:

  1. Клиент приходит в Nginx на защищённый путь. Nginx делает auth_request к /oauth2/auth.
  2. oauth2-proxy проверяет сессионную cookie. Если валидна — 200 и заголовки с атрибутами; если нет — 401.
  3. При 401 Nginx редиректит на /oauth2/start?rd=..., oauth2-proxy ведёт пользователя к провайдеру OIDC, получает ID-токен, проставляет cookie и возвращает на нужный URL.
  4. Дальше все запросы на защищённые пути проходят прозрачно, а Nginx может прокинуть в бэкенд заголовки вроде X-Auth-Request-User, X-Auth-Request-Email, X-Auth-Request-Groups.

Важно: auth_request — это лёгкий подзапрос к локальному oauth2-proxy, а не к внешнему IdP. При валидной cookie провайдер не дёргается, а ответ получается быстро.

Подготовка учётных данных в Google/Microsoft

На стороне провайдера OIDC потребуется зарегистрировать приложение, задать redirect URI и получить Client ID/Client Secret:

  • Google: тип «Веб-приложение», в разрешениях как минимум openid email profile, redirect URI вида https://your-domain.example.com/oauth2/callback.
  • Microsoft Entra ID: регистрируем приложение, включаем ID tokens, настраиваем redirect URI. Для групп можно включить выдачу groups в ID-токен или использовать Graph для групп, но в oauth2-proxy проще работать с claim groups из токена.

URI обратного вызова (/oauth2/callback) должен указывать на хост, где фронтируется oauth2-proxy (обычно тот же виртуальный хост, что и приложение, через префикс /oauth2/...).

Установка и запуск oauth2-proxy

Ниже конфиг в формате YAML с двумя провайдерами (Google, Microsoft). Один из них можно оставить активным, второй — удалить. Режим — auth-only (static://200), чтобы сервис был «чистым» авторизационным эндпоинтом для Nginx.

# /etc/oauth2-proxy/config.yaml
server:
  bind_address: "127.0.0.1:4180"
  metrics_address: "127.0.0.1:4181"
  secure_cookies: true
  cookie:
    name: "_oauth2_proxy"
    secret: "base64-32-bytes-secret-here"
    expire: 12h
    refresh: 1h
    same_site: "Lax"

upstreams:
  - "static://200"

# Проксируем атрибуты для Nginx auth_request
inject_request_headers:
  - name: "X-Auth-Request-User"
    values:
      - claim: "preferred_username"
      - claim: "email"
  - name: "X-Auth-Request-Email"
    values:
      - claim: "email"
  - name: "X-Auth-Request-Groups"
    values:
      - claim: "groups"

providers:
  - id: "google"
    provider: "oidc"
    client_id: "your-google-client-id"
    client_secret: "your-google-client-secret"
    oidc_issuer_url: "https://accounts.google.com"
    scope:
      - "openid"
      - "email"
      - "profile"

  - id: "azure"
    provider: "oidc"
    client_id: "your-azure-client-id"
    client_secret: "your-azure-client-secret"
    oidc_issuer_url: "https://login.microsoftonline.com/<TENANT_ID>/v2.0"
    scope:
      - "openid"
      - "email"
      - "profile"

# Базовые политики
email_domains:
  - "example.com"
allowed_groups:
  - "admins"
  - "devops"

# Настройки редиректов и безопасности
redirect_url: "https://app.example.com/oauth2/callback"
whitelist_domains:
  - ".example.com"
reverse_proxy: true
pass_authorization_header: false
set_authorization_header: false
skip_jwt_bearer_tokens: true

# Лимиты и диагностика
standard_logging: true
request_logging: true
silence_ping_logging: true

Сервис под systemd:

# /etc/systemd/system/oauth2-proxy.service
[Unit]
Description=oauth2-proxy
After=network-online.target
Wants=network-online.target

[Service]
User=oauth2proxy
Group=oauth2proxy
ExecStart=/usr/local/bin/oauth2-proxy --config /etc/oauth2-proxy/config.yaml
Restart=always
RestartSec=2s
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
CapabilityBoundingSet=
AmbientCapabilities=
LockPersonality=true

[Install]
WantedBy=multi-user.target

Проверьте, что cookie_secret действительно 32 байта, закодированные в base64. Пользователь сервиса должен иметь доступ к конфигу и секретам, но не обладать лишними правами. Для изоляции стенда удобно поднять отдельный инстанс на VDS. Для боевого трафика включайте HTTPS с корректными цепочками и современными алгоритмами — пригодятся SSL-сертификаты.

Nginx: включаем auth_request и аккуратные таймауты

Далее — минимально самодостаточный фрагмент. Он защищает все пути, редиректит неаутентифицированных на /oauth2/start, прокидывает атрибуты пользователя и аккуратно выставляет таймауты для подзапроса.

# upstream для oauth2-proxy
upstream oauth2_proxy {
    server 127.0.0.1:4180;
    keepalive 16;
}

# Рекомендуется увеличить лимиты заголовков, если атрибутов/куки много
large_client_header_buffers 8 16k;

server {
    listen 443 ssl http2;
    server_name app.example.com;

    # ... ssl_certificates, security headers ...

    # Таймауты для "боевого" запроса к бэкенду подбирайте отдельно

    # Внутренняя локация для проверки авторизации
    location = /oauth2/auth {
        internal;
        proxy_pass http://oauth2_proxy/oauth2/auth;

        proxy_set_header X-Original-URI $request_uri;
        proxy_set_header X-Original-Method $request_method;
        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_set_header X-Forwarded-Host $host;

        # Акуратные таймауты: auth должен отвечать быстро
        proxy_connect_timeout 0.3s;
        proxy_read_timeout 0.8s;
        proxy_send_timeout 0.8s;

        proxy_http_version 1.1;
    }

    # Вход в SSO и callback обслуживаются самим oauth2-proxy под префиксом /oauth2/
    location /oauth2/ {
        proxy_pass http://oauth2_proxy;
        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_set_header X-Forwarded-Host $host;
        proxy_http_version 1.1;

        proxy_connect_timeout 1s;
        proxy_read_timeout 5s;
        proxy_send_timeout 5s;
    }

    # Сохраняем из auth_request нужные атрибуты
    auth_request_set $auth_user   $upstream_http_x_auth_request_user;
    auth_request_set $auth_email  $upstream_http_x_auth_request_email;
    auth_request_set $auth_groups $upstream_http_x_auth_request_groups;

    # Защищаем весь сайт (можно сузить до /admin и т. п.)
    location / {
        auth_request /oauth2/auth;

        error_page 401 = @oauth2_signin;
        error_page 403 = @oauth2_forbidden;

        # Пробрасываем в бэкенд атрибуты пользователя
        proxy_set_header X-Auth-Request-User   $auth_user;
        proxy_set_header X-Auth-Request-Email  $auth_email;
        proxy_set_header X-Auth-Request-Groups $auth_groups;

        proxy_pass http://app_backend;

        # Таймауты для основного бэкенда — отдельно от auth
        proxy_connect_timeout 2s;
        proxy_read_timeout 60s;
        proxy_send_timeout 60s;
    }

    location @oauth2_signin {
        return 302 /oauth2/start?rd=$scheme://$host$request_uri;
    }

    location @oauth2_forbidden {
        add_header Content-Type text/plain;
        return 403 "Forbidden\n";
    }
}

Замечания:

  • auth_request держим «коротким»: если локальный oauth2-proxy не ответил за доли секунды, лучше отдать ошибку, чем подвесить воркеры Nginx.
  • В локации /oauth2/ ставим более мягкие таймауты: тут идут редиректы и обмен кодов/токенов с провайдером.
  • Если защищать не весь сайт, перенесите auth_request в нужные location (/admin, /internal и т. д.).

Диаграмма потока auth_request: Nginx, oauth2-proxy и OIDC провайдер

Контроль доступа: домены, группы и списки

В oauth2-proxy есть несколько уровней контроля:

  • email_domains — пустит пользователей с адресами из указанных доменов.
  • allowed_groups — потребует наличие одной из групп в claim groups. Для Google группы в ID-токен обычно не приходят; для Entra ID — можно включить; иначе используйте статический список email’ов (authenticated_emails_file).
  • whitelist_domains — ограничение доменов для редиректов (rd=), чтобы пресечь открытые редиректы.

Хорошая стратегия: на уровне oauth2-proxy сузить круг «кто в принципе может войти», а бизнес-правила дореализовать в приложении на основе заголовков X-Auth-Request-*.

Защита от петель и тонкие места редиректов

Циклы встречаются чаще всего из-за неправильного redirect_url, несовпадения хостов/протоколов в заголовках X-Forwarded-* или потому, что cookie не проставляется.

  • Убедитесь, что redirect_url и фактический путь /oauth2/callback совпадают по схеме и хосту.
  • Всегда прокидывайте X-Forwarded-Proto и X-Forwarded-Host к oauth2-proxy, а также включите reverse_proxy: true.
  • Проверьте флаг secure_cookies: true: без HTTPS браузер может отбрасывать cookie; для мультидомена используйте whitelist_domains и корректные cookie-параметры.
  • SameSite: для обычных сценариев хватит Lax. Если редиректы происходят между разными поддоменами — возможно потребуется None и Secure.

Про таймауты: быстрый auth и стабильный бэкенд

Ключевая цель — не допустить блокировки воркеров Nginx при зависшем auth-сервисе и одновременно не резать живые сессии:

  • auth_request: proxy_connect_timeout 200–300ms, proxy_read_timeout 500–800ms. Это подзапрос к локальному процессу — он должен отвечать быстро. Если нет, отдаём 503 и пишем алерт.
  • /oauth2/ (редиректы и обмен кодов): увеличьте таймауты до 3–10 секунд — возможны сетевые походы к провайдеру.
  • бэкенд: свои таймауты по профилю приложения. Для gRPC/стриминга — отдельные значения.

Не увлекайтесь микрокэшем на /oauth2/auth: кэширование 200/401/403 на секунды может дать побочные эффекты (особенно при смене групп/прав). Если и использовать — то осознанно и с дискриминацией по cookie.

FastFox SSL
Надежные SSL-сертификаты
Мы предлагаем широкий спектр SSL-сертификатов от GlobalSign по самым низким ценам. Поможем с покупкой и установкой SSL бесплатно!

Логи и заголовки для бэкенда

Пробрасывайте в приложение минимум два заголовка: X-Auth-Request-User и X-Auth-Request-Email. Если используете группы — X-Auth-Request-Groups. На стороне бэкенда логируйте их вместе с X-Request-ID для трассировки инцидентов. В Nginx можно вывести эти значения и в access_log формат — удобно для быстрой проверки, кто ходит в /admin.

Вебсокеты и долгие соединения

Аутентификация происходит на этапе установления соединения (GET-запрос перед апгрейдом). После апгрейда auth_request уже не участвует. Настройте cookie.refresh и cookie.expire так, чтобы пользователи не «выпадали» в середине длительного соединения.

Фрагменты systemd-сервиса и конфигурации Nginx для oauth2-proxy

Высокая доступность и масштабирование

oauth2-proxy статичен и лёгок. Запускайте минимум две реплики и балансируйте через Nginx upstream. Сессии хранятся в зашифрованной cookie — горизонтальное масштабирование тривиально. Не забывайте про keepalive к upstream и мониторинг готовности (статусные эндпоинты на 4181).

FastFox VDS
Облачный VDS-сервер
Виртуальные серверы с быстрым запуском и гибкой конфигурацией от 390₽ / мес
Доступные локации
Россия Нидерланды

Частые проблемы и их признаки

  • Петля /oauth2/start → провайдер → /oauth2/callback → снова /oauth2/start: проверьте проставление cookie (DevTools), заголовки X-Forwarded-Proto, reverse_proxy, домен cookie и redirect_url.
  • 403 после логина: пользователь прошёл аутентификацию, но не удовлетворяет политикам (email_domains, allowed_groups).
  • 401 на защищённых путях спустя 1 час: истёк cookie.refresh или cookie.expire; либо браузер чистит cookie из-за политики SameSite.
  • 400/431 Request Header Fields Too Large: увеличьте large_client_header_buffers и проверьте размер cookie/заголовков.
  • Подвисания Nginx: слишком большие таймауты на /oauth2/auth и нехватка воркеров. Уменьшите таймауты, включите алерты по росту активных соединений.

Разделение доступа по путям

Иногда нужно, чтобы /admin был доступен только группе admins, а /reports — ещё и devops. Есть два пути:

  • Проверять группы в приложении (проще всего).
  • Поднять второй экземпляр oauth2-proxy с иным набором allowed_groups и повесить его на другой internal-эндпоинт для auth_request конкретного location.

Безопасность: мелочи, которые дают много

  • Генерируйте сильный cookie_secret (32 байта) и храните его в отдельном файле с правами 600.
  • Ограничивайте редиректы whitelist_domains и проверяйте параметр rd на стороне Nginx, если пишете свои ручки.
  • Проставляйте Secure, HttpOnly, корректный SameSite для cookie.
  • Логи oauth2-proxy не должны утекать во внешний мир: не возвращайте их пользователю, храните отдельно, включайте silence_ping_logging.
  • Регулярно пересоздавайте client_secret у провайдера и обновляйте его в конфиге.

Пошаговый чек-лист внедрения

  1. Создайте приложение у провайдера (Google/Microsoft), получите client_id/client_secret, пропишите корректный redirect_url.
  2. Соберите /etc/oauth2-proxy/config.yaml, задайте cookie_secret, домены, группы.
  3. Поднимите oauth2-proxy как сервис, проверьте, что он слушает 4180/4181 и отдаёт 200 на /ping и 401 на /oauth2/auth без cookie.
  4. Настройте Nginx: /oauth2/auth как internal, префикс /oauth2/ на прокси, таймауты, заголовки, error_page 401 на @oauth2_signin.
  5. Залогиньтесь с реальным пользователем, проверьте в DevTools cookie, в логах Nginx/oauth2-proxy статусы 200/302/401/403.
  6. Подключите алерты: рост 401/403, ошибки 5xx у oauth2-proxy, время ответа /oauth2/auth.

Итог

Связка oauth2-proxy + Nginx auth_request — быстрый способ получить корпоративный SSO на базе OIDC с провайдерами Google/Microsoft. Она даёт предсказуемую производительность, наблюдаемость и прозрачную авторизацию через заголовки. Держите короткие таймауты на /oauth2/auth, аккуратно обращайтесь с cookie и редиректами — и получите устойчивый, безопасный и удобный шлюз доступа для админских разделов и внутренних сервисов.

Поделиться статьей

Вам будет интересно

Как безопасно выполнить bootstrap MariaDB Galera после полного останова

Как безопасно выполнить bootstrap MariaDB Galera после полного останова

После одновременной остановки всех узлов Galera обычного запуска MariaDB недостаточно. Сначала найдите сервер с последней транзакц ...
CoreDNS в Kubernetes: исправляем CrashLoopBackOff, SERVFAIL и DNS-петлю

CoreDNS в Kubernetes: исправляем CrashLoopBackOff, SERVFAIL и DNS-петлю

Разберём, почему CoreDNS перезапускается или отвечает SERVFAIL, как обнаружить замкнутую пересылку DNS-запросов, проверить доступн ...
Apache Kafka KRaft на VDS: отдельные controller и broker

Apache Kafka KRaft на VDS: отдельные controller и broker

Соберём новый Kafka-кластер без ZooKeeper на шести Linux VDS: три узла controller образуют отказоустойчивый metadata quorum, а три ...