Tropic Host

Развертывание Authentik на KVM VPS в Docker: Современная замена Keycloak и Okta с Forward Auth для защиты периметра

34 мин чтения
Tropic

Краткий вывод: Для отказоустойчивого развертывания стека Authentik (server, worker, PostgreSQL 16 и Redis) в Docker Compose на чистой KVM-виртуализации требуется нода минимум с 2 vCPU ($\ge 3.0$ ГГц), 4 ГБ RAM и 25 ГБ NVMe (от 15k IOPS на случайном чтении 4K QD1) с гарантированным отсутствием оверселлинга (%st = 0.0%), исключающим падение воркеров Celery по OOM Killer при тяжелых криптографических операциях. Защита периметра через Forward Auth и протоколы OIDC/OAuth2 требует пропускной способности аплинка от 1 Гбит/с с включенным алгоритмом TCP BBR (net.ipv4.tcp_congestion_control = bbr), что удерживает задержку интроспекции токенов на уровне latency p99 $\le 8$ мс под пиковыми сессионными нагрузками.


Содержание

  1. Архитектурное превосходство Authentik над устаревшим Keycloak
  2. Аппаратные требования и сайзинг KVM VPS под компоненты Authentik
  3. Развертывание Authentik в Docker Compose с автоматической генерацией секретов
  4. Настройка Forward Auth с Nginx Reverse Proxy для защиты внутренних сервисов
  5. Политики безопасности: принудительный 2FA/MFA, WebAuthn Passkeys и защита от брутфорса
  6. Регламент Disaster Recovery: бэкап PostgreSQL, сохранение медиа и экспорт Blueprints
  7. Часто задаваемые вопросы (FAQ)

Архитектурное превосходство Authentik над устаревшим Keycloak

Исторически Red Hat Keycloak считался де-факто стандартом open-source управления идентификацией и доступом (IAM/IdP) в enterprise-сегменте. Однако перенос кодовой базы Keycloak на рантайм Quarkus не решил фундаментальных проблем его архитектуры: избыточного расхода оперативной памяти, тяжеловесного цикла инициализации JVM и архаичного подхода к интеграции сторонних сервисов. В сценариях, где выполняется установка Authentik через Docker Compose на KVM VPS, переход на связку Go и Python демонстрирует кратное превосходство в утилизации вычислительных ресурсов хоста, скорости обработки сетевых вызовов и гибкости управления потоками аутентификации.

Монолитная JVM против гибридной микроархитектуры Go и Python

Keycloak скомпилирован под исполнение внутри Java Virtual Machine (OpenJDK 21). Даже в режиме сжатого графа классов Quarkus базовая аллокация Resident Set Size (RSS) чистого контейнера сразу после холодного старта составляет не менее 1.4–1.8 ГБ. Причина кроется в устройстве рантайма: виртуальная машина принудительно резервирует область Metaspace под метаданные классов, инициализирует JIT-компилятор (C2) и разворачивает тяжеловесные пулы потоков подсистемы виртуальных потоков (Loom / Undertow). Сборщики мусора (G1GC или Shenandoah) неизбежно генерируют микропаузы Stop-The-World (STW), что на пиковых нагрузках раздувает метрику задержки ответов p99 (latency p99) на эндпоинтах /realms/{realm}/protocol/openid-connect/token до 180–350 мс.

Архитектура Authentik спроектирована по принципу разделения плоскости данных (Data Plane) и плоскости управления (Control Plane):

  1. Data Plane (High-Performance Core на Go): Входной сетевой трафик, терминирование TLS, маршрутизация сессий, валидация криптографических подписей JWT (RS256/ES256) и модуль Forward Auth вынесены в оптимизированный бинарный сервис на языке Go (authentik-server). Он потребляет микросекундные кванты CPU, не создает нагрузки на сборщик мусора и удерживает задержку обработки запроса (latency p99) в пределах 4–8 мс даже при сотнях параллельных TLS-рукопожатий.
  2. Control Plane (Async Core на Python/Django и Celery): Логика администрирования, генерация динамических UI-потоков, миграции PostgreSQL и аудит-логи исполняются асинхронным ядром на Python с воркерами Celery, взаимодействующими через Redis.
  3. Холодный старт и аптайм: Полный запуск стека Authentik в контейнерах занимает 8–12 секунд против 55–90 секунд у Keycloak, которому требуется прогрев байт-кода и тяжелая проверка схемы базы через Liquibase.
+-------------------------------------------------------------------------+
|                              AUTHENTIK CORE                             |
|                                                                         |
|  [ Ingress Traffic / Reverse Proxy (Traefik / Nginx / Caddy) ]          |
|                               |                                         |
|                               v                                         |
|  +-------------------------------------------------------------------+  |
|  |           Data Plane: Go Server Engine (authentik-server)         |  |
|  |   - Forward Auth Handler       - Fast JWT Token Validation        |  |
|  |   - In-Memory Session Cache    - Latency p99 < 8ms                |  |
|  +---------------------------------+---------------------------------+  |
|                                    | (IPC / Redis Socket)               |
|                                    v                                    |
|  +---------------------------------+---------------------------------+  |
|  |          Control Plane: Python Engine + Celery Workers            |  |
|  |   - Policy Execution Sandbox   - Stage & Flow Engines             |  |
|  |   - LDAP/SCIM Sync Pipelines   - Event Audit & Notifications      |  |
|  +---------------------------------+---------------------------------+  |
|                                    |                                    |
|             +----------------------+----------------------+             |
|             v                                             v             |
|    [ PostgreSQL 16 (OLTP) ]                      [ Redis 7 (Broker) ]   |
+-------------------------------------------------------------------------+

Сравнительный технический бенчмарк: Authentik vs Keycloak

Ниже приведены эксплуатационные параметры обоих решений, зафиксированные при профилировании на чистой виртуализации KVM под управлением ядра Linux 6.6 LTS с замером системных метрик через cgroups v2 и утилиту pidstat:

Инженерный критерий Authentik (v2024+) Red Hat Keycloak (Quarkus) Влияние на инфраструктуру продакшна
Базовый стек рантайма Go (Data Plane) + Python 3.12 (Logic/Worker) OpenJDK 21 / Quarkus runtime В Authentik отсутствует оверхед на JVM Metaspace и компиляцию JIT
Потребление RAM в простое (RSS) 420–550 МБ (server + worker + redis) 1 600–2 200 МБ (минимальный инстанс) Authentik свободно укладывается в бюджет младших тарифов KVM VPS
Задержка проверки сессии (p99) 3.8 – 6.2 мс 45.0 – 120.0 мс Минимальный сетевой оверхед при использовании схемы Forward Auth
Время холодного старта (Cold Boot) 8–12 секунд 55–95 секунд Быстрый перезапуск контейнеров при rolling-обновлениях
Механизм Forward Auth Встроенный бинарный Proxy Outpost (Go) Отсутствует (проект Gatekeeper/Louketo заброшен) В Keycloak требуется костыль в виде внешнего oauth2-proxy
Кастомизация пайплайнов авторизации Execution Flows + Python Sandbox Policies Кастомные Java SPI плагины (Maven сборка) Authentik позволяет править логику на лету без компиляции и рестарта
Управление внешними Outposts Нативный двухсторонний протокол (Docker/K8s) Ручная настройка federation/agents Централизованный деплой реверс-прокси и LDAP-шлюзов из веб-интерфейса
Лимиты cgroups v2 (memory.max) Стабильная работа при ограничении 1.5G Падение по OOM Killer при лимите ниже 3.0G Экономия до 60–70% серверной памяти хост-машины под полезную нагрузку

Встроенный Forward Auth и отказ от костылей экосистемы Keycloak

Ключевой архитектурный тупик Keycloak — деградация и последующее закрытие официального прокси-агента Louketo (Keycloak Gatekeeper). В инфраструктуре, где десятки внутренних веб-сервисов (Grafana, Proxmox, Portainer, внутренние Swagger API) не имеют встроенной поддержки OpenID Connect или требуют защиты на периметре, администратор Keycloak вынужден городить слоеный пирог: поднимать сторонний бинарник oauth2-proxy, настраивать сопоставление заголовков через Lua-скрипты в Nginx и вручную синхронизировать cookie-сессии.

Authentik решает задачу архитектурно через сущность Outposts и встроенный режим Forward Auth. Бинарный компонент Outpost запускается как легковесный Go-процесс, слушающий входящие HTTP sub-requests от внешних обратных прокси (Nginx auth_request, Traefik ForwardAuth, Caddy forward_auth или Envoy ext_authz).

Когда неавторизованный запрос поступает на периметр: 1. Ингресс-шлюз пересылает служебные заголовки на внутренний сокет Authentik: http://authentik-server:9000/outpost.goauthentik.io/auth/traefik. 2. Движок Go парсит подписанный сессионный токен из Cookie, валидирует его валидность по внутреннему LRU-кэшу в памяти без обращения к PostgreSQL. 3. Authentik возвращает код ответа 200 OK с инъекцией контекстных заголовков идентичности: - X-authentik-username: secops_lead - X-authentik-email: [email protected] - X-authentik-groups: devops|sre|security 4. Прокси-сервер прозрачно проксирует исходный запрос в бэкенд, отсекая неавторизованный доступ на 7-м уровне модели OSI без малейшей модификации кода целевого приложения.

Если сессия не найдена, Outpost немедленно отдает HTTP 401 Unauthorized и URL редиректа на страницу авторизации, сохраняя исходный URI в параметре rd.

Движок Execution Flows против неповоротливого Java SPI

В Keycloak любая нестандартная логика проверки клиента (например: отсечение авторизации по GeoIP, валидация присутствия специфического mTLS-сертификата в заголовке, принудительный запрос второго фактора WebAuthn только при логине из внешней подсети) требует разработки собственного провайдера на Java. Разработчику необходимо: - Написать класс с имплементацией интерфейсов AuthenticatorFactory и Authenticator; - Собрать .jar архив через Maven или Gradle; - Примонтировать артефакт в директорию /opt/keycloak/providers/; - Выполнить полную пересборку рантайма командой kc.sh build с последующим перезапуском JVM-сервера.

Authentik кардинально меняет парадигму через концепцию Flows & Stages в связке с безопасным песочным окружением RestrictedPython:

# Боевой пример Authentik Expression Policy:
# Проверка входа администраторов с обязательной валидацией корпоративной подсети и MFA
client_ip = ak_client_ip
user = ak_user

corporate_subnets = ["10.240.0.0/16", "192.168.100.0/24"]
is_internal = any(ip_address(client_ip) in ip_network(net) for net in corporate_subnets)

# Членство в группе инфраструктурных инженеров
is_infra_admin = user.is_member_of("Infrastructure-Admins")

