MAATRIX / Блог / Authelia на сервере: частые ошибки и решения

Authelia на сервере: частые ошибки и решения

MAATRIX

Authelia — лёгкий forward-auth сервер: он не проксирует трафик сам, а только отвечает "да/нет" на вопрос "пускать ли этого пользователя дальше", который ему задаёт Nginx или Traefik. Именно поэтому большинство проблем с ней — не баги самой Authelia, а нестыковки в связке с реверс-прокси: не тот cookie domain, не те заголовки, не синхронизированное время. Разберём конкретные симптомы и что с ними делать.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →

Как Authelia работает с Nginx и Traefik

Прежде чем чинить, держите в голове схему. Клиент стучится в защищённый сервис (app.example.com), реверс-прокси перед ответом спрашивает у Authelia внутренним запросом: "у пользователя есть валидная сессия?". Authelia смотрит на cookie, сверяет её с хранилищем сессий и возвращает либо 200 OK с заголовками (Remote-User, Remote-Groups, Remote-Email), либо 401, из-за которого прокси делает редирект на страницу логина.

Три вещи должны совпадать между собой, иначе всё разваливается: domain в session.domain (родительский для всех защищённых поддоменов), access control rules (какой поддомен под какой политикой) и заголовки/middleware на стороне прокси (как именно он спрашивает Authelia и что делает с ответом).

Если реверс-прокси ещё не поднят, сначала разберитесь с ним отдельно, а потом накладывайте Authelia сверху.

Бесконечный редирект на страницу логина после успешного входа

Самая частая жалоба: вы вводите логин и пароль, проходите TOTP, а браузер снова кидает на /. Причина почти всегда одна — cookie от Authelia не видна защищаемому сервису.

Authelia ставит cookie сессии на домен, указанный в session.domain. Он обязан быть родительским для *всех* доменов, которые вы защищаете, и не может совпадать один в один с доменом самой Authelia:

session:
  name: authelia_session
  domain: example.com          # не auth.example.com!
  same_site: lax
  expiration: 1h
  inactivity: 5m
  remember_me: 1M

Если Authelia висит на auth.example.com, а защищённый сервис на app.example.com, cookie с domain: auth.example.com в запросах к app.example.com просто не отправится. Ставьте example.com — тогда cookie валидна для всех поддоменов.

Второй вариант той же болезни — разные схемы (http/https) на пути редиректа. Cookie с флагом Secure (Authelia ставит его автоматически при https) не уйдёт по обычному http, поэтому если часть трафика внутри docker-сети ходит по http, а снаружи https, петля почти гарантирована. Проверяйте на реальном внешнем домене, а не через curl -k на localhost — там симптом не воспроизведётся.

Третья причина, которую легко упустить — рассинхрон времени сервера влияет и на TTL cookie: если часы убежали вперёд, Authelia может считать свежую сессию уже истёкшей. Подробнее — в разделе про TOTP ниже.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

401/403 при интеграции с Traefik: ошибки в middleware и labels

С Traefik типичная ошибка — middleware forwardAuth описан, но не подключён к нужному роутеру, либо не проброшены заголовки ответа обратно клиенту.

Рабочий минимальный набор labels для сервиса за Traefik:

services:
  authelia:
    image: authelia/authelia:4.38
    networks: [proxy]
    volumes: ["./config:/config"]
    labels:
      - "traefik.http.routers.authelia.rule=Host(`auth.example.com`)"
      - "traefik.http.services.authelia.loadbalancer.server.port=9091"
      - "traefik.http.middlewares.authelia.forwardauth.address=http://authelia:9091/api/verify?rd=https://auth.example.com"
      - "traefik.http.middlewares.authelia.forwardauth.trustForwardHeader=true"
      - "traefik.http.middlewares.authelia.forwardauth.authResponseHeaders=Remote-User,Remote-Groups,Remote-Name,Remote-Email"

  app:
    image: your/app
    networks: [proxy]
    labels:
      - "traefik.http.routers.app.rule=Host(`app.example.com`)"
      - "traefik.http.routers.app.middlewares=authelia@docker"

