Use single-issuer structured AuthenticationConfiguration for k8s OIDC
Date: 2026-07-01 Status: Accepted Decision: hl-idws Deciders: Sean Brandt, Claude
Context
Section titled “Context”k3s currently trusts Authentik for kubectl OIDC via legacy --oidc-* apiserver flags. The Keycloak-side kubernetes public PKCE client + k8s-{admins,developers,viewers} groups exist and are verified (hl-5g05.12). Grounding against the LIVE cluster changed the original assumptions: it runs k8s v1.36 (structured AuthenticationConfiguration is GA at apiserver.config.k8s.io/v1 since 1.34 — not the v1beta1 assumed earlier); only the operator uses k8s OIDC (3-user trust model); and the existing oidc:k8s-* ClusterRoleBindings already match the Keycloak group names, so the cutover needs zero RBAC changes. This supersedes hl-9rhj, which chose a dual-issuer transition.
Decision
Section titled “Decision”Migrate the k3s apiserver to a file-based AuthenticationConfiguration (apiserver.config.k8s.io/v1) with a SINGLE JWT authenticator trusting only Keycloak (https://id.fzymgc.house/realms/fzymgc, audiences [kubernetes], username claim email + prefix oidc:, groups claim groups + prefix oidc:). Remove all --oidc-* flags atomically in the same render (they are mutually exclusive with --authentication-config). Cut straight over — no transitional dual-issuer trust.
Rationale
Section titled “Rationale”- The dual-issuer rationale of hl-9rhj (incremental per-user migration without invalidating sessions) does not apply: there is effectively one k8s-OIDC user, so there is no fleet of sessions to migrate independently.
- The config file is reversible (revert + rolling restart) and a non-OIDC client-cert break-glass kubeconfig exists on every server, so an atomic cutover has a safe rollback path — the concern that rejected the atomic flag switch in hl-9rhj is mitigated here.
- Single issuer is materially simpler and lower-risk than maintaining two concurrent authenticators.
apiserver.config.k8s.io/v1(GA since 1.34) is the correct API for the live 1.36 cluster; the earlierv1beta1assumption is outdated.- Existing
oidc:k8s-*RBAC already matches the Keycloak groups (with the retainedoidc:prefix), so no binding churn.
Alternatives Considered
Section titled “Alternatives Considered”- Single-issuer structured AuthenticationConfiguration (chosen): one Keycloak JWT authenticator; atomic
--oidc-*removal; reversible via config revert + break-glass; zero RBAC change. - Dual-issuer structured AuthenticationConfiguration (rejected; supersedes hl-9rhj): trust Authentik + Keycloak concurrently, drop Authentik at Phase 5. Over-engineered for a single operator; adds a second trust anchor + more complex rollout for a migration-continuity benefit that a one-user cluster does not need.
- Legacy
--oidc-*issuer repoint (rejected): keep the deprecated flag mechanism, just point it at Keycloak. Lowest churn and viable in 1.36, but does not modernize the auth mechanism and leaves the cluster on a deprecated path.
Consequences
Section titled “Consequences”- Positive: simpler config, one trust anchor, correct GA API, zero RBAC change, reversible cutover with break-glass.
- Negative: a brief OIDC-down window during the ForceNew rolling restart (covered by client-cert break-glass); no transitional grace period for any second k8s-OIDC identity (none exists).
- Neutral: Authentik
kubernetesprovider teardown becomes a separate follow-up once green; the k3s rollout is gated by an R2-specific issuer-discovery health check (non-crashing failure mode).