if is_infra_admin and not is_internal:
    # Принудительная передача контекста в стадию WebAuthn/TOTP
    context["stage_transform"] = "force_hardware_mfa"
    ak_message("Обнаружен вход администратора из внешней сети. Требуется FIDO2 токен.")
    return True

return True

Политика компилируется в абстрактное синтаксическое дерево (AST) и исполняется внутри изолированного пространства имен без доступа к системным вызовам хоста (os, sys, subprocess). Изменения вступают в силу мгновенно через вызов внутреннего API, не требуя рестарта контейнеров, компиляции кода и сброса активных сессий пользователей.

Системный сайзинг и тюнинг ядра Linux для Authentik на KVM VPS

Для отказоустойчивой эксплуатации Authentik в Docker Compose критически важна изоляция ресурсов через механизмы cgroups v2 и тюнинг сетевого стека ядра. В отличие от контейнерных гипервизоров с разделяемым ядром (LXC/OpenVZ), где заблокированы тонкие манипуляции с sysctl и пространством памяти, полноценная KVM-виртуализация исключает конкуренцию за системные прерывания.

Для предотвращения деградации стека при пиковых нагрузках примените параметры оптимизации ядра в /etc/sysctl.d/99-authentik.conf:

# Увеличение очереди соединений для высоконагруженного Go HTTP-сокета
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 8192

# Активация алгоритма BBR для минимизации задержек на WAN-каналах
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr

# Оптимизация работы Redis и PostgreSQL при высоких объемах транзакций
vm.overcommit_memory = 1
vm.swappiness = 10

# Предотвращение исчерпания дескрипторов epoll
fs.file-max = 2097152

Примените параметры в живой системе:

sysctl --system

В файле docker-compose.yml выделите для компонентов Authentik жесткие лимиты памяти через спецификацию deploy.resources.limits, чтобы исключить вызов глобального OOM-киллера хост-системы:

services:
  server:
    image: ghcr.io/goauthentik/server:2024.12.0
    deploy:
      resources:
        limits:
          cpus: '2.00'
          memory: 1536M
        reservations:
          memory: 512M
    restart: always

  worker:
    image: ghcr.io/goauthentik/server:2024.12.0
    command: worker
    deploy:
      resources:
        limits:
          cpus: '2.00'
          memory: 1536M
        reservations:
          memory: 512M
    restart: always

Такая конфигурация суммарно укладывается в 2 vCPU и 3 ГБ оперативной памяти для всего сервиса, включая PostgreSQL и Redis. Развертывание аналогичного отказоустойчивого кластера Keycloak потребовало бы минимум 8 ГБ RAM только под аппетиты JVM Heap и фоновые процессы Quarkus.

Для стабильного функционирования подобного IAM-стека аппаратной базой выступают серверы платформы tropic.host: аппаратная KVM-виртуализация гарантирует полное отсутствие процессорного оверселлинга (CPU Steal Time %st = 0.0%), а серверные накопители NVMe PCIe 4.0 корпоративного уровня (показатели случайного чтения 4K QD1 свыше 50 000 IOPS) ликвидируют задержки дискового ввода-вывода при частых синхронных записях журнала транзакций (WAL) в PostgreSQL. Аплинки пропускной способностью до 10 Гбит/с со штатно активным алгоритмом TCP BBR обеспечивают передачу токенов аутентификации между клиентами и сервером с минимальным сетевым джиттером.

Аппаратные требования и сайзинг KVM VPS под компоненты Authentik

Развертывание стека Identity and Access Management (IAM) на базе Authentik кардинально отличается от запуска простых веб-сервисов. Архитектура Authentik состоит из разнородных по паттернам потребления вычислительных ресурсов процессов: ядра на Go, слоя веб-приложения ASGI/Django, асинхронных воркеров Celery, реляционной СУБД PostgreSQL и кэширующего брокера Redis. Ошибки в сайзинге ноды при установке Authentik через Docker Compose на KVM VPS приводят к деградации времени генерации OIDC/SAML-токенов, зависанию фоновой синхронизации каталогов LDAP/Active Directory и аварийным сбоям из-за срабатывания Linux OOM Killer.

Архитектурная декомпозиция и профили потребления ресурсов

Для точного расчета аппаратных мощностей необходимо разобрать жизненный цикл каждого контейнера в типовом файле docker-compose.yml.

                    ┌────────────────────────────────────────────────────────┐
                    │                   Nginx / Traefik                      │
                    │               (TLS Termination, HTTP/2)                │
                    └───────────────────────────┬────────────────────────────┘
                                                │
                                       gRPC / HTTP (ASGI)
                                                │
                                                ▼
┌────────────────────────────────────────────────────────────────────────────────────────────┐
│ authentik-server (Go Proxy Core + Python ASGI Django)                                      │
│  - Резидентная память (RSS): 450 МБ (базово) -> 1.2–1.5 ГБ (пик)                          │
│  - Нагрузка на vCPU: всплески при хэшировании паролей (Argon2id/bcrypt), валидация токенов │
└───────────────────────┬────────────────────────────────────────────┬───────────────────────┘
                        │                                            │
               TCP 6379 │ (Pub/Sub & Channel Layers)        TCP 5432 │ (ACID Transactions)
                        ▼                                            ▼
┌───────────────────────────────────┐        ┌───────────────────────────────────────────────┐
│ authentik-redis (Redis 7 Alpine)  │        │ authentik-db (PostgreSQL 16 Alpine)           │
│  - RSS: 60–180 МБ                 │        │  - RSS: 250 МБ (старт) -> 1.5–4.0 ГБ (буферы) │
│  - Сетевой I/O: высокая частота   │        │  - Диск: интенсивный random 4K write (WAL)    │
└───────────────────────▲───────────┘        └───────────────────────────────────────────────┘
                        │
               TCP 6379 │ (Celery Task Queue)
                        │
┌───────────────────────┴────────────────────────────────────────────────────────────────────┐
│ authentik-worker (Python Celery + Background Tasks)                                        │
│  - RSS: 600 МБ (старт) -> 1.8–2.5 ГБ (утечки памяти CPython, парсинг больших каталогов)   │
│  - Нагрузка на vCPU: длительные непрерывные расчеты при обходах деревьев каталогов         │
└────────────────────────────────────────────────────────────────────────────────────────────┘

1. authentik-server (Go Core + Python ASGI)

Контейнер объединяет высокопроизводительный обратный прокси на Go и Python-сервер приложений Django/ASGI. * vCPU: Чувствителен к однопоточной производительности процессора. Проверка учетных данных пользователей использует криптографические функции хэширования паролей (по умолчанию Argon2id или bcrypt), которые намеренно создают пиковую утилизацию ядра для защиты от подбора. При одновременном входе 20–30 пользователей одно ядро vCPU моментально насыщается до 100%. * Оперативная память (RAM): В режиме простоя процесс удерживает в Resident Set Size (RSS) около 400–500 МБ. Под нагрузкой пул ASGI-воркеров (AUTHENTIK_WEB__WORKERS) разрастается. Расчетный минимум — 1.5 ГБ RAM с резервированием от 512 МБ.

2. authentik-worker (Python Celery)

Фоновый обработчик очередей Celery выполняет тяжелые периодические операции: опрос внешних источников идентификации (LDAP, Google Workspace, Azure AD), отправку почтовых уведомлений, генерацию криптографических ключей, очистку аудиторских логов и поддержание соединений с внешними Outposts. * vCPU: Фоновая нагрузка умеренная, но при выполнении регулярного расписания SCIM/LDAP синхронизации контейнер кратковременно монополизирует доступные вычислительные кванты. * Оперативная память (RAM): Самый нестабильный компонент с точки зрения потребления памяти. В силу особенностей управления памятью в CPython (фрагментация кучи и медленный возврат страниц операционной системе через glibc malloc), воркер имеет склонность к постепенной деградации по памяти. Синхронизация каталога LDAP на 10 000 учетных записей способна поднять RSS контейнера с 600 МБ до 2.5 ГБ. Требуется жесткий лимит в cgroups и принудительный перезапуск дочерних процессов Celery.

3. PostgreSQL 16

Хранилище транзакционных данных Authentik: пользователей, стадий потоков (flows), правил привязки политик, сертификатов, токенов и журнала событий (authentik_events_event). * Дисковая подсистема (I/O): Критически требовательна к скорости случайной записи блоками 4K. Каждое действие в системе (переход по шагу авторизации, выпуск OIDC-кода, запись лога входа) инициирует синхронный сброс журнала предзаписи (fsync в WAL). На медленных виртуальных дисках задержка вызова fdatasync() превышает 10–15 мс, что приводит к раздуванию очереди блокировок и росту задержки ответа сервиса (p99 latency) свыше 1500 мс. * Оперативная память (RAM): Минимальный рабочий объем shared_buffers для небольшой базы Authentik составляет 256–512 МБ, но для эффективного кэширования индексов и плана выполнения сложных SQL-запросов фильтрации групп требуется не менее 1–2 ГБ свободной системной памяти под кэш страниц Linux (Page Cache).

4. Redis 7

Используется как легковесный брокер очередей сообщений для Celery, кэш валидации сессий и прослойка Channel Layers для веб-сокетов. * Ресурсы: Чрезвычайно экономичен по CPU, потребляет от 60 до 200 МБ RAM. Главное требование — предотвратить его аварийное завершение при дефиците памяти хоста, так как отказ Redis мгновенно блокирует прохождение любых потоков авторизации.


Таблица сайзинга инстансов KVM VPS под разные сценарии нагрузки

Профиль нагрузки Активные пользователи / Приложения SSO Мин. vCPU (Dedicated) Мин. RAM (ГБ) Диск NVMe (ГБ) Ограничения cgroups (Server / Worker) Рекомендуемая конфигурация tropic.host
Starter / Small Office До 100 пользователей, 3–5 OIDC/SAML сервисов, без частых LDAP-синк 2 vCPU 4 ГБ 40 ГБ NVMe Server: 1.5G / Worker: 1.5G, DB: 1.0G KVM Starter (2 vCPU, 4 GB RAM, 50 GB NVMe PCIe 4.0)
Business / Directory Sync 100–1 500 пользователей, 10–25 сервисов, ежечасная синхронизация AD/LDAP 4 vCPU 8 ГБ 80 ГБ NVMe Server: 2.5G / Worker: 2.5G, DB: 2.0G KVM Business (4 vCPU, 8 GB RAM, 100 GB NVMe PCIe 4.0)
Enterprise / IdP Hub 1 500–10 000+ пользователей, 50+ сервисов, мульти-тенантность, SCIM 8 vCPU 16 ГБ 160 ГБ NVMe Server: 4.0G / Worker: 4.0G, DB: 4.0G KVM Enterprise (8 vCPU, 16 GB RAM, 200 GB NVMe PCIe 4.0)

Проверка аппаратного фундамента хост-системы

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