Что обычно упускают:

  1. trustForwardHeader=true — без него Traefik не передаёт исходные заголовки в X-Forwarded-*, и Authelia не может понять, к какому домену идёт запрос, поэтому решает не в вашу пользу.
  2. authResponseHeaders — без явного списка Traefik не пробрасывает Remote-User дальше в приложение, из-за чего сервисы, которые сами читают эти заголовки для SSO (например, Grafana с auth.proxy), видят анонимного пользователя, даже если Authelia пропустила запрос.
  3. Middleware не привязан к роутеру приложения — легко забыть строку traefik.http.routers.app.middlewares=authelia@docker, тогда Traefik вообще не спрашивает Authelia, и сервис открыт всем. Проверяйте через dashboard Traefik (/api/http/routers), привязан ли middleware к нужному роутеру.

Если 401 приходит сразу на попытку логина — проверьте, что роутер самой Authelia не защищён этим же middleware (частая опечатка copy-paste). Общие проблемы конфигурации Traefik отдельно от Authelia разобраны в статье про частые ошибки Traefik на сервере.

Nginx: auth_request отдаёт 500/502 вместо перенаправления на логин

С Nginx вместо labels используется модуль auth_requestnginx из репозиториев Ubuntu и в nginx:alpine он собран по умолчанию, но в кастомных минимальных сборках его иногда вырезают — проверьте nginx -V 2>&1 | grep -o with-http_auth_request_module).

Рабочая связка — два конфиг-сниппета, подключаемые в каждый защищённый server:

