Безопасность и Защита 12 мин чтения 2026-09-24

Двухфакторная аутентификация и единый вход для всех сервисов на VDS: связка Authelia и Nginx Forward Auth в Docker Compose

Веб-панели Portainer, Grafana, Uptime Kuma и MinIO на VDS часто становятся мишенью сканеров уязвимостей и брутфорс-ботов из-за отсутствия встроенной двухфакторной аутентификации. Подробное практическое руководство по установке легковесного сервера авторизации Authelia в Docker Compose: настройка Nginx auth_request (Forward Auth), единый вход (SSO), генерация криптографических секретов, привязка TOTP к Google Authenticator и изоляция внутренних сервисов от прямого доступа из интернета.

Инженерная редакция SysKit Проверено инженерами SysKit.ru

По мере роста серверной инфраструктуры на одном виртуальном сервере (VDS) неизбежно накапливается целый комплекс внутренних веб-интерфейсов и инструментов мониторинга. Системные инженеры и разработчики разворачивают панели мониторинга Grafana, статус-страницы Uptime Kuma, панели управления контейнерами Dockge / Portainer, консоли объектного хранилища MinIO, автоматизацию n8n или базы данных вроде pgAdmin.

Каждый из этих сервисов слушает отдельный сетевой порт и имеет собственную систему авторизации (либо не имеет её вовсе в бесплатной редакции). Это порождает серьезную уязвимость периметра: злоумышленники и автоматизированные сканеры круглосуточно сканируют открытые порты, выискивая 0-day уязвимости и брутфорся пароли администратора. Единственное фундаментальное решение этой проблемы — убрать все веб-панели за единый защитный барьер обратного прокси Nginx с двухфакторной аутентификацией (2FA) на базе открытого легковесного сервера Authelia.

1. Почему встроенная авторизация в веб-панелях опасна: анатомия уязвимостей и решение Forward Auth

Большинство системных администраторов защищают свои внутренние сервисы одним из трех классических способов, каждый из которых имеет критические недостатки:

Подход к защите Удобство работы Наличие 2FA (TOTP) Уровень безопасности
Встроенные логины приложений Низкое (10 разных паролей) Редко / только в Enterprise Низкий (уязвимости в коде сервиса)
HTTP Basic Auth (htpasswd) Среднее (всплывающее окно браузера) Отсутствует в принципе Средний (уязвим к перехвату)
Обязательный WireGuard / VPN Низкое (не открыть с чужого ПК/телефона) Зависит от VPN-сервера Высокий (периметр закрыт)
Nginx + Authelia Forward Auth Высокое (единый вход SSO) Обязательный TOTP / WebAuthn Максимальный (Zero-Trust)

Связка Nginx и Authelia реализует концепцию Forward Authentication (упреждающей проверки подлинности). Когда пользователь обращается к поддомену uptime.yourdomain.ru или grafana.yourdomain.ru, Nginx не передает запрос целевому контейнеру. Сначала он выполняет быстрый внутренний суб-запрос к Authelia. Если у пользователя нет валидной сессионной куки с подтвержденным 2FA-кодом, запрос мгновенно блокируется, а браузер перенаправляется на защищенный портал авторизации.

2. Как работает связка Authelia и Nginx: перехват запросов через auth_request

Архитектура Forward Auth базируется на модуле Nginx ngx_http_auth_request_module. Проверить наличие модуля в вашей установке Nginx можно стандартной командой:

nginx -V 2>&1 | grep -o with-http_auth_request_module

В дистрибутивах Ubuntu (22.04 / 24.04 LTS) и Debian модуль Auth Request включен в состав метапакета nginx-full и в большинство сборок, однако в облегченных версиях nginx-light или специфических сборках nginx-core он может быть опциональным. Поэтому обязательно выполните предварительную проверку:

nginx -V 2>&1 | grep -o with-http_auth_request_module

Если команда вернула пустоту, установите полный пакет веб-сервера со всеми стандартными модулями безопасности: sudo apt update && sudo apt install -y nginx-full.

  • 1. Входящий HTTP-запрос: Клиент обращается к целевому сервису, например https://grafana.example.com.
  • 2. Перехват директивой auth_request: Веб-сервер Nginx приостанавливает выполнение запроса и отправляет внутренний суб-запрос на стандартизированный эндпоинт авторизации Authelia http://127.0.0.1:9091/api/authz/auth-request (официальный современный эндпоинт Authz API для модуля Nginx ngx_http_auth_request_module; также Authelia сохраняет обратную совместимость с классическим legacy-эндпоинтом /api/verify), передавая оригинальные заголовки, HTTP-метод (X-Forwarded-Method), хост (X-Forwarded-Host), протокол (X-Forwarded-Proto) и сессионные cookies.
  • 3. Проверка сессии в памяти: Сервер Authelia проверяет сессионную куку. Если сессия активна и двухфакторная аутентификация пройдена, Authelia возвращает HTTP-статус 200 OK вместе с заголовками идентификации пользователя (Remote-User, Remote-Groups, Remote-Email).
  • 4. Пропуск трафика: Nginx видит статус 200 и направляет оригинальный запрос в контейнер приложения (proxy_pass).
  • 5. Перенаправление при отсутствии авторизации: Если сессия отсутствует или истекла, Authelia возвращает код 401 Unauthorized. Nginx перехватывает его локальным правилом error_page 401 = @authelia_redirect; и перенаправляет клиента на страницу входа с запросом 2FA-кода.

Почему именно Authelia, а не Keycloak или Authentik?

Популярные enterprise-системы вроде Keycloak (Java) или Authentik (Python/PostgreSQL/Redis) требуют от 2 до 4 ГБ оперативной памяти только под собственные служебные процессы. Authelia написана на Go, компилируется в один компактный статический бинарник и в боевом режиме потребляет всего 25–40 МБ RAM. Это идеальный выбор для серверов VDS начального и среднего уровня (1–4 ГБ RAM), где каждый мегабайт ресурсов должен работать на пользу приложений.

3. Подготовка VDS и генерация криптографических секретов для Authelia

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

  • JWT Secret: Ключ для подписи токенов подтверждения учетных записей и сброса паролей.
  • Session Secret: Ключ шифрования пользовательских сессионных кук в браузере.
  • Storage Encryption Key: Симметричный ключ AES для шифрования секретов двухфакторной аутентификации (TOTP seeds) в базе данных SQLite.

Подключаемся к серверу по SSH и создаем стандартизированную структуру каталогов. Обратите внимание: для безопасности файлы конфигурации и данные хранилища разделяются, а файл с переменными окружения размещается в корне проекта вне монтируемых каталогов:

# Создание рабочих каталогов стека Authelia
sudo mkdir -p /opt/authelia/config /opt/authelia/data /opt/authelia/redis-data /opt/authelia/backups
cd /opt/authelia

Сгенерируем надежные криптографические ключи с высокой энтропией. Мы рекомендуем использовать команду openssl rand -hex 32, которая генерирует строго 64 шестнадцатеричных символа (256 бит криптографической случайности). Шестнадцатеричный формат гарантированно не содержит специальных символов shell (+, /, =, $), исключая необходимость сложного экранирования в файлах .env и YAML, и не страдает от потери энтропии при фильтрации:

# Генерация четырех уникальных криптостойких секретов (строго 64 hex-символа)
echo "AUTHELIA_JWT_SECRET=$(openssl rand -hex 32)"
echo "AUTHELIA_SESSION_SECRET=$(openssl rand -hex 32)"
echo "AUTHELIA_STORAGE_ENCRYPTION_KEY=$(openssl rand -hex 32)"
echo "AUTHELIA_REDIS_PASSWORD=$(openssl rand -hex 32)"

Сохраняем полученные секреты в защищенный файл /opt/authelia/.env и ограничиваем права доступа только для суперпользователя root:

cat << 'EOF' | sudo tee /opt/authelia/.env
AUTHELIA_JWT_SECRET=ВАШ_СГЕНЕРИРОВАННЫЙ_JWT_SECRET
AUTHELIA_SESSION_SECRET=ВАШ_СГЕНЕРИРОВАННЫЙ_SESSION_SECRET
AUTHELIA_STORAGE_ENCRYPTION_KEY=ВАШ_СГЕНЕРИРОВАННЫЙ_STORAGE_KEY
AUTHELIA_REDIS_PASSWORD=ВАШ_СГЕНЕРИРОВАННЫЙ_REDIS_PASSWORD
EOF

# Защита файла секретов: чтение разрешено строго владельцу root
sudo chmod 600 /opt/authelia/.env

# Проверка целостности .env: убеждаемся в наличии всех четырех секретов (Hex 64 символа или Base64 от 43 символов)
grep -E '^AUTHELIA_(JWT_SECRET|SESSION_SECRET|STORAGE_ENCRYPTION_KEY|REDIS_PASSWORD)=[a-zA-Z0-9_\-+=/]{43,64}
# Результат выполнения команды должен быть строго равен 4. Если меньше — проверьте вставку секретов!

Изоляция секретов: почему .env нельзя класть в папку config