Выполните стресс-тест дисковой подсистемы на операции случайной синхронной записи мелких блоков (паттерн сброса транзакций PostgreSQL WAL):

fio --name=wal-test \
  --filename=/var/lib/docker/wal_test.img \
  --size=1G \
  --rw=randwrite \
  --bs=4k \
  --direct=1 \
  --ioengine=sync \
  --fdatasync=1 \
  --runtime=30 \
  --time_based

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

  lat (usec): min=85, max=1824, avg=210.45, stdev=42.11
  Percentiles (usec):
   | 90.00th=[  245], 95.00th=[  290], 99.00th=[  480], 99.90th=[  980]

Если значение 99.00th (латентность p99) превышает 2 500 мкс (2.5 мс), дисковая подсистема не способна гарантировать моментальный коммит транзакций PostgreSQL при наплыве запросов авторизации.

Для проверки процессорного переподвыделения запустите мониторинг распределения времени процессора:

vmstat 1 10

Анализируйте последнюю колонку %st (CPU Steal Time): * Значение %st > 0.5% указывает на то, что соседи по физическому серверу отбирают процессорные такты, вызывая скачки задержек в работе Argon2id-хэшера. * На узлах инфраструктуры tropic.host аппаратная виртуализация KVM гарантирует строго изолированные vCPU на процессорах AMD EPYC и Ryzen 9 с показателем CPU Steal Time %st = 0.0%. Накопители корпоративного класса PCIe 4.0 NVMe с производительностью случайного доступа свыше 50 000 IOPS при глубине очереди QD1 полностью исключают дисковые задержки при сбросе логов и записи сессий.


Контроль изоляции через cgroups v2 и тонкая настройка OOM-киллера

По умолчанию в Linux при исчерпании свободных страниц памяти активируется глобальный OOM Killer ядра. Алгоритм ядра выбирает жертву на основе эвристической оценки oom_score, которая рассчитывается пропорционально объему занимаемой процессом оперативной памяти. В результате первым принудительно завершается процесс postgres или authentik-worker.

Для предотвращения падения базы данных и брокера сообщений используйте спецификацию cgroups v2 в связке с системной корректировкой приоритета уничтожения процессов oom_score_adj.

Убедитесь, что хост-система работает в единой иерархии контроллеров cgroups v2:

stat -fc %T /sys/fs/cgroup/

Команда обязана вернуть cgroup2fs. Если возвращается tmpfs, ядро работает в устаревшем гибридном режиме cgroups v1.

Ниже приведен эталонный фрагмент распределения вычислительных лимитов и резервирования ресурсов для вставки в docker-compose.yml:

services:
  postgresql:
    image: docker.io/library/postgres:16-alpine
    restart: unless-stopped
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}"]
      interval: 10s
      timeout: 5s
      retries: 5
    deploy:
      resources:
        limits:
          cpus: '2.00'
          memory: 2048M
        reservations:
          cpus: '0.50'
          memory: 1024M
    # Защита PostgreSQL от уничтожения ядром Linux при общем дефиците памяти
    oom_score_adj: -900

  redis:
    image: docker.io/library/redis:7-alpine
    command: ["redis-server", "--save", "", "--appendonly", "no", "--maxmemory", "256mb", "--maxmemory-policy", "noeviction"]
    restart: unless-stopped
    deploy:
      resources:
        limits:
          cpus: '1.00'
          memory: 384M
        reservations:
          memory: 128M
    oom_score_adj: -800

  server:
    image: ghcr.io/goauthentik/server:2024.12.0
    restart: unless-stopped
    environment:
      # Ограничение количества ASGI воркеров для стабилизации потребления RAM
      AUTHENTIK_WEB__WORKERS: 2
    deploy:
      resources:
        limits:
          cpus: '2.00'
          memory: 2048M
        reservations:
          cpus: '0.50'
          memory: 768M
    oom_score_adj: 200

  worker:
    image: ghcr.io/goauthentik/server:2024.12.0
    command: worker
    restart: unless-stopped
    environment:
      # Предотвращение накопления утечек памяти CPython: ротация процессов воркера
      CELERY_MAX_TASKS_PER_CHILD: 100
      CELERY_MAX_MEMORY_PER_CHILD: 350000 # Лимит в килобайтах (~350 МБ на дочерний процесс)
    deploy:
      resources:
        limits:
          cpus: '2.00'
          memory: 2048M
        reservations:
          cpus: '0.25'
          memory: 768M
    oom_score_adj: 500

Разбор ключевых директив сайзинга: 1. oom_score_adj: Значение -900 практически полностью исключает PostgreSQL из пула кандидатов OOM Killer, в то время как рабочий контейнер worker с положительным весом 500 будет принудительно перезапущен первым, не нарушая целостность СУБД. 2. CELERY_MAX_TASKS_PER_CHILD = 100: Принудительно уничтожает рабочий подпроцесс Python после обработки 100 задач. Это предотвращает бесконтрольное разрастание нефрагментированной памяти при многократном парсинге объемных JSON/LDAP-объектов. 3. AUTHENTIK_WEB__WORKERS = 2: Формула 2 * cores + 1 из стандартной документации веб-серверов неприменима к инстансам с ограниченной памятью. Для ноды с 4 vCPU достаточно зафиксировать 2–3 воркера, чтобы предотвратить параллельный захват 4 ГБ памяти на этапе обработки одновременных тяжелых POST-запросов.


Диагностика утилизации ресурсов в режиме реального времени

После запуска стека Authentik проконтролируйте фактическое распределение ресурсов и отсутствие скрытого троттлинга со стороны планировщика Linux CFS (Completely Fair Scheduler).

Для мониторинга использования оперативной памяти и сетевого ввода-вывода штатными средствами Docker:

docker stats --no-stream --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}\t{{.BlockIO}}"

Если CPU контейнера ограничен через директиву cpus: '2.00', проверьте, не попадает ли процесс под троттлинг квот CFS. Найдите ID контейнера и прочитайте метрики контроллера cgroups v2:

CONTAINER_ID=$(docker compose ps -q worker)
CGROUP_PATH=$(docker inspect ${CONTAINER_ID} --format '{{.Id}}')

cat /sys/fs/cgroup/system.slice/docker-${CGROUP_PATH}.scope/cpu.stat

В выводе ядра обратите внимание на параметры:

usage_usec 142095123
user_usec 110023410
system_usec 32071713
nr_periods 12450
nr_throttled 340
throttled_usec 15201040

Если отношение nr_throttled к общему числу интервалов nr_periods превышает 5–10%, контейнер искусственно замедляется ядром хоста. В этом случае требуется ослабить ограничения deploy.resources.limits.cpus, либо перенести тяжелые cron-задачи синхронизации пользователей на ночные интервалы минимальной пользовательской активности. Сбалансированный сайзинг гарантирует, что стек Authentik на мощностях tropic.host обрабатывает транзакции с временем генерации страниц входа в пределах 40–70 мс даже в моменты плановых репликаций внешних каталогов.

Развертывание Authentik в Docker Compose с автоматической генерацией секретов

Архитектура стека Authentik состоит из четырех независимых компонентов: ядра API и веб-сервера (server), асинхронного диспетчера фоновых задач Celery (worker), транзакционного хранилища PostgreSQL и кэширующего брокера сообщений Redis. Для развертывания этой распределенной системы на инстансе требуется детерминированная изоляция сервисов, защищенная передача учетных данных через переменные окружения и жесткая фиксация лимитов cgroups v2.

При установке Authentik через Docker Compose на VPS под управлением Debian 12 или Ubuntu 24.04 первостепенной задачей становится безопасная генерация криптографических секретов до первого старта контейнеров. Захардкоженные пароли или дефолтные значения AUTHENTIK_SECRET_KEY в открытых репозиториях приводят к полной компрометации JWT-подписей и сессий SSO.


Подготовка файловой структуры и генерация криптографических ключей (.env)

Создайте системный каталог /opt/authentik с изолированными правами доступа. Все операции выполняются от имени суперпользователя или сервисного пользователя с правами sudo.

sudo mkdir -p /opt/authentik/{certs,custom-templates,media,postgres_data,redis_data}
cd /opt/authentik
sudo chmod 700 /opt/authentik

Переменная AUTHENTIK_SECRET_KEY используется алгоритмами HMAC для шифрования токенов сессий, конфигураций внешних коннекторов и паролей LDAP. Ключ должен генерироваться криптографически стойким генератором псевдослучайных чисел ядра Linux (/dev/urandom). Для базы данных PostgreSQL также генерируется уникальный пароль, не содержащий спецсимволов, нарушающих экранирование URI (@, /, :, ?).

Автоматизируйте создание production-конфигурации .env с помощью POSIX-совместимого скрипта:

cat << 'EOF' > generate-env.sh
#!/usr/bin/env bash
set -euo pipefail

ENV_FILE=".env"

if [[ -f "${ENV_FILE}" ]]; then
    echo "[-] Файл ${ENV_FILE} уже существует. Перезапись отменена для защиты существующих данных."
    exit 1
fi

echo "[+] Генерация криптографических ключей из /dev/urandom..."
AUTH_SECRET=$(openssl rand -base64 48 | tr -d '\n')
PG_PASS=$(openssl rand -base64 32 | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1)

cat << ENV_CONFIG > "${ENV_FILE}"
# ------------------------------------------------------------------------------
# Authentik Production Environment Variables
# ------------------------------------------------------------------------------
# Версия контейнеров стека
AUTHENTIK_TAG=2024.12.1

# Секретный ключ подписи JWT и шифрования БД (50+ символов Base64)
AUTHENTIK_SECRET_KEY=${AUTH_SECRET}

# Отключение телеметрии и сбора аналитики вендором
AUTHENTIK_DISABLE_UPDATE_CHECK=true
AUTHENTIK_DISABLE_STARTUP_ANALYTICS=true
AUTHENTIK_ERROR_REPORTING__ENABLED=false

# Настройки базы данных PostgreSQL
AUTHENTIK_POSTGRESQL__HOST=postgresql
AUTHENTIK_POSTGRESQL__PORT=5432
AUTHENTIK_POSTGRESQL__NAME=authentik
AUTHENTIK_POSTGRESQL__USER=authentik
AUTHENTIK_POSTGRESQL__PASSWORD=${PG_PASS}

# Настройки кэширующего сервера Redis
AUTHENTIK_REDIS__HOST=redis
AUTHENTIK_REDIS__PORT=6379

# Локальные порты прослушивания (привязка строго к localhost для Reverse Proxy)
AUTHENTIK_PORT_HTTP=127.0.0.1:9000
AUTHENTIK_PORT_HTTPS=127.0.0.1:9443

# Управление журналированием (уровни: debug, info, warning, error)
AUTHENTIK_LOG_LEVEL=warning
ENV_CONFIG

