Files
knowledge-base/decisions/2026-06-26-authentik-sso-deploy.md
dttb d39ddb989d authentik SSO/IdP на auth.dttb.ru (LXC 144): пилот OIDC Open WebUI+Gitea, обязательная 2FA
- LXC 144 Debian12/Docker, server+worker+postgres (2026.5.3, без Redis)
- NPM #41 auth.dttb.ru, LE cert id133, публичная A-запись Spaceweb
- OIDC: Open WebUI (chat) + Gitea (git, ROOT_URL→https)
- 2FA обязательна на IdP (TOTP/WebAuthn force-enroll)
- принцип «2FA через нужную дверь» (OIDC, не forward-auth)
- критичную инфру не трогали; остался ручной enrollment Олега

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 19:17:10 +03:00

85 lines
8.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
date: 2026-06-26
type: decision
tags: [dttb, sso, authentik, oidc, mfa, security]
---
# authentik — SSO/IdP для home lab (LXC 144)
> **Статус:** платформа развёрнута и здорова, пилот OIDC на Open WebUI + Gitea работает (серверная часть). Остался один ручной шаг Олега — первый вход + enrollment 2FA (см. ниже).
> **Домен:** https://auth.dttb.ru · **Контейнер:** LXC 144 (10.0.0.144)
## Зачем
Единый вход (SSO) + обязательная 2FA на сервисы home lab. Self-hosted IdP, без облака — в линию с уходом от big-tech [[project_deapple_migration]].
## Ключевое архитектурное решение: «2FA на всё, но через нужную дверь»
Олег хотел завести **все** сервисы за SSO с 2FA. Лобовой «всё за forward-auth» отвергнут — сломал бы не-браузерные клиенты (которых у нас много). Принято разделение:
| Схема | Для чего | Почему |
|---|---|---|
| **OIDC/SAML** (вход в веб-морду через authentik+2FA, машинные пути живут штатно) | сервисы с родной поддержкой SSO: Gitea, Nextcloud, Proxmox, Portainer, Open WebUI, Grafana, Home Assistant | не ломает API, git push, WebDAV, мобилки |
| **Forward-auth** (outpost + advanced-config сниппет в NPM) | голые дашборды без логина / на Basic Auth (german, KasmVNC) | NPM не имеет родного auth-поля |
| **Родной 2FA** | Proxmox (встроенный TOTP/WebAuthn), NPM | не закрывать себе путь восстановления через IdP |
| **Не за SSO вообще** | API (OmniRoute omni:20128/v1), webhooks (bot.dttb.ru), не-HTTP (RustDesk, IMAP/SMTP, Matrix federation), Vaultwarden (свой мастер-пароль+2FA) | forward-auth их убьёт / антипаттерн |
**Почему forward-auth-везде плох именно у нас:** Nextcloud (мобилка/WebDAV/**rclone-bisync KB-синк**), Gitea (git push), OmniRoute API (openclaw/german/swarmclaw/open-webui/code-server), bot webhooks, Mailcow IMAP/SMTP, RustDesk 21115-21119, Home Assistant/Matrix/Plex клиенты — всё это не проходит SSO-редирект.
## Развёртывание (что сделано)
### Контейнер LXC 144
- Debian 12, unprivileged + nesting/keyctl, onboot=1
- 2 vCPU / 4 GB / swap 2 GB / rootfs 20 GB на storage `work` (local-lvm забит 94%)
- IP статика **10.0.0.144/24**, gw 10.0.0.1, **nameserver 1.1.1.1** (против FakeIP при pull с ghcr — урок [[2026-06-22-open-webui-deploy]])
- root-пароль LXC: см. credentials
- Docker 29.6 + compose (official get.docker.com)
### Стек (официальный compose, `/opt/authentik/`)
- `ghcr.io/goauthentik/server:2026.5.3`**server + worker + postgresql:16-alpine**
- ⚠️ **2026.5.3 ушёл от Redis** — теперь postgres-backed cache (django_postgres_cache). Compose БЕЗ redis. Проще.
- AVX-граблю (как Mongo для UniFi [[2026-06-15-unifi-controller-homelab]]) authentik **не задевает** — Python/PG/нет Mongo.
- `.env`: `PG_PASS`, `AUTHENTIK_SECRET_KEY` (сгенерированы), `AUTHENTIK_BOOTSTRAP_PASSWORD`/`_EMAIL`, порты 9000/9443. Бэкап секретов — в самом `.env` контейнера.
- Миграции БД при первом старте ~3 мин (health 503 в это время — норма, не паниковать).
### Домен auth.dttb.ru
- **Публичная A-запись** добавлена на Spaceweb: `auth → 176.62.183.186` (нет wildcard — у каждого поддомена своя запись).
- NPM proxy host **id 41**`10.0.0.144:9000`, **LE cert id 133**, Force SSL + HTTP/2 + WSS.
- Локально/в NetBird `*.dttb.ru` = 10.0.0.195 (wildcard на роутере) — SSO работает и внутри.
### 2FA — обязательна на уровне IdP
Стейдж `default-authentication-mfa-validation` переведён `not_configured_action: skip → configure`, `configuration_stages = [TOTP-setup, WebAuthn-setup]`. Любой вход без устройства **принуждает** к установке TOTP или passkey. Действует на ВСЕ OIDC-логины (они идут через default-authentication-flow).
### Пилот OIDC (работает серверно)
| Сервис | authentik app/slug | client_id | redirect_uri | как подключён |
|---|---|---|---|---|
| **Open WebUI** (chat.dttb.ru, LXC 142) | `open-webui` | `1G7PLkPU…` | `https://chat.dttb.ru/oauth/oidc/callback` | контейнер пересоздан с OAuth env, скрипт `/root/recreate-owui.sh`. Локальный логин СОХРАНЁН (`ENABLE_LOGIN_FORM` не трогали), `OAUTH_MERGE_ACCOUNTS_BY_EMAIL=true` |
| **Gitea** (git.dttb.ru, LXC 136) | `gitea` | `AYl8jNZv…` | `https://git.dttb.ru/user/oauth2/authentik/callback` | auth source «authentik» (id 1) через `gitea admin auth add-oauth`. **ROOT_URL изменён** `http://10.0.0.189:3000/ → https://git.dttb.ru/` (иначе callback не совпадал). Бэкап `app.ini.bak-preauthentik-20260626`. git push/SSH не затронуты |
Секреты client_secret — в credentials.
## ⚠️ Остался ручной шаг Олега (1 раз)
SSO-вход нельзя докрутить headless — нужен браузер Олега:
1. Зайти на **https://auth.dttb.ru**, логин `akadmin` / пароль (см. credentials).
2. Система **сама предложит установить 2FA** (TOTP — отсканировать QR в Google Authenticator/2FAS, или passkey). Установить.
3. Проверить вход через SSO: на chat.dttb.ru кнопка «Continue with authentik», на git.dttb.ru «Sign in with authentik».
4. (Опц.) создать личного пользователя `oleg` вместо akadmin, akadmin оставить как break-glass.
## Плюсы / минусы
**Плюсы:** единый вход + 2FA на все совместимые сервисы; централизованное управление (можно выдавать семье/клиентам скоуп-доступ); forward-auth прикроет голые панели; self-hosted; журнал входов.
**Минусы/риски (и митигация):**
- **SPOF/lockout:** упал authentik → нет SSO-входа. → критичную инфру (Proxmox/NPM/Vaultwarden) НЕ гейтим, у сервисов сохраняем родной admin-логин как fallback.
- **Публичный IdP = поверхность атаки.** → обязательная 2FA (включена), сильные пароли. Опц. позже: geo/fail2ban, ограничить admin LAN-only.
- **Лок единственного админа** при кривом MFA-flow. → recovery через API-токен (минует flow) и `ak shell` (см. ниже).
## Recovery / управление
- API-токен (минует login-flow): `claude-bootstrap` — в credentials. `curl https://auth.dttb.ru/api/v3/core/users/me/ -H "Authorization: Bearer <tok>"`.
- Сбросить пароль akadmin: `pct exec 144 -- bash -c "cd /opt/authentik && echo 'akadmin <newpass>' | docker compose exec -T server ak change_password akadmin"` (или через `ak shell`).
- Снять обязательную 2FA (если залочило): PATCH стейджа `f3808685-…` `not_configured_action=skip` через API-токен.
- Логи: `cd /opt/authentik && docker compose logs server|worker`.
- Обновление: бампить `AUTHENTIK_TAG` в `/opt/authentik/.env``docker compose pull && up -d`.
## Spaceweb DNS — найден read-метод API
К [[../projects/dttb/spaceweb-dns]]: метод чтения зоны = **`info`** (`params {domain}`), возвращает все записи с `index`/`category`. Существующие методы: `editMain`, `editMx`, `editTxt`, `info`. Добавление одной A-записи `editMain {action:"add",name,type:"A",value,prefix:""}` — безопасно (ломает зону только цикл).
## Дальше (отдельными задачами, по согласованию)
Полный OIDC-роллаут: Proxmox (realm OpenID Connect), Nextcloud (app `user_oidc`/`sociallogin`), Portainer, Home Assistant, Grafana. Forward-auth demo на german.dttb.ru (Basic Auth → outpost). См. раздел «нужная дверь» выше.