Частая ошибка начинающих администраторов — сохранять secrets.env прямо в каталоге ./config, который затем целиком монтируется в контейнер. При таком подходе любой процесс внутри контейнера Authelia (или злоумышленник в случае уязвимости) получает прямой доступ на чтение ключей шифрования с диска. Размещение файла .env в корне проекта с передачей через директиву env_file в Compose полностью изолирует секреты от файловой системы контейнера.

4. Развертывание Authelia в Docker Compose с изоляцией ресурсов и портов

Создадим файл декларации контейнеров docker-compose.yml в папке /opt/authelia. В современной спецификации Compose Specification (начиная с Docker Compose v2.0+ без необходимости перевода хоста в кластерный режим Docker Swarm) стандартом для ограничения ресурсов является блок deploy.resources.limits. В нем жестко лимитируются оперативная память (memory: 256M) и процессорное время (cpus: '1.0'), которые Docker Engine напрямую транслирует в механизмы ядра Linux cgroups v2 (поддерживаются в Ubuntu 22.04/24.04 LTS). Для максимальной универсальности и обратной совместимости со старыми парсерами мы также дублируем корневые директивы mem_limit и cpus. Без явных лимитов ресурсов всплеск нагрузки на VDS с 1–2 ГБ RAM может спровоцировать системный OOM Killer и аварийно остановить ключевые демоны сервера.

Перед запуском контейнеров создадим защищенную конфигурацию для сервиса кэширования сессий Redis. Вместо небезопасной передачи пароля в аргументах запуска командной строки (redis-server --requirepass), где он становится виден любому локальному пользователю через ps aux и файл /proc/<pid>/cmdline, мы подготовим изолированный конфигурационный файл /opt/authelia/redis/redis.conf с правами доступа 600:

# Создаем директорию конфигурации Redis и генерируем конфиг с паролем из .env
mkdir -p /opt/authelia/redis /opt/authelia/redis-data
cat << EOF > /opt/authelia/redis/redis.conf
# Конфигурация Redis для кэширования сессий Authelia
protected-mode yes
port 6379
timeout 0
tcp-keepalive 300
save 60 1
loglevel warning
requirepass $(grep '^AUTHELIA_REDIS_PASSWORD=' /opt/authelia/.env | cut -d '=' -f2-)
maxmemory 100mb
maxmemory-policy volatile-lru
EOF

# Ограничиваем права доступа к конфигурации с паролем
chmod 600 /opt/authelia/redis/redis.conf
chmod 700 /opt/authelia/redis-data

Обратите особое внимание на правила сетевой безопасности: порт сервиса 9091 привязывается строго к локальному сокету хоста 127.0.0.1, чтобы исключить обход фаервола UFW демоном Docker:

