The operator ruled the implementation repo's main (50c8719) the latest and accepted state. Sync the spec to it. Accepted changes are reflected: one bare-metal environment, TrueNAS bulk storage, R2 off-cluster backup, CloudNativePG, single-node VPN gateway with Cilium fail-closed, inbound WireGuard, per-app auth sidecars, Renovate, the CI gate as approver, and the pm-agent. New ADR-0017..0025; ADR-0016 corrected and accepted; R-* IDs unchanged, eight amended. Where the implementation has simply not met a requirement, the requirement stands and the shortfall is recorded in docs/16-known-gaps.md. Co-Authored-By: Claude Opus 5.5 <[email protected]>
2.9 KiB
2.9 KiB
ADR-0014 — Identity Provider: Keycloak (SSO via OIDC + forward-auth)
- Status: Accepted — Keycloak, native OIDC, groups and MFA stand (forward-auth enforcement point and app list superseded by ADR-0022)
- Date: 2026-08-15
- Serves: R-IDP-01, R-IDP-02, R-IDP-03, R-IDP-04, R-IDP-05, R-IDP-06
- Supersedes: —
Context
The platform needs one identity and single sign-on: a central user directory, group-based authorization, MFA, and a way to protect both OIDC-capable apps and bare management UIs — without per-app passwords scattered across services. This also resolves the previously-open C2 auth question (12).
Decision
Use Keycloak as the platform IdP, integrated two ways behind one identity:
- Native OIDC for apps that support it (Gitea, Nextcloud, Grafana, Kasm, Semaphore, Headlamp).
- Forward-auth proxy (oauth2-proxy → Keycloak) at the Gateway for apps without usable OIDC (PiHole, *arr UIs, qBittorrent, Homepage, Prometheus, Alertmanager, Hubble UI).
Supporting decisions:
- Config as code: realms/clients/groups are declarative and GitOps-reconciled (Keycloak Operator/
Helm for the instance;
keycloak-config-clior realm-import for config). Secrets SOPS-encrypted. - Authorization by groups; MFA required for privileged groups.
- PostgreSQL on Longhorn, DR-critical, backed up off-cluster; Keycloak rebuilds from DB + git.
publicexposure (hardened) so login flows work for internet-exposed services.- Exceptions: Plex/Seerr use Plex auth; Minecraft uses Mojang/Microsoft; UniFi keeps native auth.
Alternatives considered
- Authelia / Authentik — lighter, and Authelia pairs cleanly with forward-auth; but Keycloak is the operator's explicit choice and offers the broadest OIDC/SAML, realm, and federation feature set for heterogeneous apps. (Chosen.)
- Per-app local auth only — rejected: no SSO, password sprawl, weak central policy/MFA (fails R-IDP-01/06).
- Cloud IdP (e.g. hosted OIDC) — rejected: this is a self-hosted, offline-capable home platform; identity should not depend on an external SaaS.
Consequences
- One login, one directory, central MFA and group policy; management UIs are never unauthenticated on the network (R-IDP-02).
- Keycloak becomes a critical dependency: an IdP outage impairs SSO logins → DR-critical storage
- a documented break-glass local-admin path (e.g. on Gitea) so recovery tools stay reachable.
- New platform components to run and secure: Keycloak, PostgreSQL, oauth2-proxy — all under default-deny with explicit allows and trusted TLS.
- Onboarded early (with PiHole/Gitea) so later services integrate SSO as they come up.
- Adds an
authblock to the ServiceDefinition (13) so SSO mode + allowed groups are declarative per service.