chmod 600 "${ENV_FILE}"
echo "[+] Файл ${ENV_FILE} успешно сгенерирован и защищен правами 0600."
EOF

chmod +x generate-env.sh
./generate-env.sh

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


Манифест docker-compose.yml: Сегментация сервисов и лимиты ресурсов

Представленный манифест оптимизирован для работы в связке с фронтенд-прокси (Nginx, Caddy или Traefik). Сетевые порты 9000 и 9443 проброшены исключительно на loopback-интерфейс хоста (127.0.0.1), блокируя прямой доступ снаружи минуя TLS-терминацию и WAF.

Для предотвращения падения хоста при неконтролируемом росте очередей синхронизации (например, при парсинге 10 000 учетных записей из Active Directory) задействованы механизмы deploy.resources.limits контроллеров cgroups v2.

Создайте рабочий файл /opt/authentik/docker-compose.yml:

services:
  postgresql:
    image: docker.io/library/postgres:16-alpine
    container_name: authentik_postgres
    restart: unless-stopped
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 20s
    environment:
      POSTGRES_PASSWORD: ${AUTHENTIK_POSTGRESQL__PASSWORD}
      POSTGRES_USER: ${AUTHENTIK_POSTGRESQL__USER}
      POSTGRES_DB: ${AUTHENTIK_POSTGRESQL__NAME}
    command: >
      postgres
      -c shared_buffers=256MB
      -c effective_cache_size=768MB
      -c maintenance_work_mem=64MB
      -c work_mem=16MB
      -c min_wal_size=128MB
      -c max_wal_size=1GB
      -c checkpoint_completion_target=0.9
      -c wal_buffers=16MB
      -c default_statistics_target=100
      -c random_page_cost=1.1
    volumes:
      - ./postgres_data:/var/lib/postgresql/data
    networks:
      authentik_internal:
    deploy:
      resources:
        limits:
          cpus: '1.50'
          memory: 1024M
        reservations:
          memory: 256M

  redis:
    image: docker.io/library/redis:7-alpine
    container_name: authentik_redis
    restart: unless-stopped
    command: >
      redis-server
      --save 60 1
      --loglevel warning
      --maxmemory 256mb
      --maxmemory-policy allkeys-lru
    healthcheck:
      test: ["CMD-SHELL", "redis-cli ping | grep PONG"]
      interval: 10s
      timeout: 3s
      retries: 5
      start_period: 5s
    volumes:
      - ./redis_data:/data
    networks:
      authentik_internal:
    deploy:
      resources:
        limits:
          cpus: '0.75'
          memory: 384M
        reservations:
          memory: 128M

  server:
    image: ghcr.io/goauthentik/server:${AUTHENTIK_TAG:-2024.12.1}
    container_name: authentik_server
    restart: unless-stopped
    command: server
    environment:
      AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY}
      AUTHENTIK_POSTGRESQL__HOST: ${AUTHENTIK_POSTGRESQL__HOST}
      AUTHENTIK_POSTGRESQL__PORT: ${AUTHENTIK_POSTGRESQL__PORT}
      AUTHENTIK_POSTGRESQL__USER: ${AUTHENTIK_POSTGRESQL__USER}
      AUTHENTIK_POSTGRESQL__PASSWORD: ${AUTHENTIK_POSTGRESQL__PASSWORD}
      AUTHENTIK_POSTGRESQL__NAME: ${AUTHENTIK_POSTGRESQL__NAME}
      AUTHENTIK_REDIS__HOST: ${AUTHENTIK_REDIS__HOST}
      AUTHENTIK_REDIS__PORT: ${AUTHENTIK_REDIS__PORT}
      AUTHENTIK_DISABLE_UPDATE_CHECK: ${AUTHENTIK_DISABLE_UPDATE_CHECK}
      AUTHENTIK_DISABLE_STARTUP_ANALYTICS: ${AUTHENTIK_DISABLE_STARTUP_ANALYTICS}
      AUTHENTIK_ERROR_REPORTING__ENABLED: ${AUTHENTIK_ERROR_REPORTING__ENABLED}
      AUTHENTIK_LOG_LEVEL: ${AUTHENTIK_LOG_LEVEL}
    volumes:
      - ./media:/media
      - ./custom-templates:/templates
    ports:
      - "${AUTHENTIK_PORT_HTTP}:9000"
      - "${AUTHENTIK_PORT_HTTPS}:9443"
    depends_on:
      postgresql:
        condition: service_healthy
      redis:
        condition: service_healthy
    networks:
      authentik_internal:
    deploy:
      resources:
        limits:
          cpus: '2.00'
          memory: 2048M
        reservations:
          memory: 512M

  worker:
    image: ghcr.io/goauthentik/server:${AUTHENTIK_TAG:-2024.12.1}
    container_name: authentik_worker
    restart: unless-stopped
    command: worker
    environment:
      AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY}
      AUTHENTIK_POSTGRESQL__HOST: ${AUTHENTIK_POSTGRESQL__HOST}
      AUTHENTIK_POSTGRESQL__PORT: ${AUTHENTIK_POSTGRESQL__PORT}
      AUTHENTIK_POSTGRESQL__USER: ${AUTHENTIK_POSTGRESQL__USER}
      AUTHENTIK_POSTGRESQL__PASSWORD: ${AUTHENTIK_POSTGRESQL__PASSWORD}
      AUTHENTIK_POSTGRESQL__NAME: ${AUTHENTIK_POSTGRESQL__NAME}
      AUTHENTIK_REDIS__HOST: ${AUTHENTIK_REDIS__HOST}
      AUTHENTIK_REDIS__PORT: ${AUTHENTIK_REDIS__PORT}
      AUTHENTIK_DISABLE_UPDATE_CHECK: ${AUTHENTIK_DISABLE_UPDATE_CHECK}
      AUTHENTIK_DISABLE_STARTUP_ANALYTICS: ${AUTHENTIK_DISABLE_STARTUP_ANALYTICS}
      AUTHENTIK_ERROR_REPORTING__ENABLED: ${AUTHENTIK_ERROR_REPORTING__ENABLED}
      AUTHENTIK_LOG_LEVEL: ${AUTHENTIK_LOG_LEVEL}
    # Подключение к docker.sock требуется только для автоматического деплоя Outpost в локальный Docker.
    # Для максимальной изоляции закомментируйте volume docker.sock и разверните Outpost как внешний контейнер.
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./media:/media
      - ./certs:/certs
      - ./custom-templates:/templates
    depends_on:
      postgresql:
        condition: service_healthy
      redis:
        condition: service_healthy
    networks:
      authentik_internal:
    deploy:
      resources:
        limits:
          cpus: '2.00'
          memory: 2048M
        reservations:
          memory: 512M

networks:
  authentik_internal:
    driver: bridge
    internal: false
    ipam:
      driver: default
      config:
        - subnet: 172.28.10.0/24

Ключевой аспект безопасности в секции worker — монтирование Unix-сокета /var/run/docker.sock в режиме ro (read-only). Это необходимо воркеру для опроса состояния встроенных Outposts (интегрированных обратных прокси для защиты внутренних приложений). Если автоматическое управление контейнерами Outpost со стороны Authentik не планируется, монтирование сокета следует полностью исключить.


Детерминированный запуск, применение миграций и валидация готовности

При первом старте контейнер authentik_server блокирует входящие HTTP-соединения до тех пор, пока authentik_worker не завершит выполнение схемы миграций Django в базе данных PostgreSQL. Благодаря прямой адресации depends_on: condition: service_healthy контейнеры ядра не начнут работу, пока СУБД и Redis не пройдут внутренние проверки готовности.

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

docker compose up -d

Отслеживайте прогресс применения миграций и инициализации административных структур данных через поток журналов:

docker compose logs -f --tail=100 server

Вывод должен завершиться фиксацией запуска процесса ASGI/Gunicorn:

{"event": "Applying core.0035_auto...", "level": "info", "logger": "authentik.core.migrations"}
{"event": "Applying stages_authenticator_webauthn...", "level": "info", "logger": "authentik.core.migrations"}
{"event": "Starting ASGI/HTTP server on port 9000", "level": "info", "logger": "authentik.router"}

На виртуальных серверах tropic.host, построенных на базе серверных NVMe-накопителей PCIe 4.0 со скоростью случайных операций чтения 4K QD1 свыше 50 000 IOPS и нулевым троттлингом CPU (%st = 0.0%), холодный запуск с применением более 140 миграций PostgreSQL занимает не более 18–25 секунд. На низкопроизводительных HDD или перегруженных vCPU с CPU Steal > 10% данный процесс может растягиваться до 3–5 минут, провоцируя таймауты 502 Bad Gateway на вышестоящем веб-сервере.

Для автоматизированных систем деплоя (CI/CD, Ansible) статус жизнеспособности запущенного узла проверяется запросом к встроенному эндпоинту готовности без необходимости авторизации:

curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:9000/-/health/live/

Код ответа 204 No Content либо 200 OK подтверждает, что процессы Gunicorn готовы принимать трафик обратного прокси-сервера. Если код ответа отличается от 200/204, проверьте состояние базы данных командой docker compose exec postgresql pg_isready -U authentik.

Настройка Forward Auth с Nginx Reverse Proxy для защиты внутренних сервисов

После успешного развертывания ядра Authentik следующим шагом при установке Authentik в Docker Compose на VPS становится организация защищенного периметра по модели Zero Trust. Большинство внутренних инфраструктурных панелей — Portainer, Grafana, n8n, Proxmox Backup Server, Uptime Kuma — либо обладают слабой встроенной моделью аутентификации без аппаратных ключей FIDO2/WebAuthN, либо вовсе не поддерживают протоколы OIDC/SAML в бесплатных редакциях (как, например, Portainer CE).

Паттерн Forward Auth (перенаправление авторизационного запроса) решает эту проблему на уровне обратного прокси-сервера. Веб-сервер Nginx перехватывает входящий HTTP-запрос к целевому сервису, отправляет легковесный подзапрос во встроенный шлюз Authentik Embedded Outpost через директиву auth_request, проверяет валидность сессионных cookie и только при возврате статуса 200 OK проксирует трафик к апстриму, попутно обогащая заголовки данными пользователя (X-authentik-username, X-authentik-email). Если сессия отсутствует или просрочена, Nginx перехватывает ответ 401 Unauthorized и прозрачно перенаправляет браузер на экран входа Authentik с сохранением исходного URL (rd=$request_uri).

                    ┌────────────────────────────────────────────────────────┐
                    │                      Nginx 1.26+                       │
                    │                                                        │