services:
  authelia:
    image: authelia/authelia:4.39.21
    container_name: authelia-auth
    restart: unless-stopped
    ports:
      # Публикация строго на интерфейс loopback хоста для подключения локального Nginx
      - "127.0.0.1:9091:9091"
    env_file:
      - .env
    volumes:
      # Конфигурация монтируется в режиме только для чтения (read-only)
      - ./config:/config:ro
      # База данных SQLite для хранения пользователей и 2FA-ключей
      - ./data:/data
    # Непривилегированный пользователь по стандарту CIS Docker Benchmark (UID/GID 1000)
    user: "1000:1000"
    environment:
      - TZ=Europe/Moscow
      - PUID=1000
      - PGID=1000
      - AUTHELIA_REDIS_PASSWORD=${AUTHELIA_REDIS_PASSWORD}
    # Ограничения ресурсов: поддержка Compose Specification (Compose v2) и cgroups v2
    deploy:
      resources:
        limits:
          cpus: '1.0'
          memory: 256M
    # Обратная совместимость для standalone-парсеров
    mem_limit: 256m
    cpus: 1.0
    security_opt:
      - no-new-privileges:true
    depends_on:
      redis:
        condition: service_healthy
    networks:
      - authelia-net
    healthcheck:
      # Официальная встроенная команда проверки состояния Authelia
      test: ["CMD", "authelia", "healthcheck"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 10s
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

  redis:
    image: redis:7.4-alpine
    container_name: authelia-redis
    restart: unless-stopped
    # Безопасный запуск через файл конфигурации: пароль НЕ передается аргументом командной строки
    command: ["redis-server", "/usr/local/etc/redis/redis.conf"]
    volumes:
      - ./redis-data:/data
      - ./redis/redis.conf:/usr/local/etc/redis/redis.conf:ro
    # Ограничение ресурсов памяти и процессора для предотвращения OOM
    deploy:
      resources:
        limits:
          cpus: '0.5'
          memory: 128M
    mem_limit: 128m
    cpus: 0.5
    stop_grace_period: 30s
    security_opt:
      - no-new-privileges:true
    networks:
      - authelia-net
    healthcheck:
      # Безопасная проверка: считывание пароля из смонтированного конфигурационного файла
      test: ["CMD-SHELL", "redis-cli -a "$$(grep '^requirepass' /usr/local/etc/redis/redis.conf | awk '{print $$2}')" ping | grep -q PONG || exit 1"]
      interval: 10s
      timeout: 3s
      retries: 3
      start_period: 5s
    logging:
      driver: "json-file"
      options:
        max-size: "5m"
        max-file: "2"

networks:
  authelia-net:
    driver: bridge

Совместимость версий, безопасность секретов Redis и управление ресурсами

Конфигурация актуализирована и протестирована на стабильном релизе Authelia v4.39.21 (минимально допустимая базовая версия — 4.39.20) в связке с Redis 7.4-alpine и Nginx 1.24/1.26 на Ubuntu 22.04/24.04 LTS. Рекомендуется регулярно отслеживать релизы безопасности в официальном репозитории Authelia.

Обратите внимание на принципиальные улучшения безопасности Redis в данной конфигурации:

  • Исключение пароля из ps aux и /proc: Мы полностью отказались от флага --requirepass в команде запуска. Пароль считывается процессом redis-server из смонтированного файла /usr/local/etc/redis/redis.conf с правами 600. В выводе ps aux виден только запуск демона без секретов.
  • Ресурсные лимиты Compose Specification: Директивы deploy.resources.limits штатно поддерживаются плагином Docker Compose v2 без Swarm, изолируя потребление CPU и RAM через cgroups v2 и предотвращая падение хоста при атаках типа Slowloris или переполнении кэша.

Сетевая архитектура: Nginx на хосте vs All-in-Docker

В руководстве подробно рассмотрены две популярные архитектурные модели, и выбор зависит от окружения вашего сервера:

  • Модель 1 (Гибридная: Nginx на хосте + Docker): Рекомендуется, если на вашем сервере VDS уже установлен системный веб-сервер Nginx, обслуживающий нативные сайты, PHP-FPM или Certbot. Nginx слушает порты 80/443 и делает локальный суб-запрос на http://127.0.0.1:9091/api/authz/auth-request. Внутри Docker-сети authelia-net Authelia обращается к Redis по внутреннему имени host: 'redis'. Самому веб-серверу Nginx прямой доступ к Redis не нужен — управление сессиями полностью инкапсулировано внутри Authelia.
  • Модель 2 (All-in-Docker: Nginx в контейнере): Если на VDS все службы работают исключительно в Docker, запустите Nginx как отдельный контейнер (например, nginx:alpine). В этом сценарии создайте общую внешнюю сеть docker network create web-proxy, подключите к ней контейнеры nginx и authelia-auth, а в конфиге Nginx укажите proxy_pass http://authelia-auth:9091 по имени контейнера. При этом Redis по-прежнему остается в изолированной сети authelia-net без прямого доступа извне.

Безопасность переменных окружения: сокет Docker и защита от раскрытия секретов

Размещение файла .env с правами 600 на сервере изолирует секреты от обычных непривилегированных пользователей Linux. Однако помните: любой процесс или учетная запись с доступом к сокету /var/run/docker.sock (член группы docker) может извлечь переменные окружения командами docker inspect authelia-auth или docker compose config.

Членство в группе docker на сервере эквивалентно полным правам root. В корпоративных многопользовательских инфраструктурах ограничивайте круг лиц с доступом к демону Docker, а для передачи секретов без переменных окружения используйте механизм Docker Secrets или внешнее хранилище HashiCorp Vault.

Защита порта 9091 от внешнего сканирования

Внутри контейнера сервис слушает сокет tcp://0.0.0.0:9091/ (что необходимо для приема трафика из виртуального моста Docker). Однако директива 127.0.0.1:9091:9091 привязывает порт строго к интерфейсу локальной петли (loopback) хоста, делая порт полностью невидимым для внешних сетевых адаптеров VDS. Если случайно указать 9091:9091, демон Docker автоматически создаст правила перенаправления в цепочке iptables PREROUTING в обход системного брандмауэра UFW, сделав внутренний API авторизации доступным для всего интернета.

5. Надежные VDS-провайдеры для размещения контура безопасности и авторизации

Поскольку шлюз Authelia участвует в обработке каждого входящего HTTP-запроса к вашим веб-панелям, для него критически важна стабильность гипервизора и минимальное время отклика дисковой подсистемы NVMe при чтении сессий SQLite.

Инженерные критерии отбора VDS и партнерская прозрачность

Контур аутентификации Forward Auth критически зависит от скорости дискового I/O и памяти гипервизора:

  • Задержка fsync для SQLite WAL: При каждом чекпоинте транзакций PRAGMA wal_checkpoint(TRUNCATE); время синхронизации страниц должно составлять ≤ 0.5 мс при случайном чтении/записи 4K от 10 000 IOPS, чтобы исключить задержки на суб-запросах Nginx.
  • Низкая сетевая задержка кэша Redis: Время отклика loopback/bridge сокетов Redis на процессорах AMD EPYC / Intel Xeon Gold составляет ≤ 0.2 мс, что дает практически мгновенную проверку кук.

Примечание редакции SysKit: Рекомендованные ниже серверы протестированы нашими инженерами по этим метрикам. Ссылки являются партнерскими (реферальными), что позволяет поддерживать платформу и публиковать открытые руководства без пейволлов.

Рекомендуемые VDS-провайдеры

Отказоустойчивые сервера с быстрыми NVMe и каналом до 1 Гбит/с

NVMe / Anti-DDoS
⚡

Timeweb Cloud

Надежная инфраструктурная площадка для шлюзов авторизации Authelia и обратного прокси Nginx: дата-центры в РФ с сетевой задержкой до 5–15 мс, ультрабыстрые NVMe-диски и аппаратная фильтрация L3/L4 атак.

Конфигурация
от 1 vCPU / 1–2 GB RAM / 20–30 GB NVMe
Tier III / SLA 99.98%
🔷

Selectel

Инфраструктура корпоративного уровня Tier III с гарантированной доступностью 99.98% и защитой от сетевых атак. Аппаратная KVM-виртуализация для критических сервисов единого входа и шлюзов Nginx.

Конфигурация
от 1–2 vCPU / 2–4 GB RAM / 30 GB NVMe
KVM / Авто-бэкапы
🟠

Beget

Стабильные VDS с быстрым развертыванием дистрибутива Ubuntu 24.04 LTS, выделенным статическим IPv4, автоматическими ежедневными бэкапами и круглосуточной поддержкой инженеров 24/7.

Конфигурация
от 1 vCPU / 1 GB RAM / 20 GB NVMe

6. Настройка конфигурации configuration.yml: Access Control, сессии и двухфакторная аутентификация TOTP

Создадим основной конфигурационный файл Authelia /opt/authelia/config/configuration.yml. В нем описываются правила доступа, доменная зона cookies и параметры шифрования:

# Серверная конфигурация прослушивания
server:
  address: 'tcp://0.0.0.0:9091/'
  buffers:
    read: 4096
    write: 4096
  # Доверенные адреса обратного прокси для корректного чтения X-Forwarded-For
  trusted_proxies:
    - '127.0.0.1/32'     # Локальный loopback интерфейс
    - '::1/128'          # Локальный IPv6 loopback
    - '172.17.0.1/32'    # Шлюз Docker bridge по умолчанию (docker-proxy на хосте)
    - '172.20.0.1/32'    # Шлюз изолированной сети authelia-net
    - '172.20.0.0/16'    # Подсеть контейнеров Docker (для Модели 2 All-in-Docker)

# Настройки журналирования (info для продакшена, debug для отладки Nginx)
log:
  level: 'info'
  format: 'text'

# Единый домен для сессионных cookie
session:
  name: 'authelia_session'
  # Мастер-ключ шифрования сессий (передается из .env)
  secret: '${AUTHELIA_SESSION_SECRET}'
  same_site: 'lax'  # Официальный стандарт безопасности Authelia для единого домена
  inactivity: '1h'
  expiration: '12h'
  remember_me: '1 month'
  redis:
    host: 'redis'
    port: 6379
    password: '${AUTHELIA_REDIS_PASSWORD}'
    database_index: 0
  cookies:
    - domain: 'yourdomain.ru'
      authelia_url: 'https://auth.yourdomain.ru'
      default_redirection_url: 'https://yourdomain.ru'

# Хранилище сессий и двухфакторных ключей (SQLite в отдельном томе данных)
storage:
  local:
    path: '/data/db.sqlite3'
  encryption_key: '${AUTHELIA_STORAGE_ENCRYPTION_KEY}'

# Провайдер уведомлений (строго обязателен в Authelia 4.38+ для валидации конфигурации и Identity Verification)
# Файл размещен в /data/notification.txt, так как каталог /config смонтирован в режиме read-only
notifier:
  disable_startup_check: false
  filesystem:
    filename: '/data/notification.txt'

# Метод двухфакторной аутентификации по умолчанию
default_2fa_method: 'totp'

# Провайдер двухфакторной аутентификации (TOTP с универсальной совместимостью)
totp:
  issuer: 'SysKit Security (yourdomain.ru)'
  algorithm: 'sha1'  # Официальный стандарт совместимости со всеми мобильными приложениями
  digits: 6
  period: 30
  skew: 1
  secret_size: 32

# Провайдер пользователей (локальный YAML-файл)
authentication_backend:
  file:
    path: '/config/users_database.yml'
    watch: true
    password:
      algorithm: 'argon2id'
      iterations: 3
      memory: 65536
      parallelism: 4
      key_length: 32
      salt_length: 16

# Защита от брутфорс-атак и перебора паролей
regulation:
  max_retries: 5
  find_time: '2m'
  ban_time: '15m'

# Правила контроля доступа (Access Control Policies)
access_control:
  default_policy: 'deny'
  rules:
    # 1. Публичный доступ к порталу авторизации Authelia
    - domain: 'auth.yourdomain.ru'
      policy: 'bypass'

    # 2. Обязательная двухфакторная аутентификация (2FA) для всех внутренних панелей управления
    - domain:
        - 'uptime.yourdomain.ru'
        - 'grafana.yourdomain.ru'
        - 'dockge.yourdomain.ru'
        - 'minio.yourdomain.ru'
      policy: 'two_factor'

Правило привязки домена в секции session.cookies

Параметр domain: 'yourdomain.ru' задает доменную зону действия сессионного токена. Указывайте корневой домен второго уровня без поддоменов и без точки в начале (Authelia автоматически нормализует cookie для всех поддоменов: auth.yourdomain.ru, uptime.yourdomain.ru, grafana.yourdomain.ru). Параметр authelia_url должен строго указывать на полный HTTPS-адрес портала входа: https://auth.yourdomain.ru.

Критическая важность trusted_proxies: почему без 172.17.0.1/32 контур защиты откажет

В архитектурной модели «Nginx на хосте» локальный обратный прокси перенаправляет трафик на сокет 127.0.0.1:9091. При этом соединение проходит через механизм docker-proxy, и контейнер Authelia видит входящий TCP-пакет не с адреса 127.0.0.1, а с внутреннего IP-адреса шлюза виртуального Docker-моста (обычно 172.17.0.1 для docker0 или 172.20.0.1 для пользовательской bridge-сети authelia-net).

Если IP шлюза Docker bridge отсутствует в списке trusted_proxies:

  • Authelia считает входящий прокси недоверенным и полностью игнорирует заголовок X-Forwarded-For с реальным IP клиента.
  • Внутренний модуль защиты от брутфорса (regulation) фиксирует все попытки авторизации от лица общего IP шлюза (172.17.0.1).
  • После 5 ошибочных вводов пароля злоумышленником Authelia отправляет в бан адрес шлюза, что приводит к мгновенной блокировке доступа для всех добросовестных пользователей сервера сразу!

Явное добавление '172.17.0.1/32' и '172.20.0.1/32' в trusted_proxies полностью устраняет эту критическую проблему.

Политика SameSite для SSO и строгое требование флага Secure для SameSite=None

Политика same_site: 'lax' — официально рекомендованный разработчиками Authelia стандарт для единого домена:

  • Когда SameSite=Lax работает идеально: Портал входа (auth.yourdomain.ru) и защищаемые сервисы (grafana.yourdomain.ru, uptime.yourdomain.ru) находятся на поддоменах одного базового домена (eTLD+1 yourdomain.ru). Браузер классифицирует такие взаимодействия как Same-Site, поэтому при установке cookie с директивой domain: 'yourdomain.ru' сессия безопасно передается при любых переходах верхнего уровня (top-level GET-навигация и 302-редиректы), сохраняя строгую защиту от CSRF-атак.
  • Когда требуется SameSite=None: Если ваш кластер Authelia обслуживает сервисы на разных корневых доменах (например, auth.company.ru защищает dashboard.project.com), браузер считает переходы между ними сторонними (Cross-Site). В этом случае кука с политикой lax блокируется браузером. Для работы кросс-доменного SSO в configuration.yml создается отдельный блок в session.cookies для каждого домена либо задается same_site: 'none'. Обязательное условие стандарта IETF: Браузеры безусловно блокируют cookie с SameSite=None, если для нее не установлен флаг Secure (политика Reject insecure SameSite=None cookies). Authelia автоматически проставляет атрибут Secure только тогда, когда запрос пришел по защищенному протоколу HTTPS. Поэтому Nginx в суб-запросе auth_request обязан всегда передавать заголовок proxy_set_header X-Forwarded-Proto $scheme; (значение https). В противном случае Authelia не выставит флаг Secure, и браузер без предупреждения заблокирует сессионную куку, вызвав вечный цикл перенаправлений на страницу входа.

Обязательность секции notifier в Authelia 4.38+ и защита от Read-only file system

В соответствии со схемой конфигурации Authelia (начиная с версий 4.38/4.39), наличие ровно одного провайдера уведомлений (filesystem или smtp) является строго обязательным условием для успешной валидации схемы при старте. Без блока notifier контейнер падает с ошибкой failed to initialize notification provider.

  • Почему именно /data/notification.txt: В нашем манифесте Docker Compose папка /config смонтирована в режиме только для чтения (:ro). Если указать стандартный путь /config/notification.txt, при попытке записи одноразовой ссылки верификации Authelia аварийно завершится с ошибкой Read-only file system. Файл необходимо размещать в томе /data/notification.txt, где разрешена запись.
  • Переключение на продакшен-почту (SMTP): Для автоматической отправки писем пользователям замените блок filesystem на ваш корпоративный SMTP-сервер (Яндекс 360, VK WorkSpace или Mailgun):
    notifier:
      smtp:
        host: 'smtp.yandex.ru'
        port: 465
        timeout: '5s'
        username: 'auth@yourdomain.ru'
        password: '${AUTHELIA_NOTIFIER_SMTP_PASSWORD}'
        sender: 'SysKit Security <auth@yourdomain.ru>'
        subject: '{title}'
        startup_check_address: 'test@yourdomain.ru'

Ограничение Public Suffix List (PSL) для домена cookie

Значение domain: 'yourdomain.ru' в секции session.cookies задает зону действия сессионного токена. Домен обязан быть вашим собственным регистрируемым доменным именем (eTLD+1). Если вы используете бесплатные сервисы динамического DNS (такие как duckdns.org, no-ip.com, nip.io) или доменные зоны из глобального реестра Public Suffix List, современные браузеры в целях безопасности категорически блокируют установку cookies на корневой суффикс провайдера. При работе с Dynamic DNS сессию необходимо привязывать к полному FQDN (например, myvds.duckdns.org), либо зарегистрировать собственный домен второго уровня.

Хранилище сессий и базы данных: SQLite vs Redis и PostgreSQL

По умолчанию Authelia хранит сессии в зашифрованной cookie и базу пользователей в локальном SQLite (/data/db.sqlite3). Для одиночного VDS связка SQLite (в режиме WAL) + кэш сессий в Redis является идеальным стандартом: она дает минимальные задержки дискового I/O без накладных расходов на сетевые сокеты баз данных.

Если же вы разворачиваете отказоустойчивый кластер (High Availability) с несколькими узлами Authelia за общим балансировщиком, хранилище storage переключается с SQLite на внешний PostgreSQL:

# Пример конфигурации PostgreSQL для High Availability кластера Authelia
storage:
  postgres:
    host: 'postgres'
    port: 5432
    database: 'authelia'
    username: 'authelia_user'
    password: '${AUTHELIA_POSTGRES_PASSWORD}'
    schema: 'public'
    ssl:
      mode: 'require'
  encryption_key: '${AUTHELIA_STORAGE_ENCRYPTION_KEY}'

При этом кэш сессий session.redis обеспечивает мгновенную синхронизацию активных пользовательских входов между всеми нодами Authelia в реальном времени.

Безопасность trusted_proxies: настройка для хоста и Docker-контейнеров

Параметр trusted_proxies указывает Authelia, от каких именно IP-адресов разрешено принимать заголовок X-Forwarded-For с реальным адресом клиента для работы защиты от перебора паролей (подсистема regulation).

Официальная документация Authelia категорически запрещает указывать широкие подсети (такие как 172.18.0.0/16 или 10.0.0.0/8), поскольку любой соседний контейнер сможет подделывать IP-адреса. Настраивайте доверенные прокси строго под вашу архитектуру:

  • Сценарий 1 (Nginx на хосте): указывайте строго локальные петлевые интерфейсы '127.0.0.1/32' и '::1/128'. Пакеты с этих адресов не могут быть отправлены извне хоста.
  • Сценарий 2 (All-in-Docker: Nginx в контейнере): назначьте контейнеру Nginx фиксированный статический IP в выделенной Docker-сети authelia-ingress (например, 172.20.0.2) и пропишите в trusted_proxies строго этот адрес: '172.20.0.2/32'. Если Nginx обращается к Authelia из контейнера, а в trusted_proxies указан лишь 127.0.0.1, Authelia проигнорирует заголовок X-Forwarded-For, и подсистема защиты от брутфорса не сможет корректно блокировать атакующих.

Совместимость алгоритмов TOTP: почему HMAC-SHA1 остается стандартом по умолчанию

В конфигурации Authelia мы рекомендуем использовать проверенный стандарт algorithm: 'sha1'.

В спецификации RFC 6238 генерация одноразовых кодов TOTP построена на криптографической конструкции HMAC (HMAC-SHA1). В отличие от проверки контрольных сумм файлов или цифровых подписей, HMAC фундаментально не подвержен коллизионным атакам, поскольку на каждом шаге хэшируется секретный ключ совместно с монотонно растущим счетчиком времени. Национальный институт стандартов и технологий (NIST) в руководстве SP 800-63B (Digital Identity Guidelines) допускает использование HMAC-SHA1 для одноразовых кодов TOTP в качестве фактора владения, поскольку криптографический секретный ключ нивелирует теоретические уязвимости хеш-функции к коллизиям при генерации 30-секундных OTP.

Ключевой фактор выбора — универсальная совместимость. Многие популярные мобильные приложения (включая классический Google Authenticator) исторически жестко ориентированы на SHA1 для 6-значных кодов и могут давать ошибку валидации при использовании QR-кодов с SHA256. Алгоритм sha256 имеет смысл включать только в изолированных корпоративных средах, где все сотрудники используют поддерживающие его клиенты (1Password, Bitwarden, 2FAS, Aegis).

Параметр skew: 1 задает допустимое отклонение системных часов на 1 временной шаг (период в 30 секунд). При валидации сервер Authelia проверяет код не только для текущего окна времени, но также для непосредственно предшествующего (t-1) и последующего (t+1) интервалов. Это обеспечивает суммарный допуск в ±30 секунд относительно текущего момента, компенсируя несинхронизированное время между мобильным устройством инженера и сервером NTP.

Добавим первого пользователя-администратора. Для генерации криптостойкого пароля с современным хэшированием Argon2id воспользуемся встроенной утилитой самого контейнера Authelia:

# Генерация хэша пароля Argon2id через контейнер Authelia
docker run --rm authelia/authelia:4.39.21 authelia crypto hash generate argon2 --password "ВашСложныйПароль"

Консоль выдаст строку вида $argon2id$v=19$m=65536,t=3,p=4$.... Создаем файл пользователей /opt/authelia/config/users_database.yml:

users:
  admin:
    displayname: 'Главный Администратор'
    password: '$argon2id$v=19$m=65536,t=3,p=4$ВАШ_ХЭШ_ПАРОЛЯ'
    email: 'admin@yourdomain.ru'
    groups:
      - admins
      - devops

Для соблюдения принципа наименьших привилегий (Least Privilege) в манифесте docker-compose.yml мы передали переменные PUID=1000 и PGID=1000. При старте стартовый скрипт entrypoint образа Authelia сбрасывает права суперпользователя и исполняет бинарник от непривилегированного пользователя authelia с UID 1000 / GID 1000. Чтобы процесс контейнера имел доступ к файлам, настроим права владения:

# Назначение владельца UID 1000 (пользователь контейнера Authelia)
sudo chown -R 1000:1000 /opt/authelia/config /opt/authelia/data
# Чтение и запись разрешены строго владельцу процесса контейнера
sudo chmod 600 /opt/authelia/config/users_database.yml /opt/authelia/config/configuration.yml

Запускаем стек в фоновом режиме:

# Запуск контейнера Authelia
cd /opt/authelia && docker compose up -d

# Проверка статуса работы и логов
docker compose ps
docker compose logs authelia

7. Интеграция с Nginx: универсальный сниппет аутентификации и защита целевых сервисов (Uptime Kuma, Grafana)

Чтобы исключить распространенную ошибку с глобальным перехватом кодов 401 и гарантировать изоляцию правил, создадим модульный включаемый файл (snippet) в Nginx: /etc/nginx/snippets/authelia-authrequest.conf:

# Внутренний эндпоинт проверки сессии Authelia (официальный Authz API v4.38+)
location = /authelia {
    internal;
    proxy_pass http://127.0.0.1:9091/api/authz/auth-request;

    # HTTP/1.1 и сброс заголовка Connection для сохранения keepalive соединений с шлюзом
    proxy_http_version 1.1;
    proxy_set_header Connection "";

    # Ограничение размера суб-запроса для защиты буферной памяти Nginx
    client_max_body_size 1m;

    # Таймауты суб-запроса: предотвращают 60-секундное зависание клиентских запросов при перезагрузке Authelia
    proxy_connect_timeout 2s;
    proxy_send_timeout 2s;
    proxy_read_timeout 3s;

    # Исключаем http_500 и http_502 при одиночном upstream, предотвращая лишние задержки и циклы
    proxy_next_upstream error timeout invalid_header;

    # Официальные стандартные заголовки Forward Auth для Nginx auth_request
    proxy_set_header X-Original-Method $request_method;
    proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
    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;
    proxy_set_header X-Forwarded-URI $request_uri;

    # Сброс тела запроса и обнуление Content-Length для экономии буфера
    proxy_pass_request_body off;
    proxy_set_header Content-Length "";
}

# Именованный локейшн для перенаправления неавторизованных запросов
location @authelia_redirect {
    return 302 https://auth.yourdomain.ru/?rd=$scheme://$http_host$request_uri;
}

Архитектура семейств Authz API: auth-request vs forward-auth и legacy verify

Начиная с версии Authelia v4.38+, подсистема авторизации разделена на специализированные реализации (Authz API):

  • /api/authz/auth-request (стратегия AuthRequest): Официальный стандарт для модуля Nginx ngx_http_auth_request_module и Caddy forward_auth. Принимает заголовки X-Original-Method, X-Original-URL, обеспечивая проверку правил Access Control в зависимости от HTTP-метода (GET vs POST).
  • /api/authz/forward-auth (стратегия ForwardAuth): Универсальный альтернативный эндпоинт (используется по умолчанию в Traefik ForwardAuth и некоторых кастомных сборках прокси). Полностью совместим со стандартным набором заголовков.
  • /api/verify (стратегия HeaderLegacy): Исторический эндпоинт первой волны Authelia. Сохранен для обратной совместимости со старыми конфигурациями, но в новых развертываниях уступает нативному /api/authz/auth-request.

Защита от открытого редиректа (Open Redirect) в параметре rd

В директиве @authelia_redirect в портал передается параметр ?rd=$scheme://$http_host$request_uri. Authelia валидирует целевой адрес перехода: редирект разрешен исключительно на домены, явно перечисленные в секции session.cookies[].domain.

Правило безопасности: Никогда не указывайте слишком широкие или публичные домены (например, домены бесплатных динамических DNS без суффикса). Всегда задавайте точный корпоративный домен организации (domain: 'yourdomain.ru') и определяйте резервный адрес default_redirection_url: 'https://yourdomain.ru'. Если злоумышленник попытается подставить фишинговый URL (?rd=https://evil.com), Authelia мгновенно отклонит запрос с кодом 403.

Зачем обнулять Content-Length при proxy_pass_request_body off?

При выполнении суб-запроса auth_request веб-серверу не требуется передавать в Authelia тело исходного запроса (POST-данные, файлы вложений). Директива proxy_pass_request_body off экономит сетевой буфер и процессорное время. Однако заголовок Content-Length исходного запроса при этом мог бы остаться прежним. Обнуление proxy_set_header Content-Length ""; критически важно: оно предотвращает зависание HTTP-парсера Authelia в ожидании несуществующего потока байтов.

Проверка поддержки IPv6 и настройка Real IP (Хост vs Docker)

Перед активацией виртуальных хостов Nginx обратите внимание на два ключевых параметра сетевого окружения:

  • Проверка поддержки IPv6: Проверьте активность стека командой:
    test -f /proc/net/if_inet6 && echo "IPv6 активен" || echo "IPv6 отключен в ядре"
    Критическое предупреждение о запуске Nginx при отсутствии IPv6: Если стек IPv6 отключен в ядре VDS (net.ipv6.conf.all.disable_ipv6 = 1), наличие активной директивы listen [::]:80; или listen [::]:443; вызовет фатальный сбой nginx: [emerg] socket() [::]:80 failed (97: Address family not supported by protocol). В результате веб-сервер Nginx аварийно завершит работу и не сможет запуститься. Поэтому во всех приведенных эталонных конфигурациях директивы IPv6 по умолчанию закомментированы. Раскомментируйте их только после успешной проверки поддержки IPv6 в системе командой ip -6 addr.
  • Директива set_real_ip_from в Docker: Если Nginx работает как Docker-контейнер (Модель 2), сокет 127.0.0.1 указывает на локальную петлю внутри контейнера. Чтобы Nginx корректно извлекал реальный IP пользователя из заголовка внешнего прокси или балансировщика, добавьте адрес шлюза Docker-моста: set_real_ip_from 172.20.0.1; (где 172.20.0.1 — шлюз вашей Compose-сети). Для Nginx на хосте оставляйте доверие 127.0.0.1.

Создадим виртуальный хост портала аутентификации /etc/nginx/sites-available/auth.conf. Здесь мы настраиваем проксирование запросов к внутреннему шлюзу Authelia, активируем строгий рейт-лимит на форму ввода пароля и внедряем заголовки безопасности (Content-Security-Policy, X-Frame-Options) против кликджекинга и XSS-атак.

Для строгого соблюдения синтаксиса Nginx директиву limit_req_zone необходимо размещать в контексте http. Создадим отдельный файл конфигурации /etc/nginx/conf.d/ratelimit.conf, что исключит конфликт дублирования зоны памяти при включении нескольких виртуальных хостов:

# /etc/nginx/conf.d/ratelimit.conf
# Ограничение частоты обращений к форме аутентификации Authelia (2 запроса в секунду на IP)
limit_req_zone $binary_remote_addr zone=auth_limit:10m rate=2r/s;
limit_req_status 429;

Теперь создадим файл виртуального хоста портала /etc/nginx/sites-available/auth.conf:

server {
    listen 80;
    # listen [::]:80; # Раскомментируйте, если IPv6 поддерживается ядром VDS
    server_name auth.yourdomain.ru;

    # Доверие локальному прокси для определения реального адреса клиента
    set_real_ip_from 127.0.0.1;
    set_real_ip_from ::1;
    real_ip_header X-Forwarded-For;
    real_ip_recursive on;

    # Защита от перебора паролей: всплеск (burst) до 5 запросов без искусственной задержки
    limit_req zone=auth_limit burst=5 nodelay;

    location / {
        proxy_pass http://127.0.0.1:9091;
        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_set_header X-Forwarded-Host $http_host;
        proxy_set_header X-Forwarded-Port $server_port;
        proxy_set_header X-Forwarded-Uri $request_uri;
        proxy_set_header X-Original-URL $scheme://$http_host$request_uri;

        # Заголовки безопасности и Content-Security-Policy для защиты формы ввода 2FA от XSS и кликджекинга
        add_header X-Frame-Options "DENY" always;
        add_header X-Content-Type-Options "nosniff" always;
        add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self';" always;
    }
}

А теперь настроим защиту целевого сервиса на практическом примере панели мониторинга Uptime Kuma, работающей на локальном сокете 127.0.0.1:3001. Для безопасной поддержки WebSocket соединений в Nginx используется условная директива map, обновляющая протокол только при наличии клиентского заголовка Upgrade. Создаем полный файл конфигурации /etc/nginx/sites-available/uptime.conf:

# Безопасное определение заголовка Connection для WebSockets и HTTP/1.1
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      "keep-alive";
}

server {
    listen 80;
    # listen [::]:80; # Раскомментируйте, если IPv6 поддерживается ядром VDS
    server_name uptime.yourdomain.ru;

    # Доверенные адреса для заголовка реального IP
    set_real_ip_from 127.0.0.1;
    set_real_ip_from ::1;
    real_ip_header X-Forwarded-For;
    real_ip_recursive on;

    # Подключение стандартизированного эндпоинта верификации Authelia
    include /etc/nginx/snippets/authelia-authrequest.conf;

    location / {
        # Активация упреждающей проверки через Authelia
        auth_request /authelia;

        # Перехват 401 Unauthorized и 403 Forbidden через единый именованный переход
        error_page 401 403 = @authelia_redirect;

        # Проброс заголовков авторизованного пользователя (включая Remote-Name) в приложение
        auth_request_set $user $upstream_http_remote_user;
        auth_request_set $name $upstream_http_remote_name;
        auth_request_set $groups $upstream_http_remote_groups;
        auth_request_set $email $upstream_http_remote_email;
        proxy_set_header Remote-User $user;
        proxy_set_header Remote-Name $name;
        proxy_set_header Remote-Groups $groups;
        proxy_set_header Remote-Email $email;

        # Заголовки безопасности
        add_header X-Frame-Options "DENY" always;
        add_header X-Content-Type-Options "nosniff" always;
        add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; connect-src 'self' wss:; img-src 'self' data: https:; font-src 'self'; base-uri 'self'; form-action 'self'; frame-ancestors 'none';" always;

        proxy_pass http://127.0.0.1:3001;
        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_set_header X-Forwarded-Host $http_host;

        # Безопасная поддержка WebSockets без разрыва keep-alive соединений
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;

        # Отключение буферизации для мгновенной доставки WebSocket-событий в реальном времени
        proxy_buffering off;
        proxy_cache off;
        proxy_read_timeout 86400s;
        proxy_send_timeout 86400s;
    }
}

Критическая безопасность Remote-User: защита от спуфинга и прямого доступа

Механизм доверенной аутентификации (Auth Proxy) в Grafana и других панелях доверяет заголовку Remote-User безоговорочно. Если допустить ошибку в сетевой изоляции, злоумышленник получит наивысшие привилегии в обход Authelia:

  • Привязка служб строго к 127.0.0.1: Все защищаемые сервисы (Uptime Kuma, Grafana, Portainer) ОБЯЗАНЫ публиковать свои порты исключительно на локальный loopback: 127.0.0.1:3001:3001. Публикация на 0.0.0.0:3001 или открытие порта в UFW позволит атакующему отправить прямой HTTP-запрос с поддельным заголовком Remote-User: admin прямо в приложение.
  • Очистка входящих заголовков в Nginx: Директива proxy_set_header Remote-User $user; перезаписывает переданный клиентом заголовок значением переменной из суб-запроса Authelia. Если запрос не прошел аутентификацию, переменная $user пуста, и клиентский заголовок аннулируется.
  • Фильтрация источников (whitelist) в приложении: В файле grafana.ini директива whitelist = 127.0.0.1, ::1 запрещает Grafana принимать заголовок Remote-User от любых источников, кроме локального процесса Nginx.

Как настроить сквозной вход (SSO) в Grafana через Remote-User

Многие приложения (например, Grafana) умеют автоматически авторизовывать пользователя по переданному заголовку Remote-User без повторного ввода пароля. Директива header_name = Remote-User передает основной идентификатор учетной записи (мапится на username), а параметр headers пробрасывает дополнительные атрибуты профиля. В конфигурационном файле /etc/grafana/grafana.ini активируйте секцию [auth.proxy]:

[auth.proxy]
enabled = true
# Основной заголовок аутентификации (мапится на имя учетной записи)
header_name = Remote-User
header_property = username
auto_sign_up = true
sync_ttl = 60
# Прием заголовков разрешен строго от локального обратного прокси Nginx
# Доверенный адрес прокси: 127.0.0.1 для Nginx на хосте, либо IP шлюза моста (172.17.0.1 / 172.20.0.1) при Nginx в Docker
whitelist = 127.0.0.1, ::1, 172.17.0.1
# Дополнительные поля профиля (Email, группы, отображаемое имя)
headers = Email:Remote-Email, Groups:Remote-Groups, Name:Remote-Name
# Ограничение автоматического создания аккаунтов строго корпоративным доменом
allowed_domains = yourdomain.ru

После перезапуска Grafana пользователь, успешно прошедший 2FA в Authelia, попадает в интерфейс Grafana под своей учетной записью моментально.

8. Выпуск SSL-сертификатов Let's Encrypt и безопасная настройка редиректа

Перед запуском Certbot обязательно проверьте, что DNS A-записи для всех настраиваемых поддоменов (auth.yourdomain.ru, uptime.yourdomain.ru, grafana.yourdomain.ru) уже успешно делегированы и указывают на внешний IP-адрес вашего сервера. Если записи еще не обновились в мировых кэшах DNS, сервер проверки Let's Encrypt вернет ошибку валидации:

Чтобы исключить проблему взаимной блокировки SSL (когда в конфигурации прописываются директивы сертификатов, которых физически еще нет на диске), активируем сайты на HTTP-порту 80, тестируем конфигурацию Nginx, а затем выпускаем сертификаты через Certbot с автоматическим редиректом:

# Создание символических ссылок в sites-enabled
sudo ln -s /etc/nginx/sites-available/auth.conf /etc/nginx/sites-enabled/
sudo ln -s /etc/nginx/sites-available/uptime.conf /etc/nginx/sites-enabled/

# Проверка корректности конфигурации Nginx
sudo nginx -t

# Применение конфигурации
sudo systemctl reload nginx

# Автоматический выпуск SSL-сертификатов и включение 301 редиректа на HTTPS
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d auth.yourdomain.ru -d uptime.yourdomain.ru --redirect

Утилита Certbot автоматически проверит права владения доменами по протоколу ACME, выпустит сертификаты Let's Encrypt, сконфигурирует защищенные блоки listen 443 ssl http2 и добавит постоянный редирект 301 с незащищенного HTTP. Ниже представлен эталонный итоговый вид конфигурации /etc/nginx/sites-available/auth.conf в режиме активного SSL:

# Эталонный продакшен-виртуальный хост с HTTPS и Let's Encrypt
server {
    listen 80;
    # listen [::]:80; # Раскомментируйте при наличии IPv6
    server_name auth.yourdomain.ru;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    # listen [::]:443 ssl http2; # Раскомментируйте при наличии IPv6
    server_name auth.yourdomain.ru;

    # Пути к сертификатам Let's Encrypt
    ssl_certificate /etc/letsencrypt/live/auth.yourdomain.ru/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/auth.yourdomain.ru/privkey.pem;

    # Современные безопасные параметры TLS 1.2 / TLS 1.3
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers off;
    ssl_session_timeout 1d;
    ssl_session_cache shared:SSL:10m;

    # HSTS заголовок для защиты от даунгрейда соединения
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

    # Доверенные адреса и определение реального IP клиента
    set_real_ip_from 127.0.0.1;
    set_real_ip_from ::1;
    real_ip_header X-Forwarded-For;
    real_ip_recursive on;

    # Ограничение частоты запросов к форме авторизации
    limit_req zone=auth_limit burst=5 nodelay;

    location / {
        proxy_pass http://127.0.0.1:9091;
        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_set_header X-Forwarded-Host $http_host;
        proxy_set_header X-Forwarded-Port $server_port;
        proxy_set_header X-Forwarded-Uri $request_uri;

        add_header X-Frame-Options "DENY" always;
        add_header X-Content-Type-Options "nosniff" always;
        add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; frame-ancestors 'none';" always;
    }
}

9. Первый вход администратора, привязка Google Authenticator и проверка работы 2FA

После выпуска сертификатов откройте в браузере защищаемый адрес https://uptime.yourdomain.ru. Происходит мгновенный процесс перехвата:

  1. Nginx перехватывает обращение к закрытому сервису и перенаправляет браузер на портал https://auth.yourdomain.ru/?rd=https%3A%2F%2Fuptime.yourdomain.ru%2F.
  2. Вводим логин admin и пароль учетной записи.
  3. Подтверждение личности (Identity Verification): Поскольку для пользователя еще не настроен второй фактор, Authelia запускает обязательную процедуру защиты от несанкционированной регистрации. На экране появится уведомление: «An email has been sent to confirm your identity».
  4. Получение ссылки подтверждения: Так как в конфигурации настроен провайдер filesystem, открываем файл нотификаций на сервере:
    # Просмотр ссылки верификации устройства из журнала уведомлений
    cat /opt/authelia/data/notification.txt
    Скопируйте сгенерированную одноразовую ссылку (вида https://auth.yourdomain.ru/one-time/...) и откройте ее в браузере. При использовании SMTP ссылка придет непосредственно на почтовый ящик администратора.
  5. Генерация и сканирование QR-кода: После перехода по ссылке верификации Authelia сгенерирует криптографический сид и отобразит на экране QR-код TOTP. Отсканируйте его в мобильном приложении Google Authenticator, Яндекс Ключ или 2FAS.
  6. Введите сгенерированный 6-значный одноразовый пароль и нажмите «Подтвердить».
  7. Authelia сохраняет зашифрованный сид в базу db.sqlite3, выставляет безопасную сессионную cookie на доменную зону .yourdomain.ru и мгновенно перенаправляет вас обратно на целевой сервис https://uptime.yourdomain.ru!

Теперь попробуйте открыть любой другой защищенный поддомен в этом же браузере (например https://grafana.yourdomain.ru). Благодаря механизму Single Sign-On (SSO) повторный ввод пароля и 2FA-кода уже не потребуется: Authelia подтвердит валидность сессии за 1 миллисекунду.

10. Эксплуатация контура: ротация секретов без простоя, бэкап SQLite/Redis и диагностика сбоев

Концепция эшелонированной обороны (Defense-in-Depth)

Шлюз Forward Auth гарантирует, что к целевым интерфейсам не сможет обратиться неаутентифицированный пользователь. Однако концепция Zero-Trust требует помнить: аутентификация не защищает от уязвимостей внутри самих приложений. Если атакующий завладеет скомпрометированной учетной записью одного из инженеров, уязвимости в коде бэкендов (SQL-инъекции, RCE, SSRF) могут позволить развить атаку вглубь сервера.

  • Изоляция контейнеров: Запускайте бэкенды без root-прав (security_opt: [no-new-privileges:true], cap_drop: [ALL]) и монтируйте корень файловой системы в режиме только для чтения (read_only: true).
  • Сетевой брандмауэр UFW: Никогда не открывайте сервисные порты (3001, 3000, 9091, 6379) в интернет. Единственные открытые порты сервера — это 80 и 443 для Nginx и нестандартный порт SSH.
  • Регулярные обновления безопасности: Настройте автоматические уведомления об уязвимостях и своевременно обновляйте образы защищаемых веб-панелей.

Регламент плановой ротации криптографических секретов без простоя сервиса

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

  • 1. Ротация токенов JWT и сессий (AUTHELIA_JWT_SECRET и AUTHELIA_SESSION_SECRET):

    Сгенерируйте новые 64-символьные HEX-значения: openssl rand -hex 32. Замените переменные в файле /opt/authelia/.env и выполните быстрое бесшовное обновление контейнера:

    docker compose -f /opt/authelia/docker-compose.yml up -d authelia

    Влияние на пользователей: Текущие активные сессионные cookies в браузерах мгновенно инвалидируются. Пользователям потребуется повторно ввести логин и 6-значный код TOTP при следующем переходе. Сервис остается полностью доступен, downtime равен 0.

  • 2. Ротация пароля базы кэша сессий (AUTHELIA_REDIS_PASSWORD):

    Сгенерируйте новый пароль. Обновите директиву requirepass в конфигурационном файле /opt/authelia/redis/redis.conf и значение AUTHELIA_REDIS_PASSWORD в /opt/authelia/.env. Примените изменения:

    # Перезапуск сервиса кэша и шлюза с новым паролем
    docker compose -f /opt/authelia/docker-compose.yml restart redis authelia
  • 3. Ротация мастер-ключа шифрования хранилища (AUTHELIA_STORAGE_ENCRYPTION_KEY):

    Этим ключом зашифрованы персональные TOTP-сиды пользователей в базе данных db.sqlite3. Внимание: простая замена ключа в .env приведет к фатальной ошибке дешифрации unable to decrypt и полной потере доступа пользователей к 2FA! Для безопасной онлайн-перешифровки базы новым ключом используйте официальную команду миграции Authelia:

    # Создаем страховой резервный снимок базы перед ротацией
    sqlite3 /opt/authelia/data/db.sqlite3 ".backup /opt/authelia/data/db.sqlite3.bak_$(date +%F)"
    
    # Генерируем новый 64-символьный ключ
    NEW_KEY=$(openssl rand -hex 32)
    echo "Новый ключ: $NEW_KEY"
    
    # Выполняем автоматическую перешифровку хранилища
    docker run --rm -v /opt/authelia/config:/config -v /opt/authelia/data:/data \
        authelia/authelia:4.39.21 authelia storage encryption change-key \
        --config /config/configuration.yml \
        --new-encryption-key "$NEW_KEY"
    
    # После успешного ответа утилиты обновляем ключ в .env и перезапускаем шлюз
    sed -i "s/^AUTHELIA_STORAGE_ENCRYPTION_KEY=.*/AUTHELIA_STORAGE_ENCRYPTION_KEY=$NEW_KEY/" /opt/authelia/.env
    docker compose -f /opt/authelia/docker-compose.yml restart authelia

Автоматизированное резервное копирование SQLite (db.sqlite3) и Redis с защитой от сбоев WAL

База данных SQLite в Authelia функционирует в высокопроизводительном режиме упреждающего журнала транзакций WAL (Write-Ahead Logging). В этом режиме активные записи находятся в служебных файлах db.sqlite3-wal и db.sqlite3-shm. Простое копирование файла cp db.sqlite3 на работающем сервере категорически недопустимо — оно приводит к получению поврежденного, битого дампа (Corrupted Database Snapshot)!

Для создания целостных, транзакционно согласованных бэкапов создадим автоматизированный bash-скрипт с проверкой PRAGMA integrity_check, сжатием gzip и ротацией архивов за последние 14 дней в папке /opt/authelia/backups:

# Создаем каталог бэкапов с правами только для администратора
mkdir -p /opt/authelia/backups
chmod 700 /opt/authelia/backups

# Создаем скрипт горячего резервного копирования
cat << 'EOF' > /opt/authelia/backup-authelia.sh
#!/usr/bin/env bash
set -euo pipefail

BACKUP_DIR="/opt/authelia/backups"
TIMESTAMP=$(date +"%Y%m%d_%H%M%S")
DB_FILE="/opt/authelia/data/db.sqlite3"
BACKUP_DB="$BACKUP_DIR/authelia_db_$TIMESTAMP.sqlite3"
REDIS_BACKUP="$BACKUP_DIR/redis_dump_$TIMESTAMP.rdb"

# 1. Атомарный онлайн-бэкап базы SQLite с фиксацией WAL
if [ -f "$DB_FILE" ]; then
    sqlite3 "$DB_FILE" ".backup '$BACKUP_DB'"
    # Проверка целостности созданного бэкапа
    INTEGRITY=$(sqlite3 "$BACKUP_DB" "PRAGMA integrity_check;")
    if [ "$INTEGRITY" != "ok" ]; then
        echo "[ERROR] Сбой проверки целостности бэкапа SQLite: $INTEGRITY" >&2
        rm -f "$BACKUP_DB"
        exit 1
    fi
    gzip -9 "$BACKUP_DB"
fi

# 2. Сохранение снимка сессий Redis на диск
docker exec authelia-redis redis-cli -a "$(grep '^requirepass' /opt/authelia/redis/redis.conf | awk '{print $2}')" BGSAVE > /dev/null 2>&1 || true
sleep 2
if [ -f "/opt/authelia/redis-data/dump.rdb" ]; then
    cp /opt/authelia/redis-data/dump.rdb "$REDIS_BACKUP"
    gzip -9 "$REDIS_BACKUP"
fi

# 3. Сохранение зашифрованного архива конфигурации (.env, configuration.yml, redis.conf)
tar -czf "$BACKUP_DIR/authelia_config_$TIMESTAMP.tar.gz" -C /opt/authelia .env config redis

# 4. Ротация: удаление локальных бэкапов старше 14 дней
find "$BACKUP_DIR" -type f -mtime +14 -delete
echo "[$(date)] Резервное копирование Authelia успешно завершено."
EOF

# Выставляем права на исполнение только для root
chmod 700 /opt/authelia/backup-authelia.sh

Добавим запуск резервного копирования в планировщик cron для ежедневного выполнения в 03:30 ночи:

# Добавляем регламентное задание бэкапа в cron
echo "30 3 * * * root /opt/authelia/backup-authelia.sh >> /var/log/authelia-backup.log 2>&1" > /etc/cron.d/authelia-backup
chmod 644 /etc/cron.d/authelia-backup

Диагностика типовых сбоев и решение проблем (Troubleshooting)

При интеграции Forward Auth администраторы могут столкнуться с типовыми ситуациями. Разберем ключевые сценарии диагностики:

1. Бесконечный цикл редиректов (302 Redirect Loop)

Причина: Несовпадение доменного суффикса cookie в session.cookies[0].domain и URL в адресной строке браузера, либо указание протокола HTTP вместо HTTPS в параметре authelia_url.

Решение: Убедитесь, что в configuration.yml в параметре authelia_url указан точный адрес https://auth.yourdomain.ru, а в конфигурации Nginx портала авторизации передаются заголовки X-Forwarded-Proto $scheme, X-Forwarded-Host $http_host и X-Forwarded-Port $server_port.

2. Ложный редирект на авторизацию при внутренних ошибках бэкенда

Причина: Размещение директивы error_page 401 на верхнем уровне блока server, из-за чего внутренние ответы 401 от защищаемого приложения ошибочно перехватываются Nginx.

Решение: Изолировать директиву: error_page 401 403 = @authelia_redirect; объявляется строго внутри блока location /, где активен auth_request /authelia;.

3. Сбои WebSockets в Uptime Kuma или Grafana Live

Причина: Отсутствие директив обновления протокола до HTTP/1.1 и заголовков Upgrade в конфигурации проксирования защищаемого приложения.

Решение: Обязательно добавьте в location / директивы: proxy_http_version 1.1;, proxy_set_header Upgrade $http_upgrade; и proxy_set_header Connection "upgrade";.

4. Cookie блокируется браузером при использовании DuckDNS (Public Suffix List)

Причина: Сервисы динамического DNS (например, duckdns.org) внесены в глобальный Public Suffix List (PSL). Браузеры блокируют установку cookie со domain: duckdns.org в целях межпользовательской безопасности.

Решение: При использовании динамического DNS привязывайте cookie к вашему полному хосту (domain: 'myhost.duckdns.org') либо зарегистрируйте собственный домен второго уровня (eTLD+1).

5. Сброс авторизации администраторов при перезапуске Authelia

Причина: Хранение сессий исключительно в памяти или файле SQLite при отсутствии Redis.

Решение: В нашем стеке сессии вынесены в персистентный контейнер Redis (секция session.redis). Сессии сохраняются активными при любых перезапусках и обновлениях контейнера Authelia.

6. Ошибка 403 Forbidden для непривилегированных учетных записей

Причина: Пользователь успешно прошел 2FA, но не входит в группу, имеющую право доступа к целевому сервису по правилам access_control.rules.

Решение: Директива error_page 401 403 = @authelia_redirect; направляет пользователя на портал Authelia, где отображается информативная страница «Access Denied» с пояснением отсутствия прав, вместо пустой страницы ошибки Nginx.

11. Резервное копирование и итоговый чек-лист безопасности контура авторизации

В базе данных SQLite /opt/authelia/data/db.sqlite3 хранятся зарегистрированные 2FA-ключи (TOTP seeds) пользователей, а в файле /opt/authelia/.env — мастер-ключи шифрования. Если эти файлы будут утеряны при сбое диска, все пользователи потеряют доступ к серверам и потребуется сброс двухфакторной аутентификации вручную.

Настроим регулярное автоматическое резервное копирование через cron:

Безопасность ключей шифрования бэкапов: изоляция секрета

Хранение симметричного ключа .backup_key на том же сервере в каталоге /opt/authelia допустимо только как шаг для автоматизации создания локального архива перед немедленной отправкой в удаленное хранилище. Если злоумышленник получит root-доступ к серверу, он одновременно завладеет и базой данных, и ключом ее расшифровки.

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

Для защиты резервных копий от утечки мастер-ключей шифрования сгенерируем отдельный пароль архивации и сохраним его в защищенный файл /opt/authelia/.backup_key с правами 600:

# Генерация ключа шифрования резервных копий
openssl rand -hex 32 | sudo tee /opt/authelia/.backup_key >/dev/null
sudo chmod 600 /opt/authelia/.backup_key

Создаем надежный скрипт резервного копирования с WAL-чекпоинтом SQLite, верификацией целостности и симметричным шифрованием AES-256-CBC:

# Создание автоматического скрипта онлайн-бэкапа Authelia с шифрованием AES-256
cat << 'EOF' | sudo tee /opt/authelia/backup.sh
#!/bin/bash
set -euo pipefail

BACKUP_DIR="/opt/authelia/backups"
DATE=$(date +%Y%m%d_%H%M%S)
mkdir -p "$BACKUP_DIR"

# 1. Сброс незафиксированных транзакций из WAL-журнала в основной файл SQLite
sqlite3 /opt/authelia/data/db.sqlite3 "PRAGMA wal_checkpoint(TRUNCATE);" >/dev/null

# 2. Создание атомарного онлайн-снапшота (нативный API SQLite без остановки сервиса)
sqlite3 /opt/authelia/data/db.sqlite3 ".backup $BACKUP_DIR/authelia_db_$DATE.sqlite3"

# 3. Автоматическая верификация целостности созданного файла
sqlite3 "$BACKUP_DIR/authelia_db_$DATE.sqlite3" "PRAGMA integrity_check;" >/dev/null

# 4. Формирование единого архива конфигураций, .env ключей и базы данных
tar -czf "$BACKUP_DIR/authelia_snapshot_$DATE.tar.gz" \
    -C /opt/authelia .env config/ \
    -C "$BACKUP_DIR" "authelia_db_$DATE.sqlite3"

# 5. Симметричное шифрование архива алгоритмом AES-256-CBC с солью и PBKDF2
openssl enc -aes-256-cbc -salt -pbkdf2 \
    -pass file:/opt/authelia/.backup_key \
    -in "$BACKUP_DIR/authelia_snapshot_$DATE.tar.gz" \
    -out "$BACKUP_DIR/authelia_snapshot_$DATE.tar.gz.enc"

# 6. Удаление временных незашифрованных файлов
rm -f "$BACKUP_DIR/authelia_snapshot_$DATE.tar.gz" "$BACKUP_DIR/authelia_db_$DATE.sqlite3"

# 7. Ротация: хранение зашифрованных копий за последние 14 дней
find "$BACKUP_DIR" -type f -name "*.enc" -mtime +14 -delete
EOF

sudo chmod +x /opt/authelia/backup.sh

# Добавление задачи в системный crontab (ежедневно в 03:30 ночи)
(sudo crontab -l 2>/dev/null || true; echo "30 3 * * * /opt/authelia/backup.sh > /var/log/authelia_backup.log 2>&1") | sudo crontab -

Пошаговый регламент восстановления после аварии (Disaster Recovery)

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

# 1. Расшифровка последнего зашифрованного архива
openssl enc -d -aes-256-cbc -pbkdf2 \
    -pass file:/opt/authelia/.backup_key \
    -in /opt/authelia/backups/authelia_snapshot_ДАТА.tar.gz.enc \
    -out /tmp/authelia_restore.tar.gz

# 2. Распаковка архива во временный каталог
mkdir -p /tmp/authelia_restore
tar -xzf /tmp/authelia_restore.tar.gz -C /tmp/authelia_restore/

# 3. Остановка служб Authelia
cd /opt/authelia && docker compose down

# 4. Восстановление мастер-ключей, конфигураций и снапшота базы SQLite
sudo cp /tmp/authelia_restore/.env /opt/authelia/.env
sudo cp -r /tmp/authelia_restore/config/* /opt/authelia/config/
sudo cp /tmp/authelia_restore/authelia_db_*.sqlite3 /opt/authelia/data/db.sqlite3

# 5. Проверка целостности восстановленной базы данных
sqlite3 /opt/authelia/data/db.sqlite3 "PRAGMA integrity_check;"

# 6. Запуск контейнеров и удаление временных расшифрованных файлов
docker compose up -d
rm -rf /tmp/authelia_restore /tmp/authelia_restore.tar.gz

Сброс WAL-журнала и шифрование резервных копий AES-256

Команда PRAGMA wal_checkpoint(TRUNCATE); сбрасывает все незафиксированные транзакции из вспомогательного WAL-журнала (файлы db.sqlite3-wal) в основную базу данных до снятия снапшота. Резервная копия базы мгновенно верифицируется утилитой PRAGMA integrity_check;. Финальный архив шифруется криптографическим алгоритмом AES-256-CBC с PBKDF2: даже при компрометации файловой системы сервера злоумышленник не сможет извлечь хеши паролей, TOTP-секреты или файл .env без мастер-ключа .backup_key.

Разверните отказоустойчивый сервер авторизации на надежном VDS

Выберите производительный VDS с быстрым NVMe-накопителем, выделенным статическим IP-адресом и аппаратной защитой от сетевых атак для безопасного размещения панелей управления и критической IT-инфраструктуры.

Выбрать VDS для инфраструктуры в Timeweb Cloud

Итоговый чек-лист готовности контура безопасности Authelia + Nginx Forward Auth:

  • Контейнеры: Службы authelia-auth (256m RAM, образ 4.39.20) и authelia-redis (128m RAM) запущены с флагом no-new-privileges:true и находятся в статусе Healthy.
  • Сетевая изоляция: Порты 9091, 6379 и внутренние порты сервисов (3001) опубликованы строго на 127.0.0.1, исключая прямой обход Nginx и спуфинг заголовка Remote-User.
  • Секреты и .env: Все 4 мастер-токена сгенерированы утилитой openssl rand -hex 32, проверены командой валидации и сохранены в /opt/authelia/.env с правами 600.
  • Конфигурация SSO: Задана безопасная директива same_site: 'lax' для единого домена, алгоритм TOTP установлен в совместимый sha1, а в trusted_proxies указаны строго проверенные локальные интерфейсы.
  • Nginx сниппет: Подключен модульный файл /etc/nginx/snippets/authelia-authrequest.conf на современный эндпоинт /api/authz/auth-request с суб-локейшном @authelia_redirect.
  • Защита портала логина и сервисов: Настроен рейт-лимит rate=2r/s burst=5 (статус 429), заголовки X-Frame-Options: DENY и строгие директивы Content-Security-Policy.
  • HTTPS и SSL: Выпущены сертификаты Let's Encrypt, настроен постоянный 301-редирект на HTTPS и активирован безопасный стек TLS 1.2/1.3 с HSTS.
  • Резервное копирование и DR: Настроен автоматический cron-скрипт со сбросом WAL-журнала, шифрованием AES-256-CBC и протестированным регламентом восстановления.

Вопросы и ответы (FAQ)

Популярные вопросы вебмастеров и сисадминов по данной теме

В чем разница между эндпоинтами /api/verify и /api/authz/auth-request?
Начиная с версии Authelia 4.38, разработчики стандартизировали интерфейсы авторизации под различные типы обратных прокси: /api/authz/auth-request для модуля Nginx auth_request, /api/authz/forward-auth для Traefik/Caddy и /api/authz/ext-authz для Envoy. Эндпоинт /api/authz/auth-request анализирует метод оригинального запроса через заголовок X-Forwarded-Method и является официальным эталоном для новых установок на Authelia 4.39+. Классический эндпоинт /api/verify сохраняет полную обратную совместимость, поэтому существующие работающие конфигурации не требуют экстренной перенастройки.
В чем разница между политиками single_factor, two_factor и bypass в configuration.yml?
Политика bypass полностью отключает аутентификацию для указанного домена или пути (используется для самого портала auth.yourdomain.ru, открытых API или внешних вебхуков). Политика single_factor требует только ввода логина и мастер-пароля (удобно для второстепенных общедоступных разделов). Политика two_factor требует обязательного прохождения двух ступеней: ввода мастер-пароля и подтверждения 6-значным кодом из приложения TOTP (Google Authenticator) или аппаратного ключа WebAuthn/FIDO2. Для всех панелей управления администратора рекомендуется строго two_factor.
Почему сессионные cookies не сохраняются или блокируются браузером при использовании DuckDNS?
Популярные сервисы динамического DNS (например, duckdns.org) внесены в глобальный Public Suffix List (PSL). Современные браузеры блокируют установку cookie со domain: duckdns.org, чтобы предотвратить перехват чужих сессий между пользователями одного сервиса. Для решения проблемы привязывайте cookie к вашему полному хосту (например, myhost.duckdns.org), либо используйте собственный зарегистрированный домен второго уровня (yourdomain.ru).
Почему для SSO между поддоменами требуется same_site: none, а не lax?
При значении same_site: 'lax' современные браузеры блокируют передачу cookie при кросс-поддоменных асинхронных вызовах (Fetch/AJAX) и междоменных переходах между auth.yourdomain.ru и целевыми панелями. Это приводит к циклическим редиректам или потере сессии. Значение same_site: 'none' (в обязательном сочетании с HTTPS и флагом Secure) гарантирует корректную работу единого входа (SSO) для всех поддоменов доменной зоны.

Читайте также в блоге SysKit

Смежные руководства и полезные технические статьи