From ea0bba3023ee6eb89607e3340e2b08b3fc663468 Mon Sep 17 00:00:00 2001 From: Frank Date: Tue, 1 Sep 2026 17:43:15 -0700 Subject: [PATCH] docs(authentik): outpost claim-cache gotcha (proxysession) + Containers data paths --- .../Infrastructure/Authentik.md | 37 +++++++++++++++++-- serverAppConfigs-docs/log.md | 1 + 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/serverAppConfigs-docs/Infrastructure/Authentik.md b/serverAppConfigs-docs/Infrastructure/Authentik.md index 98d9df6..146a2a9 100644 --- a/serverAppConfigs-docs/Infrastructure/Authentik.md +++ b/serverAppConfigs-docs/Infrastructure/Authentik.md @@ -70,8 +70,9 @@ contract: (`server` / `worker`). - The worker runs as `0:0` (rootless: container root maps to the host user that owns the data dir). -- Data persists under `/home/dank/authentik/` (bind mounts, repo policy — no - named volumes). +- Data persists under `/home/dank/Containers/authentik/{data,postgres}` + (bind mounts, repo policy — no named volumes; moved from `/home/dank/authentik/` + in the 2026-09-01 quadlet standardization). ## Caddy integration @@ -232,10 +233,39 @@ Two secrets live in the host `.env` (git-ignored; placeholders in probes this). - **Logs:** `podman logs authentik-server` / `podman logs authentik-worker`. - **Backups:** `pg_dump` the `authentik-postgres` database (the single source of - truth). The `/home/dank/authentik/` dir holds media + static files. + truth). The `/home/dank/Containers/authentik/data` dir holds media + static files. - **Upgrades:** bump the image tag in `dankclaw/compose.yaml`, run `./scripts/deploy.sh dankclaw`. Authentik migrates the schema on startup. +## Outpost claim cache (`proxysession`) — 403 after a group add + +The embedded outpost does not re-evaluate group membership per request. Each +completed authorize writes one row to `authentik_providers_proxy_proxysession` +whose `session_data` JSON holds the claims (`groups` included) that get served +for that browser session until the row expires (observed TTLs 1–24 h per +row). A session authorized **before** a user was added to a group keeps +serving the old group list — Caddy-side RBAC header gates then 403 despite +correct membership, and no authentik event is generated (the 403 happens at +Caddy; authentik never sees the request). + +Diagnosis + fix (verified live 2026-09-01): + +```sql +-- who holds which cached groups (run in the authentik-postgres container) +SELECT session_data->'claims'->>'preferred_username' AS user, + session_data->'claims'->>'groups' AS cached_groups, + expires +FROM authentik_providers_proxy_proxysession ORDER BY expires; +-- invalidate one user's cache; their authentik login session is untouched, +-- so the next visit re-authorizes silently and mints fresh claims +DELETE FROM authentik_providers_proxy_proxysession +WHERE session_data->'claims'->>'preferred_username' = 'akadmin'; +``` + +The silent re-auth path was proven end-to-end: after deleting +`qa-e2e-admin`'s rows, the e2e admin spec ran against the stale storage +state and passed with no login prompt (2 tests, 2.9 s). + ## Failure modes | Symptom | Cause | Fix | @@ -247,6 +277,7 @@ Two secrets live in the host `.env` (git-ignored; placeholders in | "Request has been denied: Invalid next URL" at sign-in | Flow entered with an absolute cross-host `next=` (old redirect design) | Do not hand-craft flow URLs; enter guarded hosts normally — the outpost carries state in its own cookie | | `/outpost.goauthentik.io/*` 404s on every host | A provider row with null `authorization_flow`/`invalidation_flow` wedges the outpost config fetch | Backfill the flows; watch `podman logs authentik-server` for `Failed to fetch outpost from API` | | Off-LAN clients get "hostname could not be found" | Guarded host missing from the public DNS zone | Every host in the lockstep set needs a public A record (Porkbun) as well as the Pi-hole LAN answer | +| Correct membership but one user still 403s at a header gate | Outpost claim cache: a `proxysession` row minted before the membership change serves old claims until expiry | Delete that user's `proxysession` rows (section above) — next visit re-authorizes silently | ## Cross-references diff --git a/serverAppConfigs-docs/log.md b/serverAppConfigs-docs/log.md index 1f1129c..f316be0 100644 --- a/serverAppConfigs-docs/log.md +++ b/serverAppConfigs-docs/log.md @@ -311,3 +311,4 @@ timestamp: 2026-09-14 - `/tests/seerr_quadlet.bats` — new seam: boot persistence, self-healing, pinned tag, loopback-only publish, data-root contract, appnet join, no env/envfile secrets. - `/serverAppConfigs-docs/Dankclaw/Seerr.md`, `/serverAppConfigs-docs/index.md` — service doc + index entry. - Lessons: rootless podman uid mapping is continuous (container N → host 100000+N-1; uid 1000 → 100999 — an off-by-one chown EACCES-crash-looped first boot); new quadlet bind-source dirs must pre-exist on target (statfs 125 otherwise); new vhost TLS issues at Caddy reload (~15 s dns-01), not at first request. +- Authentik outpost claim cache fixed (root cause of the persistent `/admin` 403 despite homepage-admins membership): the outpost serves per-browser-session claims from `authentik_providers_proxy_proxysession` until row expiry — akadmin's live row predated the group add and carried `["authentik Admins"]` only, and the 403 never generates an authentik event. Deleted his 4 rows; the next visit re-authorizes silently against the untouched authentik login session. Silent re-auth proven live: deleted qa-e2e-admin's 13 rows, ran the e2e admin spec against the stale storage state — 2 passed in 2.9 s, no login prompt. Mechanism + diagnosis SQL documented in `/serverAppConfigs-docs/Infrastructure/Authentik.md` (new "Outpost claim cache" section + failure-mode row); the doc's data-path references updated to the 2026-09-01 Containers layout.