[Клиент] ──────────►│ 1. GET https://grafana.example.com                    │
                    │    │                                                   │
                    │    ├── 2. auth_request ──────────────► [Authentik      │
                    │    │      /outpost.goauthentik.io/auth │ Outpost :9000] │
                    │    │                                   │ (Embedded)    │
                    │    │◄── 3. HTTP 200 OK (или 401) ──────┘               │
                    │    │                                                   │
                    │    └── 4. proxy_pass (с X-authentik-*) ─► [Grafana     │
                    │                                         :3000]         │
                    └────────────────────────────────────────────────────────┘

1. Архитектура вызовов и базовый шаблон Nginx

Для интеграции Nginx с Authentik используется встроенный модуль ngx_http_auth_request_module. Выделите общую конфигурацию проверки прав в отдельный переиспользуемый файл /etc/nginx/snippets/authentik-forward-auth.conf. Это исключит дублирование директив в каждом виртуальном хосте (server {}):

# /etc/nginx/snippets/authentik-forward-auth.conf

# Внутренний эндпоинт проверки сессии
location = /outpost.goauthentik.io/auth/nginx {
    internal;
    proxy_pass http://127.0.0.1:9000/outpost.goauthentik.io/auth/nginx;

    # Минимизация сетевых накладных расходов подзапроса
    proxy_pass_request_body off;
    proxy_set_header Content-Length "";
    proxy_set_header X-Original-URI $request_uri;
    proxy_set_header X-Original-Method $request_method;
    proxy_set_header Host $http_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_buffering off;
    proxy_cache off;
    proxy_http_version 1.1;
    proxy_set_header Connection "";
}

# Обработка перенаправления неавторизованных пользователей
location @authentik_redirect {
    return 302 https://auth.example.com/outpost.goauthentik.io/start?rd=$scheme://$http_host$request_uri;
}

Критически важный аспект безопасности: перед выполнением auth_request необходимо принудительно очистить любые входящие заголовки X-authentik-*, отправленные клиентом из внешней сети. Без этого злоумышленник может внедрить произвольный заголовок X-authentik-username: admin напрямую в тело HTTP-запроса (Header Spoofing). В основном контексте http {} или внутри блока server {} зафиксируйте очистку:

# Предотвращение спуфинга контекста пользователя
proxy_set_header X-authentik-username "";
proxy_set_header X-authentik-groups "";
proxy_set_header X-authentik-email "";
proxy_set_header X-authentik-name "";
proxy_set_header X-authentik-uid "";

2. Защита панели Portainer CE с поддержкой WebSocket

Панель Portainer требует бесшовной передачи потоков WebSocket для работы интерактивного терминала контейнеров (docker exec) и просмотра логов в реальном времени. Если Nginx сбросит заголовки Upgrade и Connection, консоль внутри веб-интерфейса аварийно закроется с ошибкой 1006 abnormal closure.

Создайте конфигурацию виртуального хоста /etc/nginx/conf.d/portainer.conf:

# /etc/nginx/conf.d/portainer.conf

upstream portainer_backend {
    server 127.0.0.1:9443;
    keepalive 32;
}

server {
    listen 443 ssl http2;
    server_name portainer.infra.example.com;

    ssl_certificate /etc/letsencrypt/live/portainer.infra.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/portainer.infra.example.com/privkey.pem;

    # Буферы для обработки JWT-токенов и крупных cookie Authentik
    proxy_buffer_size 128k;
    proxy_buffers 4 256k;
    proxy_busy_buffers_size 256k;

    include /etc/nginx/snippets/authentik-forward-auth.conf;

    location / {
        # Активация Forward Auth
        auth_request /outpost.goauthentik.io/auth/nginx;
        error_page 401 = @authentik_redirect;

        # Извлечение метаданных из ответа Authentik Outpost
        auth_request_set $authentik_user $upstream_http_x_authentik_username;
        auth_request_set $authentik_email $upstream_http_x_authentik_email;

        proxy_pass https://portainer_backend;
        proxy_ssl_verify off; # Для самоподписанных сертификатов Portainer

        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-authentik-username $authentik_user;
        proxy_set_header X-authentik-email $authentik_email;

        # Полноценная поддержка WebSocket для терминалов Portainer
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
    }
}

3. Сквозной SSO для Grafana через Auth Proxy

Grafana обладает встроенным модулем auth.proxy, позволяющим автоматически авторизовывать пользователя и назначать ему роли на основании HTTP-заголовков, переданных доверенным прокси-сервером, полностью минуя форму ввода логина и пароля.

Внесите правки в конфигурационный файл grafana.ini или передайте параметры через переменные окружения в docker-compose.yml:

[auth.proxy]
enabled = true
header_name = X-authentik-username
header_property = username
auto_sign_up = true
sync_ttl = 60
whitelist = 127.0.0.1, 172.18.0.0/16
enable_touch_session = true

[users]
allow_sign_up = false
auto_assign_org = true
auto_assign_org_role = Viewer

Конфигурация Nginx для Grafana (/etc/nginx/conf.d/grafana.conf) транслирует проверенную сессию из Authentik:

