Двухфакторная аутентификация для Nginx: защита веб-сервисов с Authelia | AdminWiki
Timeweb Cloud — сервера, Kubernetes, S3, Terraform. Лучшие цены IaaS.
Попробовать

Двухфакторная аутентификация для Nginx: защита веб-сервисов с Authelia

19 мая 2026 11 мин. чтения
Содержание статьи

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

Почему Authelia и Nginx - оптимальная связка для защиты сервисов

Nginx как обратный прокси и балансировщик нагрузки выступает единой точкой входа для множества внутренних сервисов: панели управления TrueNAS или Proxmox, Grafana, API, порталы разработчиков. Без дополнительной защиты доступ к этим ресурсам часто ограничен только базовой аутентификацией или IP-адресами, что создает риски при компрометации пароля или необходимости доступа извне.

Authelia решает эту задачу, выступая централизованным шлюзом аутентификации. В отличие от простых решений вроде htpasswd, она поддерживает двухфакторную аутентификацию по TOTP (Time-based One-Time Password), резервные коды и детальные политики доступа. По сравнению с тяжеловесными системами вроде Keycloak, Authelia проще в развертывании и управлении, идеально подходит для защиты внутренних сервисов. Она работает как промежуточное ПО (middleware), интегрируясь с Nginx через стандартный модуль auth_request.

Архитектура защиты: как работает связка Nginx и Authelia

Поток запросов при включенной защите выглядит так:

  1. Клиент отправляет запрос к защищенному ресурсу (например, https://panel.example.com).
  2. Nginx перехватывает запрос и с помощью директивы auth_request отправляет внутренний подзапрос на эндпоинт аутентификации.
  3. Этот подзапрос проксируется на сервис Authelia (http://authelia:9091/api/verify).
  4. Authelia проверяет наличие валидной сессии в cookies запроса. Если сессии нет или она истекла, возвращает код 401.
  5. Nginx, получив 401, перенаправляет пользователя на страницу входа Authelia (https://auth.example.com).
  6. После успешного ввода логина, пароля и кода из приложения-аутентификатора, Authelia устанавливает сессионную cookie и возвращает код 200.
  7. Nginx, получив успешный ответ 200, проксирует исходный запрос к целевому backend-сервису (TrueNAS, Grafana и т.д.).

Модуль auth_request в Nginx выступает «проверяющим». Он не обрабатывает логику входа, а только запрашивает у внешнего сервиса (Authelia) вердикт: разрешить или запретить доступ. Это позволяет добавить защиту, минимально меняя конфигурацию существующих виртуальных хостов.

Authelia против других решений: краткое сравнение для Nginx

Для защиты Nginx существуют альтернативы, каждая со своей областью применения.

  • oauth2-proxy: Более простое решение для аутентификации через OAuth2-провайдеров (Google, GitHub). Подходит для быстрого добавления единого входа, но не поддерживает TOTP из коробки и имеет менее гибкие политики доступа.
  • Keycloak с Gatekeeper: Полноценная система идентификации и управления доступом (IAM). Обеспечивает Single Sign-On (SSO), поддержку протоколов OIDC, SAML. Чрезмерно сложна для сценария «просто добавить 2FA к нескольким панелям», требует значительных ресурсов.
  • Authelia: Занимает промежуточную позицию. Самодостаточное решение со встроенной двухфакторной аутентификацией, управлением пользователями через YAML-файл и детальными политиками доступа на основе пути, пользователя, группы и IP-адреса. Идеально для защиты внутренних сервисов в средах DevOps.

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

Подготовка среды: развертывание Authelia с Docker Compose

Стандартный и рекомендуемый способ запуска Authelia - в Docker-контейнерах. Это обеспечивает изоляцию, простоту обновления и переносимость конфигурации. Приведенный ниже docker-compose.yml и конфигурационные файлы образуют рабочую основу.

Готовый docker-compose.yml для запуска Authelia

Создайте директорию для проекта (например, /opt/authelia) и поместите в нее файл docker-compose.yml:

version: '3.8'

services:
  authelia:
    image: authelia/authelia:latest
    container_name: authelia
    volumes:
      - ./config:/config
      - ./users_database.yml:/config/users_database.yml:ro
    environment:
      - TZ=Europe/Moscow
    ports:
      - "9091:9091"
    restart: unless-stopped
    networks:
      - authelia-network

networks:
  authelia-network:
    driver: bridge

Этот файл определяет сервис Authelia, монтирует директорию с конфигурацией, пробрасывает порт 9091 наружу и создает изолированную сеть. Адаптируйте параметр TZ под свой часовой пояс. Для работы в одной сети с другими сервисами (например, Nginx в другом контейнере) используйте общую Docker-сеть вместо проброса портов.

Базовая конфигурация Authelia: config.yml и users_database.yml

Перед первым запуском необходимо создать и настроить конфигурационные файлы.

1. Создайте файл /opt/authelia/config/configuration.yml с минимальными настройками:

host: 0.0.0.0
port: 9091
log:
  level: info

jwt_secret: YOUR_SUPER_SECRET_JWT_KEY_HERE # Сгенерируйте случайную строку (openssl rand -hex 32)

default_redirection_url: https://auth.example.com

server:
  read_buffer_size: 4096
  write_buffer_size: 4096
  enable_pprof: false
  enable_expvars: false
  disable_keepalive: false

session:
  name: authelia_session
  secret: YOUR_SUPER_SECRET_SESSION_KEY_HERE # Другая случайная строка
  expiration: 1h
  inactivity: 5m
  domain: example.com # Ваш домен

storage:
  local:
    path: /config/db.sqlite3

notifier:
  disable_startup_check: false

authentication_backend:
  file:
    path: /config/users_database.yml
    password:
      algorithm: argon2id
      iterations: 1
      salt_length: 16
      parallelism: 8
      memory: 128

access_control:
  default_policy: deny
  rules:
    - domain:
        - "auth.example.com"
      policy: bypass

    - domain:
        - "panel.example.com"
      policy: two_factor
      subject: "group:admins"

Замените YOUR_SUPER_SECRET_JWT_KEY_HERE и YOUR_SUPER_SECRET_SESSION_KEY_HERE на сгенерированные строки. Укажите свой домен в секциях session.domain и правилах доступа.

2. Создайте файл пользователей /opt/authelia/users_database.yml:

users:
  admin:
    displayname: "Administrator"
    password: "$argon2id$v=19$m=128,t=1,p=8$WXpHb2tNb1dEdzVUQW9NZw$5g7Z/7..." # Хеш пароля
    email: admin@example.com
    groups:
      - admins

Пароль хешируется утилитой authelia crypto hash generate или можно временно задать его в открытом виде, а после первого входа Authelia предложит сменить и сгенерирует хеш.

После создания файлов запустите Authelia: docker-compose up -d. Сервис будет доступен по адресу http://ваш_сервер:9091.

Интеграция с Nginx: два практических сценария

Интеграция Authelia с Nginx сводится к настройке модуля auth_request. Рассмотрим два наиболее частых сценария.

Сценарий 1: Добавление 2FA к существующему виртуальному хосту Nginx

Предположим, у вас уже работает виртуальный хост для панели управления TrueNAS по адресу panel.example.com. Чтобы добавить перед ней двухфакторную аутентификацию, нужно модифицировать его конфигурацию в /etc/nginx/sites-available/panel.example.com.

Добавьте следующие директивы в блок server:

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

    # ... ваши настройки SSL ...

    # Локация для внутренних запросов аутентификации к Authelia
    location = /auth_request {
        internal;
        proxy_pass http://127.0.0.1:9091/api/verify; # Адрес Authelia
        proxy_pass_request_body off;
        proxy_set_header Content-Length "";
        proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
        proxy_set_header X-Original-Method $request_method;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Host $http_host;
    }

    # Основная локация, защищенная auth_request
    location / {
        auth_request /auth_request;
        auth_request_set $user $upstream_http_remote_user;
        auth_request_set $groups $upstream_http_remote_groups;
        proxy_set_header Remote-User $user;
        proxy_set_header Remote-Groups $groups;

        error_page 401 =302 https://auth.example.com/?rd=$scheme://$http_host$request_uri;

        # Проксирование на backend (TrueNAS)
        proxy_pass http://192.168.1.100:80; # IP и порт TrueNAS
        # ... остальные proxy_set_header ...
    }
}

Ключевые изменения:

  • Создана внутренняя локация /auth_request, которая проксирует проверку на Authelia.
  • Директива auth_request /auth_request; в основной локации активирует проверку для всех запросов.
  • auth_request_set извлекает заголовки пользователя и групп из ответа Authelia и передает их в backend.
  • error_page 401 =302 ... перенаправляет неаутентифицированных пользователей на страницу входа Authelia.

После внесения изменений проверьте конфигурацию (nginx -t) и перезагрузите Nginx (systemctl reload nginx). Теперь доступ к панели будет требовать двухфакторную аутентификацию. Аналогичный подход используется для защиты веб-панелей TrueNAS и Proxmox.

Сценарий 2: Создание нового защищенного серверного блока с нуля

Если вы развертываете новый сервис (например, мониторинг Grafana) и сразу хотите его защитить, используйте готовую конфигурацию.

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

    ssl_certificate /etc/letsencrypt/live/grafana.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/grafana.example.com/privkey.pem;
    # ... остальные настройки SSL ...

    location = /auth_request {
        internal;
        proxy_pass http://authelia:9091/api/verify; # Используем имя сервиса в Docker-сети
        proxy_pass_request_body off;
        proxy_set_header Content-Length "";
        proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
        proxy_set_header X-Original-Method $request_method;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Host $http_host;
    }

    location / {
        auth_request /auth_request;
        auth_request_set $user $upstream_http_remote_user;
        proxy_set_header X-WEBAUTH-USER $user; # Grafana принимает пользователя в этом заголовке

        error_page 401 =302 https://auth.example.com/?rd=$scheme://$http_host$request_uri;

        proxy_pass http://grafana:3000; # Прокси на контейнер Grafana
        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;
    }
}

Эта конфигурация предполагает, что Nginx и Authelia работают в одной Docker-сети, поэтому используется имя сервиса authelia вместо IP-адреса. Обратите внимание на заголовок X-WEBAUTH-USER, который передает аутентифицированного пользователя в Grafana для автоматического входа.

Ключевые директивы Nginx: auth_request, proxy_pass и обработка ошибок

Понимание назначения директив критично для отладки.

  • auth_request: Активирует модуль аутентификации для location или server. В качестве аргумента принимает внутреннюю локацию, которая сделает подзапрос к сервису аутентификации.
  • auth_request_set: Извлекает данные из заголовков ответа сервиса аутентификации и сохраняет их в переменные Nginx. Например, $upstream_http_remote_user содержит значение заголовка Remote-User, который отправляет Authelia при успешной проверке.
  • proxy_pass в локации /auth_request: Должен вести на эндпоинт проверки Authelia (/api/verify). Убедитесь, что адрес и порт верны.
  • error_page 401: Определяет действие при получении кода 401 (Unauthorized) от Authelia. Чаще всего это редирект 302 на страницу входа Authelia с параметром rd (redirect), куда вернуть пользователя после успешного входа.
  • proxy_set_header: Корректная передача заголовков (особенно X-Forwarded-*) важна для работы Authelia и backend-сервисов, чтобы они видели реальные IP-адреса и протоколы.

Правильная настройка этих директив - залог отсутствия бесконечных редиректов и ошибок доступа.

Настройка политик доступа и резервных кодов в Authelia

После базовой интеграции важно настроить детальный контроль и обеспечить отказоустойчивость системы.

Политики доступа: как ограничить доступ по пути, пользователю и IP

Политики доступа в Authelia задаются в секции access_control.rules файла configuration.yml. Правила обрабатываются сверху вниз, первое совпавшее правило применяется.

access_control:
  default_policy: deny
  rules:
    - domain: "auth.example.com"
      policy: bypass # Страница входа Authelia доступна всем

    - domain: "panel.example.com"
      resources:
        - "^/api/health$"
      policy: bypass # Эндпоинт healthcheck доступен без аутентификации

    - domain: "panel.example.com"
      policy: two_factor
      subject: "group:admins" # Полный доступ только группе admins

    - domain: "panel.example.com"
      resources:
        - "^/public/"
      policy: one_factor # Доступ к публичному разделу по логину/паролю

    - domain: "internal.example.com"
      policy: two_factor
      networks:
        - "192.168.1.0/24" # Требовать 2FA только из внутренней сети

    - domain: "*.example.com"
      policy: deny # Запретить все для остальных поддоменов

Доступные политики: bypass (пропустить), one_factor (только пароль), two_factor (пароль + TOTP), deny (запретить). Правила могут комбинироваться по доменам (с поддержкой wildcard *), ресурсам (регулярные выражения), субъектам (пользователь или группа) и сетям.

Резервные TOTP-коды: создание и безопасное хранение

Резервные коды - это одноразовые коды, которые служат запасным методом входа при потере основного устройства с аутентификатором (телефона). В Authelia они генерируются локально в браузере пользователя при настройке двухфакторной аутентификации и никуда не передаются, что обеспечивает безопасность.

Процесс генерации:

  1. Пользователь входит в интерфейс Authelia (https://auth.example.com) под своей учетной записью.
  2. В разделе профиля переходит к настройке двухфакторной аутентификации.
  3. После сканирования QR-кода в приложении-аутентификаторе (Google Authenticator, Authy) Authelia отображает набор резервных кодов (обычно 10-16 штук).
  4. Каждый код можно использовать только один раз, после чего он становится недействительным.

Рекомендации по хранению, основанные на практике крупных сервисов (GitHub, Google):

  • Сохраните коды в нескольких физически разделенных безопасных местах: зашифрованный диск (VeraCrypt), аппаратный сейф, защищенный менеджер паролей.
  • Распечатайте коды на бумаге и храните в надежном месте, недоступном для посторонних.
  • Не храните коды в незашифрованных файлах на рабочей станции или в облаке.
  • При использовании кода вычеркивайте его из списка.

Формат кодов в Authelia обычно представляет собой строку из 16 цифр, разделенных на группы для удобства чтения (например, 1234-5678-9012-3456). Резервные коды - критически важный компонент любой системы 2FA, их наличие и безопасное хранение предотвращает полную потерю доступа к защищенным сервисам.

Отладка и решение типичных проблем

Даже с готовыми конфигурациями могут возникнуть проблемы. Вот чек-лист для диагностики.

Бесконечные редиректы и ошибки 401/500: анализ логов

Проблема: браузер попадает в цикл редиректов между защищенным ресурсом и страницей входа Authelia, либо получает ошибку 500.

Что проверять:

  1. Логи Authelia: docker logs authelia. Ищите ошибки в конфигурации, проблемы с подключением к базе данных (SQLite), неверные секреты (jwt_secret, session.secret). Ошибка «Invalid JWT» указывает на неверный jwt_secret.
  2. Логи Nginx: tail -f /var/log/nginx/error.log. Ищите ошибки проксирования (failed to resolve, connection refused) к Authelia. Убедитесь, что адрес в proxy_pass локации /auth_request корректен и сервис доступен.
  3. Коды ответа: Включите в конфиг Nginx логирование кодов ответа от Authelia, добавив в локацию /auth_request: proxy_intercept_errors on; и просматривая access.log.
  4. Политики доступа: Убедитесь, что для домена защищенного ресурса в Authelia задано правило с политикой one_factor или two_factor, а не deny. Правило bypass для домена аутентификации (auth.example.com) должно быть первым.

Частая причина ошибок 500 - несовпадение секретов (jwt_secret) между экземплярами Authelia при обновлении или редеплое. Сгенерируйте новые секреты и обновите их во всех конфигурациях.

Конфликты с базовой аутентификацией и другими методами

Проблема: на backend-сервисе (например, старом веб-интерфейсе) уже настроена базовая HTTP-аутентификация (basic auth). Nginx, получив успешный ответ от Authelia, проксирует запрос к backend, который запрашивает свои учетные данные, создавая второй уровень аутентификации или ошибку 401.

Решение:

  • Предпочтительный способ: Отключить базовую аутентификацию в backend-сервисе, если это возможно. Защита теперь обеспечивается Authelia.
  • Альтернатива: Настроить Nginx на передачу заголовков базовой аутентификации через Authelia. Это сложный сценарий, требующий хранения паролей в двух системах. Добавьте в конфигурацию Nginx в локацию /auth_request передачу заголовка Authorization: proxy_set_header Authorization $http_authorization;. Однако Authelia не будет проверять эти учетные данные.

В большинстве случаев достаточно отключить встроенную аутентификацию защищаемого сервиса, оставив Authelia единственным шлюзом. Это упрощает управление доступом и аудит.

Проблема: аутентификация в Authelia проходит успешно, но при переходе к защищенному ресурсу пользователь снова перенаправляется на страницу входа. Сессия не сохраняется.

Что проверять:

  1. Домен cookie в Authelia: В файле configuration.yml параметр session.domain должен быть установлен в корневой домен (например, example.com), а не в поддомен. Cookie с доменом .example.com будут передаваться во все поддомены.
  2. Флаги cookie: Убедитесь, что для продакшн-среды (при использовании HTTPS) в Authelia включены флаги secure и http_only для сессионных cookie (они включены по умолчанию при правильной настройке X-Forwarded-Proto).
  3. Передача заголовков: В локации /auth_request Nginx должен передавать заголовки Cookie к Authelia и принимать их обратно. Убедитесь, что нет директив, сбрасывающих или игнорирующих эти заголовки.
  4. Время жизни сессии: Проверьте настройки session.expiration (общее время жизни) и session.inactivity (время неактивности) в Authelia. Слишком короткие значения могут вызывать частые разлогинивания.
  5. Синхронизация времени: Убедитесь, что время на сервере с Authelia, Nginx и клиентском устройстве синхронизировано (используйте NTP). Расхождение во времени влияет на валидность TOTP-кодов и сессионных cookie.

Для глубокой диагностики проблем с аутентификацией, особенно ошибок 401 и 403, полезно изучить отдельное руководство по диагностике ошибок 401/403 в стеке Nginx, Docker и Kubernetes, где разобраны команды анализа логов и проверки заголовков.

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

Поделиться:
Сохранить гайд? В закладки браузера