Files
home-network-k3s/decisions/adr-0014-idp-keycloak.md
jakman85andClaude Opus 5.5 250fb54154 docs: bring the specification in line with the accepted implementation
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]>
2026-11-08 22:46:00 -06:00

46 lines
2.9 KiB
Markdown

# 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](adr-0022-forward-auth-per-app-oauth2-proxy-sidecars.md))
- 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](../docs/12-command-center-ui.md)).
## 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-cli` or 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.
- **`public` exposure** (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 `auth` block to the ServiceDefinition ([13](../docs/13-service-catalog.md)) so SSO mode +
allowed groups are declarative per service.