# snippets/authelia-location.conf
location /authelia {
    internal;
    proxy_pass http://authelia:9091/api/verify;
    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-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-Host $http_host;
    proxy_set_header X-Forwarded-Uri $request_uri;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

# snippets/authelia-authrequest.conf
auth_request /authelia;
auth_request_set $target_url $scheme://$http_host$request_uri;
error_page 401 =302 https://auth.example.com/?rd=$target_url;
auth_request_set $user $upstream_http_remote_user;
proxy_set_header Remote-User $user;

Подключение в server приложения: include snippets/authelia-location.conf;, а в защищённой location / { include snippets/authelia-authrequest.conf; proxy_pass http://app-backend; }.

Типичные причины 500/502 именно на этом узле:

  • Authelia недоступна изнутри docker-сети — если Nginx не в той же сети, что и контейнер Authelia, proxy_pass http://authelia:9091 резолвится в никуда. Проверяйте docker exec nginx getent hosts authelia.
  • Тело запроса передаётся в /authelia — без proxy_pass_request_body off и обнулённого Content-Length большие POST-запросы (загрузка файлов) иногда рвут внутренний auth-запрос с 502, потому что Authelia не ожидает тело на /api/verify.
  • error_page 401 без =302 — если написать просто error_page 401 https://auth.example.com/;, Nginx отдаст статус 401 с телом страницы логина вместо редиректа, и браузер это не подхватит как переход.

Общие проблемы auth_request и reverse proxy на Nginx (504, не видит правки конфига) разобраны отдельно в материале про частые ошибки Nginx как reverse proxy — свериться стоит, если проблема не специфична для Authelia.

Сессии слетают: Redis, session secret и перезапуски

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

Для продакшена сессии нужно выносить в Redis:

session:
  redis:
    host: redis
    port: 6379
    # password: опционально, если Redis защищён паролем

Отдельная и более коварная проблема — secret для сессий и JWT должен быть зафиксирован явно и не меняться между перезапусками. Если он генерируется на лету или задан через переменную окружения, которая пересоздаётся при каждом деплое (например, случайное значение в CI), Authelia при рестарте будет считать все существующие cookie невалидными — те же симптомы, что при отсутствии Redis, только маскируются под "иногда работает, иногда нет".

Храните такие секреты в файле, подключаемом через session.secret_file, storage.encryption_key_file, identity_validation.reset_password.jwt_secret_file, а не генерируйте заново при каждом билде — это касается и storage.encryption_key, потеря которого делает нечитаемой всю базу пользовательских данных (включая секреты TOTP).

Если параллельно с Authelia на сервере крутится ещё несколько сервисов в Docker Compose, свериться со списком типовых граблей продакшен-конфигурации можно в статье про частые ошибки Docker Compose для продакшена — там как раз про секреты, volumes и порядок запуска зависимостей вроде Redis.

TOTP не проходит проверку, хотя код точно верный

Если пользователь вводит код из приложения-аутентификатора (Google Authenticator, Aegis, 1Password) и стабильно получает "неверный код" — в 9 случаях из 10 дело не в Authelia, а в рассинхронизации системного времени сервера. TOTP-коды валидны короткое окно (обычно 30 секунд ± допуск), и если часы сервера убежали хотя бы на минуту, все коды будут отклоняться, независимо от того, насколько верно они введены.

Проверка и фикс на Ubuntu/Debian:

timedatectl status   # смотрите "System clock synchronized: yes"
sudo apt install systemd-timesyncd -y
sudo timedatectl set-ntp true && sudo systemctl restart systemd-timesyncd

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

Вторая причина — пересоздание базы пользователей (storage) без миграции: секреты TOTP теряются, и пользователям нужно заново сканировать QR-код в Settings → Security → One-Time Password.

Третья, менее очевидная — повторная попытка с тем же кодом. Authelia защищается от replay-атак и не даёт использовать тот же TOTP-код дважды в пределах его окна действия. Если пользователь два раза быстро нажал "войти", вторая попытка закономерно отклонится — нужно дождаться следующего кода.

Access control rules: правило не срабатывает, хотя оно есть в конфиге

Правила в access_control.rules применяются сверху вниз, первое совпадение побеждает. Это главный источник путаницы: люди дописывают конкретное правило в конец списка, а общее правило выше него уже успело перехватить запрос.

Неправильно — общее правило стоит раньше частного и всегда срабатывает первым:

access_control:
  default_policy: deny
  rules:
    - domain: "*.example.com"
      policy: one_factor
    - domain: "admin.example.com"      # никогда не сработает,
      policy: two_factor                # правило выше уже отдало one_factor

Правильно — от частного к общему:

access_control:
  default_policy: deny
  rules:
    - domain: "admin.example.com"
      policy: two_factor
      subject:
        - "group:admins"
    - domain: "*.example.com"
      policy: one_factor

Ещё одна частая ошибка — забытый default_policy: deny в самом низу. Без него действует bypass, и любой домен, не попавший под явное правило, оказывается открытым без авторизации вообще — тихая дыра в безопасности, которую не видно, пока не проверишь вручную.

Если задача — не просто закрыть один сервис логином, а построить полноценный SSO с ролями и группами, стоит посмотреть в сторону Keycloak — установка описана в статье про настройку Keycloak на VPS. Authelia часто используют как лёгкий guard перед внутренними дашбордами, а Keycloak — как полноценный identity provider с OIDC/SAML.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

Authelia обязательно нужен Redis?

Нет, для одного инстанса можно жить с сессиями в памяти, но каждый рестарт контейнера тогда разлогинивает всех. Redis обязателен, если реплик Authelia несколько — иначе сессии одной реплики не увидит другая.

Можно ли использовать Authelia без Docker?

Да, есть standalone-бинарник и systemd unit с той же самой configuration.yml. Docker просто самый популярный способ из-за простоты подключения к сети с Nginx/Traefik.

Authelia защищает сам трафик приложения или только логин?

Только проверку доступа на входе. Дальше трафик идёт напрямую от прокси к приложению — это не VPN и не шифрование канала, а контроль "пускать/не пускать".

Забыл сбросить 2FA, а доступа через веб нет — что делать?

Зайдите по SSH и удалите привязанный TOTP-секрет пользователя прямо в базе (SQLite/PostgreSQL/MySQL), либо используйте команду authelia storage user identity generate-reset-password для ссылки на сброс через notifier.

Нужен ли отдельный сервер под Authelia?

Нет, это лёгкий Go-бинарник на десятки мегабайт памяти в простое — он прекрасно живёт в том же docker-compose стеке, что и остальные сервисы.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →