Per-host group gating in Caddy from X-Authentik-Groups; Discord roles -> Authentik groups policy; domain-level forward auth documented

BOT_REQUIRE_GROUP / LIVE_REQUIRE_GROUP in .env drive a header_regexp matcher after forward_auth
(403 before the app). authentik/discord-roles-to-groups.py syncs Discord roles into groups at
every login so access follows Discord roles without manual edits.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
2026-10-30 23:22:20 -05:00
parent bee53f2718
commit 384a7ed552
6 changed files with 96 additions and 20 deletions

View File

@@ -208,22 +208,25 @@ the 500 Mbps line. For a crowd, restream to Twitch from OBS.
## 5g. Authentik forward-auth for the Caddy-gated hosts (`live.`, `bot.`; later the main host)
Caddy's `forward_auth` asks Authentik's **embedded outpost** (127.0.0.1:9000) on every request. The
generated Caddy blocks work with either Authentik mode below; nothing in Caddy changes.
Design: **Authentik authenticates once for the whole domain; Caddy authorizes per host** from the
`X-Authentik-Groups` header the outpost returns; **Discord roles fill the Authentik groups** at login.
**Recommended: one domain-level provider for all hosts** (one login, one cookie, new hosts need no Authentik work):
1. Applications → Outposts → edit "authentik Embedded Outpost" → config `authentik_host: https://auth.HOST`.
2. Applications → Create with provider → Application `Vanguard sites`; **Proxy Provider**, authorization flow
implicit-consent, mode **Forward auth (domain level)**, authentication URL `https://auth.HOST`,
cookie domain `HOST's parent domain` (e.g. `dumpstervigilantes.rip`).
3. Edit the Embedded Outpost → Applications → add `Vanguard sites` → Update.
4. Test in a private window: `https://live.HOST/` → Discord login → portal; `https://bot.HOST/` opens with no
second login; `https://live.HOST/me/stream-key` shows your username.
Trade-off: one access policy for every gated host (the guild policy at login already limits it to server
members). A host that must be restricted to a group gets its own **single-application** Proxy Provider
(external host `https://x.HOST`, added to the outpost) and enforces nothing else; the bot web UI also
authorizes by the `X-Authentik-Groups` header itself.
cookie domain = the parent domain (e.g. `dumpstervigilantes.rip`). Edit the Embedded Outpost →
Applications → add it → Update. One login now covers every gated host.
3. Per-host group requirement = `.env`: `BOT_REQUIRE_GROUP=bot-users` (default), `LIVE_REQUIRE_GROUP=`
(empty = any logged-in member). `./configure.py` apply regenerates the Caddy blocks; a user outside the
group gets a 403 from Caddy before the app sees the request.
4. Discord roles → groups: Directory → Groups: create `bot-users` (and keep `streamers`, `editors`, `admins`).
Discord source → Scopes: `guilds guilds.members.read`. Customization → Policies → Expression Policy
`discord-roles-to-groups` from `authentik/discord-roles-to-groups.py` (fill in the server ID and the
role-id → group table). Bind it to `default-source-enrollment` **and** `default-source-authentication`
(engine mode `all`, after `discord-guild-member`). From then on giving someone a Discord role gives them
the matching access on next login; removing it removes it.
5. Test in a private window: `https://live.HOST/` → Discord login → portal; `https://bot.HOST/` → 403 unless
you hold the bot role; `https://live.HOST/me/stream-key` shows your username.
Jellyfin and Gitea use OIDC providers instead, not this.