# /etc/nginx/conf.d/grafana.conf

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

    ssl_certificate /etc/letsencrypt/live/grafana.infra.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/grafana.infra.example.com/privkey.pem;

    include /etc/nginx/snippets/authentik-forward-auth.conf;

    location / {
        auth_request /outpost.goauthentik.io/auth/nginx;
        error_page 401 = @authentik_redirect;

        auth_request_set $authentik_user $upstream_http_x_authentik_username;
        auth_request_set $authentik_email $upstream_http_x_authentik_email;

        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;

        proxy_set_header Host $http_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;

        # Передача доверенных заголовков в Grafana
        proxy_set_header X-authentik-username $authentik_user;
        proxy_set_header X-authentik-email $authentik_email;

        # WebSocket для Live Dashboard streaming
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

4. Разделение UI и API/Webhook для n8n

Платформа автоматизации n8n требует изоляции: административный веб-интерфейс должен быть закрыт единым входом Authentik, тогда как внешние хуки (/webhook/* и /webhook-test/*), принимающие события от внешних платформ (GitHub, Stripe, Telegram), обязаны принимать HTTP POST запросы напрямую без перенаправления на SSO.

# /etc/nginx/conf.d/n8n.conf

server {
    listen 443 ssl http2;
    server_name n8n.infra.example.com;

    ssl_certificate /etc/letsencrypt/live/n8n.infra.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/n8n.infra.example.com/privkey.pem;

    include /etc/nginx/snippets/authentik-forward-auth.conf;

    # 1. Открытые маршруты для вебхуков (прямой доступ)
    location ~ ^/(webhook|webhook-test|webhook-waiting)/ {
        proxy_pass http://127.0.0.1:5678;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # Увеличенный таймаут для длительных воркфлоу
        proxy_read_timeout 300s;
        proxy_send_timeout 300s;
    }

    # 2. Защищенный веб-интерфейс и редактор сценариев
    location / {
        auth_request /outpost.goauthentik.io/auth/nginx;
        error_page 401 = @authentik_redirect;

        auth_request_set $authentik_user $upstream_http_x_authentik_username;

        proxy_pass http://127.0.0.1:5678;
        proxy_http_version 1.1;

        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-authentik-username $authentik_user;

        # SSE / EventSource и WebSocket для обновления холста воркфлоу
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_buffering off;
        proxy_cache off;
        chunked_transfer_encoding off;
    }
}

5. Тюнинг стека TCP и минимизация latency p99 sub-request

Архитектурный недостаток Forward Auth — удвоение числа сетевых транзакций на каждый HTTP-запрос (каждый клик в браузере вызывает внутренний sub-request к Authentik Outpost). При интенсивной навигации пользователя это может приводить к исчерпанию пула эфемерных портов на локальной петле 127.0.0.1 и накоплению сокетов в состоянии TIME_WAIT.

Для удержания накладных расходов в пределах 1.5–2.5 мс по метрике latency p99 настройте пул постоянных соединений (keepalive) в блоке Nginx:

# /etc/nginx/conf.d/authentik_upstream.conf

upstream authentik_outpost_backend {
    server 127.0.0.1:9000;
    keepalive 64;
    keepalive_requests 10000;
    keepalive_timeout 60s;
}

И обновите директиву внутри /etc/nginx/snippets/authentik-forward-auth.conf:

location = /outpost.goauthentik.io/auth/nginx {
    internal;
    proxy_pass http://authentik_outpost_backend/outpost.goauthentik.io/auth/nginx;
    proxy_http_version 1.1;
    proxy_set_header Connection "";
    ...
}

На уровне ядра операционной системы Linux оптимизируйте параметры повторного использования сокетов и очереди подключений в /etc/sysctl.d/99-network-performance.conf:

# Быстрое переиспользование сокетов TIME_WAIT для локальных исходящих запросов
net.ipv4.tcp_tw_reuse = 1

# Расширение диапазона эфемерных портов
net.ipv4.ip_local_port_range = 10240 65535

# Глубина очереди неполных (SYN) и готовых (Accept) соединений
net.core.somaxconn = 8192
net.ipv4.tcp_max_syn_backlog = 8192

# Буферизация TCP сокетов (min default max)
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216

Примените параметры ядра без перезагрузки:

sysctl -p /etc/sysctl.d/99-network-performance.conf

При развертывании инфраструктуры на KVM-нодах tropic.host, где процессорная архитектура на базе современных ядер AMD EPYC и Ryzen 9 гарантирует полное отсутствие переподписки (CPU Steal Time %st = 0.0%), а накопители корпоративного класса PCIe 4.0 NVMe обеспечивают сверхнизкий джиттер дисковой подсистемы (IOPS > 50 000 на 4K QD1), внутренний вызов auth_request отрабатывает мгновенно. Локальная задержка верификации токена в памяти через Redis и Python ASGI не превышает 2–3 мс, обеспечивая мгновенный отклик интерфейсов защищаемых сервисов. На серверах с высоким CPU Steal (> 5–8%) задержка планировщика задач KVM приводит к росту задержки sub-request до 120–250 мс, вызывая визуальные микрофризы при загрузке SPA-приложений.

6. Валидация цепочки авторизации и журналов

Проверьте корректность конфигурации синтаксиса Nginx и перезапустите рабочий пул:

nginx -t && systemctl reload nginx

Для верификации работы сквозного потока выполните синтетический запрос к защищенному сервису через curl, инспектируя заголовки ответа и статус перенаправления:

curl -I -k https://portainer.infra.example.com

Корректный ответ неавторизованному клиенту должен содержать статус 302 Moved Temporarily со ссылкой на шлюз Authentik:

HTTP/2 302 
server: nginx
date: Sun, 11 Oct 2026 14:00:00 GMT
location: https://auth.example.com/outpost.goauthentik.io/start?rd=https://portainer.infra.example.com/
content-type: text/html

Отслеживайте журнал аутентификации в контейнере Authentik Server для контроля генерируемых заголовков:

docker compose logs -f --tail=50 server | grep "outpost.goauthentik.io"

При успешной авторизации лог фиксирует статус возврата 200:

{"auth_via": "session", "event": "Handled forward-auth request", "host": "portainer.infra.example.com", "level": "info", "logger": "authentik.outpost.proxy", "status": 200, "user": "devops_admin"}

Контроль прохождения запросов через Nginx с замером таймингов апстрима выполняется через форматированный вывод журнала доступа:

tail -f /var/log/nginx/access.log | grep --line-buffered "portainer"

Если в журнале фиксируется статус 500 Internal Server Error, проверьте лимиты буферов: при разрастании числа ролей и групп пользователя в Authentik размер cookie authentik_session может превысить стандартные 4 КБ, требуя расширения директивы proxy_buffer_size до 128k в блоке location / веб-сервера Nginx.

Политики безопасности: принудительный 2FA/MFA, WebAuthn Passkeys и защита от брутфорса

После настройки шлюзов аутентификации и проксирования запросов критически важно заблокировать векторы компрометации учетных записей. В стандартной конфигурации Authentik допускает вход по одной паре логин/пароль, что делает внутренний периметр уязвимым к атакам по словарям и перебору учетных данных (credential stuffing). Полноценная защита строится на декларативных цепочках Authentik (Flows и Stages), аппаратных токенах FIDO2/WebAuthn, поведенческих политиках на Python и низкоуровневой защите ядра Linux от L7-брутфорса.


1. Архитектура Flow и создание принудительного Enrollment-потока MFA

Authentik оперирует концепцией конвейера: Flow (поток) состоит из последовательности Stages (стадий), переходы между которыми регулируются Policies (политиками). Для принуждения всех пользователей к настройке второго фактора при первом входе создается специализированный поток регистрации MFA с последующей врезкой в основной поток аутентификации.

Шаг 1. Создание стадий настройки факторов

В веб-интерфейсе Authentik перейдите в раздел Flows & Stages $\rightarrow$ Stages: 1. Создайте стадию Authenticator TOTP Setup Stage: * Name: default-totp-setup /Digits: 6 (стандарт RFC 6238) * Step: 30 секунд * Algorithm: SHA-1 или SHA-256 (SHA-1 обеспечивает максимальную совместимость с Google Authenticator, Яндекс.Ключ и Aegis). 2. Создайте стадию Authenticator WebAuthn Setup Stage: * Name: default-webauthn-setup * User verification: preferred (допускает биометрию без принудительного PIN-кода на устройствах без TPM) * Resident key requirement: preferred (для поддержки Passkeys).

Шаг 2. Конфигурация Stage Prompt и изолированного потока Enrollment

Создайте новый поток типа Stage Configuration: * Name: custom-mfa-enrollment-flow * Title: Регистрация обязательного второго фактора * Designation: Stage Configuration

Внутри потока привяжите стадии выбора метода аутентификации через стадию-переключатель (Authenticator Validation/Selection Stage), объединяющую TOTP и WebAuthn. Это дает пользователю возможность привязать либо аппаратный ключ YubiKey/биометрический Passkey браузера, либо программный TOTP-токен.

Шаг 3. Создание Python Expression Policy для проверки наличия MFA

Чтобы поток запускался только для пользователей, у которых еще нет настроенного фактора, создайте политику Custom Python Policy в меню Customization $\rightarrow$ Policies:

# Политика валидации наличия активных аппаратных или программных факторов
from authentik.stages.authenticator_totp.models import TOTPDevice
from authentik.stages.authenticator_webauthn.models import WebAuthnDevice
from authentik.stages.authenticator_duo.models import DuoDevice
from authentik.stages.authenticator_static.models import StaticDevice

user = request.user

# Если пользователь анонимен, пропускаем оценку
if not user.is_authenticated:
    return False

# Запрос в PostgreSQL через Django ORM для проверки подтвержденных устройств
has_totp = TOTPDevice.objects.filter(user=user, confirmed=True).exists()
has_webauthn = WebAuthnDevice.objects.filter(user=user, confirmed=True).exists()
has_static = StaticDevice.objects.filter(user=user, confirmed=True).exists()

# Возвращаем True (триггер политики), если ни одного фактора не привязано
if not (has_totp or has_webauthn or has_static):
    return True

return False

Шаг 4. Врезка в основной поток авторизации (default-authentication-flow)

Перейдите в Flows $\rightarrow$ default-authentication-flow. Порядок привязки (Stage Bindings) должен выглядеть следующим образом:

  1. default-authentication-identification (Order: 10) — ввод логина/email.
  2. default-authentication-password (Order: 20) — проверка хеша пароля.
  3. custom-mfa-enrollment-binding (Order: 25):
  4. Target Stage: стадия вызова потока custom-mfa-enrollment-flow.
  5. Policy: привязанная Python Expression Policy.
  6. Re-evaluate policies: True (критично для динамической проверки состояния сессии).
  7. default-authentication-mfa-validation (Order: 30) — валидация второго фактора (запрашивается, если устройства уже зарегистрированы).
  8. default-authentication-login (Order: 100) — финализация сессии и генерация cookie authentik_session.

2. Интеграция WebAuthn Passkeys (FIDO2) и ограничения доменной зоны

Аппаратные ключи FIDO2 и связки WebAuthn Passkeys устраняют уязвимость к фишингу в реальном времени (AiTM-прокси типа Evilginx2), так как криптографическая подпись задачи (challenge) жестко привязывается к origin домена.

Для корректной работы WebAuthn в Docker-стеке Authentik соблюдайте строгие системные требования: * Криптографический контекст (Secure Context): Браузерное API navigator.credentials.create() и navigator.credentials.get() мгновенно блокируется браузерами на любых адресах без валидного TLS-сертификата (за исключением localhost). Сертификат Let's Encrypt или корпоративного CA обязателен. * Совпадение Relying Party ID (RP ID): Значение RP ID обязано совпадать с корневым или текущим доменом Authentik. Если инстанс доступен по адресу auth.example.com, RP ID должен быть auth.example.com либо родительским example.com.

В настройках стадии Authenticator WebAuthn Setup Stage задайте параметры:

Relying Party Entity Name: Infra SSO Gateway
Relying Party ID: auth.example.com
User Verification: preferred
Authenticator Attachment: cross-platform (для физических YubiKey) или unspecified (для Passkeys смартфона/macOS TouchID)
Resident Key: required

Параметр Resident Key: required обеспечивает создание Discoverable Credentials (Passkey): пользователь сможет авторизоваться вообще без ввода логина и пароля — Authentik определит пользователя по открытому ключу и идентификатору учетной записи внутри защищенного анклава токена.


3. Защита от брутфорса на уровне Authentik: Rate Limiting и Token Bucket

Authentik включает встроенный механизм ограничения частоты запросов, функционирующий на базе Redis через алгоритм Token Bucket. Конфигурация применяется непосредственно к стадии идентификации:

  1. Откройте Flows & Stages $\rightarrow$ Stages $\rightarrow$ default-authentication-identification.
  2. Найдите блок Password stage settings / Stage settings.
  3. Настройте директиву Rate Limiting:
  4. Requests per period: 5
  5. Period (seconds): 300 (блокировка перебора после 5 неверных попыток на 5 минут).
  6. Key generating function: ip_and_user (комбинирует IP-адрес источника и введенное имя учетной записи, что предотвращает DoS конкретного пользователя распределенным ботнетом, сохраняя локализацию блокировки атакующего IP).

Состояние счетчиков хранится в распределенном кеше Redis инстанса Authentik под ключами вида: authentik:core:rate_limit:ip_and_user:<hash>

Проверить текущее состояние rate-лимитов в Redis можно прямо из терминала сервера:

docker compose exec redis redis-cli KEYS "authentik:*rate_limit*"

4. Фильтрация L7-атак через Fail2ban на хостовой ОС

Встроенный Rate Limiting Authentik защищает базу данных от перегрузки, но процесс Django/Gunicorn продолжает обрабатывать TLS-хендшейки и входящие HTTP POST-запросы. Чтобы отсекать атакующих на уровне пакетного фильтра ядра Linux (nftables/iptables), свяжем журнал Authentik с хостовой службой fail2ban.

Шаг 1. Настройка фильтра Fail2ban для логов Authentik

Контейнер Authentik Server пишет события в формате JSON в общий stdout Docker. Создайте регулярное выражение для перехвата неудачных авторизаций:

# /etc/fail2ban/filter.d/authentik.conf
[INCLUDES]
before = common.conf

[Definition]
# Поиск событий с ошибкой авторизации и парсинг IP клиента
failregex = ^.*"event":\s*"Failed to authenticate",.*"client_ip":\s*"<HOST>".*$
ignoreregex =
datepattern = "timestamp":\s*"%%Y-%%m-%%dT%%H:%%M:%%S

Шаг 2. Привязка Jail к журналу Docker

Создайте конфигурацию изолятора, направив его на JSON-логи Docker daemon:

# /etc/fail2ban/jail.d/authentik.local
[authentik]
enabled = true
filter = authentik
logpath = /var/lib/docker/containers/*/*-json.log
backend = polling
port = 80,443
protocol = tcp
banaction = iptables-multiport[name=authentik, port="80,443", protocol=tcp]
maxretry = 5
findtime = 600
bantime = 86400
Внимание: Использование backend = polling или backend = systemd зависит от настроек логирования Docker daemon в /etc/docker/daemon.json. Если драйвер логирования установлен как journald, переключите logpath = на чтение через journalctl (backend = systemd, journalmatch = CONTAINER_NAME=authentik-server-1).

Перезапустите демон Fail2ban и проверьте активность правила:

systemctl restart fail2ban
fail2ban-client status authentik

5. Тюнинг сетевого стека ядра Linux и аппаратные метрики

При активных попытках подбора учетных данных алгоритмы хэширования паролей (в Authentik по умолчанию применяется Argon2id с параметрами памяти $m=65536$ КБ, итераций $t=3$, параллелизма $p=4$) создают экстремальную нагрузку на процессорную подсистему. Каждый параллельный POST-запрос на верификацию пароля захватывает до 64 МБ оперативной памяти и нагружает выделенные ядра CPU на 100% на протяжении 80–150 мс.

Если виртуализация сервера страдает оверселлингом вычислительных ресурсов, показатель CPU Steal Time (%st) резко возрастает. При значениях %st > 5% время генерации хеша Argon2id увеличивается с 90 мс до 1 200–2 500 мс, вызывая исчерпание пула воркеров Gunicorn/Celery и рост задержки $p99$ для всей инфраструктуры SSO.

При промышленной эксплуатации стека authentik docker compose установка vps должна осуществляться на надежной аппаратной базе. Инфраструктура KVM VPS от tropic.host предоставляет аппаратную виртуализацию без переподписки с гарантированным показателем CPU Steal Time %st = 0.0% на высокочастотных процессорах AMD EPYC и Ryzen 9. В сочетании с корпоративными серверными NVMe-накопителями PCIe 4.0 (производительность произвольного чтения 4K QD1 превышает 50 000 IOPS) это гарантирует отсутствие I/O-блокировок при постоянном сбросе WAL-логов PostgreSQL и транзакционных сессий Redis. Встроенная L3/L4 DDoS-защита и чистые выделенные IPv4-адреса отсекают паразитный флуд до того, как пакеты достигнут сетевого сокета хоста.

Для оптимизации прохождения сетевых пакетов и защиты сокетов от истощения памяти под нагрузкой внесите директивы в конфигурацию ядра Linux:

cat <<'EOF' > /etc/sysctl.d/99-authentik-security.conf
# Защита от переполнения очереди полуоткрытых соединений (SYN Flood)
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 8192
net.core.somaxconn = 65535

# Ускорение очистки сокетов в состоянии FIN-WAIT-2 и TIME-WAIT
net.ipv4.tcp_fin_timeout = 15
net.ipv4.tcp_tw_reuse = 1

# Предотвращение исчерпания памяти при атаках типа Slowloris
net.ipv4.tcp_keepalive_time = 300
net.ipv4.tcp_keepalive_intvl = 15
net.ipv4.tcp_keepalive_probes = 5

# Включение передового алгоритма контроля перегрузки Google BBR
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr
EOF

sysctl --system

Верифицируйте статус работы алгоритма BBR и размер очереди сокетов:

sysctl net.ipv4.tcp_congestion_control
ss -lnt '( sport = :443 or sport = :80 )'

Колонка Send-Q в выводе ss не должна превышать значение Listen-Backlog веб-сервера. Если Send-Q приближается к максимуму, увеличьте директиву backlog в конфигурации блока listen reverse-proxy Nginx/Traefik до значения 65535, устраняя отбрасывание входящих TLS-соединений на уровне TCP-стека.

Регламент Disaster Recovery: бэкап PostgreSQL, сохранение медиа и экспорт Blueprints

Архитектура Authentik разделяет состояние системы на три независимых слоя: реляционная СУБД PostgreSQL (пользовательская база, сопоставления групп, криптографические сертификаты, токены сессий и настройки стадий аутентификации), персистентный том медиа-файлов (пользовательские аватары, брендинг, кастомные иконки приложений) и конфигурационный контекст окружения. Кэш Redis и очереди Celery являются эфемерными: потеря данных в оперативной памяти Redis не нарушает целостность identity-провайдера, так как брокер задач и кэш-инвалидация автоматически пересоздаются worker-процессами при старте.

Критическим фактором сохранения консистентности является приватный ключ шифрования AUTHENTIK_SECRET_KEY. Authentik использует его для симметричного шифрования секретов клиентских приложений (OAuth2/OIDC Client Secrets, SAML Private Keys, сервисные токены) в таблицах PostgreSQL через алгоритм AES-GCM. Восстановление дампа базы данных без идентичного значения AUTHENTIK_SECRET_KEY приводит к необратимой потере доступа к настроенным интеграциям: воркеры Authentik начнут генерировать исключения дешифрования cryptography.exceptions.InvalidTag при каждой попытке вызова внешнего провайдера.

Автоматизированный скрипт создания атомарного бэкапа

Создание снимка базы данных в рабочей среде должно происходить в режиме строгой изоляции транзакций без блокировки таблиц на запись (READ COMMITTED / REPEATABLE READ). Вызов утилиты pg_dump выполняется непосредственно внутри контейнера СУБД с компрессией в кастомный бинарный формат (-Fc), что позволяет при восстановлении задействовать параллельные потоки (pg_restore -j).

Создайте на хосте рабочий каталог для скриптов обслуживания:

install -d -m 0700 /opt/authentik/scripts /opt/authentik/backups
cat <<'EOF' > /opt/authentik/scripts/backup-authentik.sh
#!/usr/bin/env bash
set -Eeuo pipefail

# Конфигурация путей и окружения
AUTH_DIR="/opt/authentik"
BACKUP_DIR="${AUTH_DIR}/backups"
TIMESTAMP="$(date +'%Y%m%d_%H%M%S')"
BACKUP_NAME="authentik_backup_${TIMESTAMP}"
WORK_DIR="/tmp/${BACKUP_NAME}"
RETENTION_DAYS=14

# GPG-пассфраза для шифрования (в production используйте закрытый ключ или AWS KMS/HashiCorp Vault)
GPG_PASSPHRASE_FILE="${AUTH_DIR}/.backup_passphrase"

# Загрузка переменных окружения Docker Compose
if [[ ! -f "${AUTH_DIR}/.env" ]]; then
    echo "[CRITICAL] Файл ${AUTH_DIR}/.env не найден!" >&2
    exit 1
fi

set -a
# shellcheck disable=SC1091
source "${AUTH_DIR}/.env"
set +a

install -d -m 0700 "${WORK_DIR}"

trap 'rm -rf "${WORK_DIR}"' EXIT INT TERM

echo "[INFO] Запуск резервного копирования Authentik: ${TIMESTAMP}"

# 1. Дамп PostgreSQL в кастомном бинарном формате с контролем транзакций
PG_DUMP_FILE="${WORK_DIR}/postgres_${TIMESTAMP}.dump"
echo "[INFO] Создание согласованного дампа базы данных PostgreSQL..."

docker compose -f "${AUTH_DIR}/docker-compose.yml" exec -T postgresql \
    pg_dump \
    -U "${PG_USER:-authentik}" \
    -d "${PG_DB:-authentik}" \
    -F c \
    -b \
    -v \
    --no-owner \
    --no-privileges \
    > "${PG_DUMP_FILE}"

# Проверка ненулевого размера дампа
if [[ ! -s "${PG_DUMP_FILE}" ]]; then
    echo "[CRITICAL] Дамп базы данных пуст или завершился с ошибкой!" >&2
    exit 2
fi

# 2. Архивирование персистентного тома media
MEDIA_ARCHIVE="${WORK_DIR}/media_${TIMESTAMP}.tar.gz"
echo "[INFO] Архивация пользовательских медиа-файлов..."
if [[ -d "${AUTH_DIR}/media" ]]; then
    tar -czf "${MEDIA_ARCHIVE}" -C "${AUTH_DIR}" media
else
    # Создаем пустой маркер, если директория не вынесена на хост
    tar -czf "${MEDIA_ARCHIVE}" -T /dev/null
fi

# 3. Сохранение конфигурации окружения и Blueprints
CONFIG_ARCHIVE="${WORK_DIR}/config_${TIMESTAMP}.tar.gz"
echo "[INFO] Упаковка конфигурационных манифестов..."
tar -czf "${CONFIG_ARCHIVE}" \
    -C "${AUTH_DIR}" \
    .env \
    docker-compose.yml \
    $(test -d "${AUTH_DIR}/custom-templates" && echo "custom-templates") \
    $(test -d "${AUTH_DIR}/blueprints" && echo "blueprints")

# 4. Формирование манифеста контрольных сумм SHA-256
cd "${WORK_DIR}"
sha256sum "postgres_${TIMESTAMP}.dump" "media_${TIMESTAMP}.tar.gz" "config_${TIMESTAMP}.tar.gz" > "SHA256SUMS"

# 5. Сборка единого архива и шифрование через AES-256 (GPG)
FINAL_ARCHIVE="${BACKUP_DIR}/${BACKUP_NAME}.tar"
tar -cf "${FINAL_ARCHIVE}" -C "${WORK_DIR}" .

echo "[INFO] Шифрование резервного архива..."
if [[ -f "${GPG_PASSPHRASE_FILE}" ]]; then
    gpg --batch --yes --symmetric --cipher-algo AES256 \
        --passphrase-file "${GPG_PASSPHRASE_FILE}" \
        --output "${FINAL_ARCHIVE}.gpg" \
        "${FINAL_ARCHIVE}"
    rm -f "${FINAL_ARCHIVE}"
    FINAL_TARGET="${FINAL_ARCHIVE}.gpg"
else
    echo "[WARN] GPG-пассфраза не найдена. Архив сохранен без шифрования!"
    FINAL_TARGET="${FINAL_ARCHIVE}"
fi

chmod 0600 "${FINAL_TARGET}"

# 6. Ротация локальных архивов старше указанного интервала
find "${BACKUP_DIR}" -type f -name "authentik_backup_*.tar*" -mtime "+${RETENTION_DAYS}" -delete

echo "[SUCCESS] Бэкап успешно сформирован: ${FINAL_TARGET}"
EOF

chmod 0700 /opt/authentik/scripts/backup-authentik.sh

Сгенерируйте криптостойкую пассфразу для симметричного шифрования дампа с правами доступа исключительно для суперпользователя:

openssl rand -base64 32 > /opt/authentik/.backup_passphrase
chmod 0400 /opt/authentik/.backup_passphrase

При интенсивной аутентификации пользователей и частой генерации токенов фоновый pg_dump создает пиковую нагрузку на подсистему дискового ввода-вывода. На стандартных shared-хостингах с переподпиской дисков (overselling) I/O wait приводит к росту показателя CPU Steal Time (%st), вызывая деградацию задержки обработки сетевых пакетов ядра Linux и рост p99 latency SSO-авторизации с 15 мс до 2 000+ мс.

На виртуальных серверах платформы tropic.host за счет аппаратной виртуализации KVM паразитный CPU Steal Time исключен на уровне гипервизора (%st = 0.0%). Серверные накопители NVMe PCIe 4.0 корпоративного класса (IOPS на операциях произвольного чтения 4K QD1 превышают 50 000) выполняют полный сброс буферов транзакций PostgreSQL на накопитель без образования дисковых очередей, исключая лаги в работе Identity Provider во время резервного копирования.

Для репликации зашифрованных дампов в удаленное объектное хранилище (S3/Wasabi/MinIO) подключите отправку через rclone или aws-cli:

cat <<'EOF' >> /opt/authentik/scripts/backup-authentik.sh

# 7. Отправка в удаленное S3-совместимое хранилище
if command -v aws >/dev/null 2>&1; then
    echo "[INFO] Выгрузка архива в удаленный S3-бакет..."
    aws s3 cp "${FINAL_TARGET}" "s3://${S3_BACKUP_BUCKET}/authentik/${BACKUP_NAME}.tar.gpg" \
        --storage-class STANDARD_IA
fi
EOF

Автоматизация через systemd Timer вместо Cron

Использование связки systemd.service и systemd.timer предпочтительнее классического демона cron. Конфигурация через systemd обеспечивает изоляцию процесса в отдельном слайсе cgroups v2, логирование всех потоков STDOUT/STDERR в journald, а также задержку выполнения (RandomizedDelaySec) для предотвращения одновременного запуска бэкапов на пуле нод.

Создайте служебный юнит:

cat <<'EOF' > /etc/systemd/system/authentik-backup.service
[Unit]
Description=Authentik Database and Configuration Hot Backup
After=docker.service
Requires=docker.service

[Service]
Type=oneshot
User=root
WorkingDirectory=/opt/authentik
ExecStart=/opt/authentik/scripts/backup-authentik.sh
StandardOutput=journal
StandardError=journal
# Ограничение потребления ресурсов через cgroups v2
CPUQuota=50%
MemoryMax=1G
TasksMax=100
IOSchedulingClass=idle
CPUSchedulingPolicy=batch

[Install]
WantedBy=multi-user.target
EOF

Создайте таймер с ежедневным запуском в 03:00 ночи по времени хоста:

cat <<'EOF' > /etc/systemd/system/authentik-backup.timer
[Unit]
Description=Daily Authentik Backup Timer

[Timer]
OnCalendar=*-*-* 03:00:00
RandomizedDelaySec=1800
Persistent=true

[Install]
Timers.target
EOF

systemctl daemon-reload
systemctl enable --now authentik-backup.timer

Проверьте статус и расписание следующего выполнения:

systemctl list-timers authentik-backup.timer

Декларативный экспорт инфраструктуры: Authentik Blueprints

Authentik поддерживает подход Configuration-as-Code через механизм Blueprints. Blueprints представляют собой сериализованные YAML-манифесты, описывающие потоки аутентификации (Flows), стадии (Stages), политики доступа (Policies), свойства провайдеров (Proxy, OAuth2/OIDC, SAML, LDAP) и привязанные приложения. Экспорт Blueprints позволяет восстановить архитектуру доступа на чистом инстансе даже в случае полной потери дампа базы данных или при миграции между major-версиями.

Выполните экспорт всех кастомных объектов инфраструктуры через CLI-утилиту Authentik внутри работающего контейнера:

# Создание директории для хранения экспортированных манифестов
install -d -m 0755 /opt/authentik/blueprints

# Экспорт сущностей через служебную команду внутреннего интерфейса Authentik
docker compose -f /opt/authentik/docker-compose.yml exec -T server \
    ak export_blueprint \
    --include-all \
    --format yaml \
    > /opt/authentik/blueprints/cluster-state-export.yaml

Проверьте структуру полученного манифеста. Он должен содержать блоки version: 1, метаданные и манифесты контекстов:

head -n 25 /opt/authentik/blueprints/cluster-state-export.yaml

Для автоматического применения манифеста при холодном старте системы на новом VPS достаточно положить файл в смонтированную директорию ./blueprints/ и передать его имя в переменную окружения AUTHENTIK_BOOTSTRAP_BLUEPRINT=cluster-state-export.yaml в файле .env.


Пошаговый сценарий Disaster Recovery (Восстановление из архива)

Рассмотрим сценарий полного отказа инфраструктуры (Bare Metal Recovery), когда текущий сервер недоступен, а развертывание выполняется на новом KVM-сервере tropic.host под управлением чистой ОС Ubuntu 24.04 LTS.

┌─────────────────────────────────────────────────────────────────┐
│              Disaster Recovery Pipeline (RTO < 5 мин)           │
└────────────────────────────────┬────────────────────────────────┘
                                 │
                                 ▼
┌─────────────────────────────────────────────────────────────────┐
│ 1. Скачивание артефакта из S3 и верификация SHA-256             │
└────────────────────────────────┬────────────────────────────────┘
                                 │
                                 ▼
┌─────────────────────────────────────────────────────────────────┐
│ 2. Расшифровка GPG (AES-256) и извлечение .env / сертификатов   │
└────────────────────────────────┬────────────────────────────────┘
                                 │
                                 ▼
┌─────────────────────────────────────────────────────────────────┐
│ 3. Холодный запуск только контейнера postgresql                 │
└────────────────────────────────┬────────────────────────────────┘
                                 │
                                 ▼
┌─────────────────────────────────────────────────────────────────┐
│ 4. Очистка старой схемы и параллельный импорт (pg_restore -j)   │
└────────────────────────────────┬────────────────────────────────┘
                                 │
                                 ▼
┌─────────────────────────────────────────────────────────────────┐
│ 5. Запуск server/worker и верификация криптографических ключей  │
└─────────────────────────────────────────────────────────────────┘

Шаг 1: Подготовка нового хоста и извлечение данных

Установите Docker Engine и сопутствующие сетевые утилиты:

apt-get update && apt-get install -y docker.io docker-compose-v2 gpg tar curl jq
install -d -m 0700 /opt/authentik /opt/authentik/restore

Скопируйте зашифрованный архив бэкапа и пассфразу на целевой сервер в директорию /opt/authentik/restore/.

Расшифруйте контейнер и распакуйте вложенные компоненты:

cd /opt/authentik/restore

# Расшифровка GPG
gpg --batch --yes --decrypt \
    --passphrase-file /opt/authentik/.backup_passphrase \
    --output backup_payload.tar \
    authentik_backup_*.tar.gpg

# Извлечение компонентов во временную директорию
tar -xf backup_payload.tar

Шаг 2: Валидация целостности данных (Integrity Check)

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

sha256sum -c SHA256SUMS

В выводе для всех трех файлов (postgres_*.dump, media_*.tar.gz, config_*.tar.gz) должен присутствовать статус OK. При несовпадении контрольной суммы процесс прерывается — поврежденный архив нельзя восстанавливать в СУБД.

Шаг 3: Восстановление конфигурации и файловых структур

Разверните конфигурацию и медиа-файлы в рабочий каталог /opt/authentik:

# Восстановление .env и docker-compose.yml
tar -xzf config_*.tar.gz -C /opt/authentik/

# Восстановление медиа-файлов
tar -xzf media_*.tar.gz -C /opt/authentik/

# Корректировка владельца для медиа-каталога (UID 1000 использует процесс Authentik)
chown -R 1000:1000 /opt/authentik/media

Убедитесь, что файл /opt/authentik/.env содержит корректные параметры:

grep -E '^(AUTHENTIK_SECRET_KEY|PG_PASS|AUTHENTIK_POSTGRESQL)' /opt/authentik/.env

Шаг 4: Запуск изолированного инстанса PostgreSQL

Запустите исключительно контейнер базы данных. Серверные службы server и worker не должны запускаться до завершения накатки дампа, чтобы предотвратить выполнение параллельных автомиграций Django на незаполненной базе:

cd /opt/authentik
docker compose up -d postgresql

Ожидайте готовности сокета СУБД к приему сетевых соединений. Для проверки используйте системную утилиту pg_isready:

until docker compose exec -T postgresql pg_isready -U authentik -d authentik; do
    echo "[WAIT] Ожидание инициализации сокета PostgreSQL..."
    sleep 2
done

Шаг 5: Накатка дампа базы данных через pg_restore

Скопируйте бинарный дамп внутрь контейнера или перенаправьте поток через пайплайн. Используйте флаги --clean и --if-exists для безопасной перезаписи существующих таблиц, а также флаг -e (--exit-on-error) для фиксации сбоев импорта:

DUMP_FILE=$(ls /opt/authentik/restore/postgres_*.dump)

echo "[INFO] Восстановление базы данных из ${DUMP_FILE}..."

# Очистка текущей схемы и импорт дампа
docker compose exec -T postgresql \
    pg_restore \
    -U authentik \
    -d authentik \
    --clean \
    --if-exists \
    --no-owner \
    --no-privileges \
    --verbose \
    < "${DUMP_FILE}"
[!NOTE] В выводе команды допустимы предупреждающие сообщения об отсутствии удаляемых объектов (table does not exist) в блоке предстартовой очистки --clean. Критическими являются ошибки FATAL: или сбои целостности связей внешних ключей (foreign key constraint violation).

Шаг 6: Запуск полного стека Authentik и верификация

После завершения импорта схемы запустите кэш Redis, сервер приложений и воркеры фоновых задач:

docker compose up -d

Отслеживайте процедуру запуска компонентов:

docker compose logs -f --tail=100 server worker

В журнале должны отсутствовать сообщения о сбоях ключа шифрования:

docker compose logs server | grep -iE "(crypto|secret_key|invalidtag)" || echo "[PASS] Ошибок расшифровки секретов не обнаружено."

Шаг 7: Аудит целостности сущностей identity-провайдера

Выполните техническую валидацию восстановленного инстанса на уровне базы данных и внутреннего API:

  1. Проверка количества объектов через CLI PostgreSQL:
docker compose exec -T postgresql psql -U authentik -d authentik -c "
SELECT 
    (SELECT COUNT(*) FROM authentik_core_user) AS total_users,
    (SELECT COUNT(*) FROM authentik_core_application) AS total_apps,
    (SELECT COUNT(*) FROM authentik_providers_oauth2_oauth2provider) AS oauth_providers,
    (SELECT COUNT(*) FROM authentik_flows_flow) AS total_flows;
"

Количество пользователей и приложений должно в точности совпадать с метриками исходной ноды до сбоя.

  1. Проверка работоспособности эндпоинта аутентификации:

Выполните проверку доступности потока авторизации через локальный сокет, замеряя код ответа и сетевую задержку:

curl -s -o /dev/null -w "HTTP Status: %{http_code} | Total Time: %{time_total}s\n" \
    -H "Host: auth.yourdomain.com" \
    http://127.0.0.1:9000/if/flow/default-authentication-flow/

При корректном восстановлении ответ возвращает код HTTP Status: 200 или 302, а время ответа time_total на NVMe-дисках серверов tropic.host составляет менее 0.025s (25 мс). Значение HTTP Status: 500 свидетельствует о несовпадении секрета AUTHENTIK_SECRET_KEY или повреждении кэша сессий в СУБД.

  1. Очистка временных файлов восстановления:

После успешной верификации удалите незашифрованные распакованные файлы дампа, содержащие конфиденциальные данные пользователей:

rm -rf /opt/authentik/restore

Процесс Disaster Recovery завершен. За счет детерминированного развертывания через Docker Compose и отсутствия узких мест в производительности дисковой подсистемы время полного восстановления сервиса (RTO — Recovery Time Objective) не превышает 3–5 минут, а точка восстановления (RPO — Recovery Point Objective) ограничивается интервалом последнего запуска systemd-таймера.

Часто задаваемые вопросы (FAQ)

Почему Authentik требует меньше ресурсов, чем Keycloak?

Authentik построен на Go (высокопроизводительный прокси-слой Outpost) и Python, в то время как Keycloak работает на тяжелом Java Quarkus JVM с высоким постоянным аппетитом к памяти.

Что такое Forward Auth и как он защищает сервисы?

Forward Auth перехватывает все HTTP-запросы на уровне Nginx. Если сессия пользователя не авторизована в Authentik, запрос блокируется до успешного прохождения SSO и 2FA.