M4a: define the six OIDC clients and the authorization model in Keycloak (no cutover) #279
Labels
No labels
bug
enhancement
in-progress
needs-info
needs-triage
p0
ready-for-agent
ready-for-human
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Cloonar/nixos#279
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Part of #266 (M4, first half). Follows #277, which replaced
realmFileswith a convergingkeycloak-config-cliimport and landed the LDAP group mapper.Define all six OIDC clients and the authorization model in the
cloonarrealm. Nothing is cut over — every application keeps pointing at Authelia. This issue makes Keycloak ready to serve them; repointing the apps is separate work.Decisions already made — do not re-litigate
web-armorweb-02.claude-mcp-forgejois being retired and is NOT migrated. Six clients, not the seven Authelia carries. Removing its Authelia client and thehosts/fw/vms/web/mcp-forgejo.nixvhost is separate cleanup — do not touch either here.one_factorin Authelia today. Thetwo_factorrequirement exists only inaccess_controlfor the forward-auth vhosts, which is M5.Secrets — already placed, reference by name
hosts/web-arm/secrets.yamlcarries what is needed. Three clients reuse a secret the host already holds; read them under a second name viasops.secrets.<n>.key, the waykeycloak-ldap-bindreadslinuxbind-password. Do not duplicate a value into a new key.web-armgrafana-oauth-secretimmich-oidc-client-secretnextcloud-secretsgitea-oidc-client-secretfreescout-oidc-client-secretsynapse-oidc-client-secretmas-authelia-client-secretsynapse-oidc-client-secretis a genuine duplicate of a value that also lives in web-02's secrets file —web-armcannot decrypt web-02's — so note the rotation coupling in a comment. gitea's and freescout's are new values that their applications do not have yet; those get pasted in at cutover, which is why nothing must be repointed in this issue.Every client must state its secret explicitly. A client left without one may be handed a fresh secret by
keycloak-config-clion each converge run undermanaged = full, which would break that application intermittently after cutover. Generate-once-then-pin is the only safe steady state.The access matrix to reproduce
From
hosts/web-arm/modules/authelia.nix:admin-onlyAdministratorsinternalAdministrators,Mitarbeiter,macher.solutionsimmichimmichKeycloak has no per-client policy field. Express it as realm roles assigned to the mapped LDAP groups, plus per-client authentication-flow overrides containing a
Condition - User Rolesubflow ending inDeny Access. Synapse needs no override and uses the default flow.Two failure modes to design against
1. An override flow that omits
auth-spnegosilently kills SSO for that client. A flow override replaces the whole browser flow, so each gating flow must still contain theauth-cookie/auth-spnego/identity-provider-redirector/ forms structure frombrowser-spnego. Miss it and the client still works by password — it just stops doing the one thing this entire workstream exists for, and a smoke test would not catch it.2.
full.pathon the group-membership mapper. Keycloak's group membership mapper emits/Administratorsby default. Authelia emits bareAdministrators, and Grafana'srole_attribute_path(hosts/web-arm/modules/grafana/default.nix) is written against the bare form. Setfull.path = falseor Grafana's role mapping breaks at cutover.Client details
Port each from
authelia.nix, preserving redirect URIs exactly:https://git.cloonar.com/user/oauth2/authelia/callbackhttps://nextcloud.cloonar.com/apps/oidc_login/oidchttps://grafana.cloonar.com/login/generic_oauthhttps://support.cloonar.com/oauth-login/callback/fryg87l64https://matrix.cloonar.com/upstream/callback/01KJPRKN397E5N8D0CA2Z3TJ7Yhttps://immich.cloonar.com/auth/login,https://immich.cloonar.com/user-settings,app.immich:///oauth-callbackPer-client notes:
consent_mode = "implicit"on every client →consentRequired: false.token_endpoint_auth_method = "client_secret_post"(freescout, synapse, immich): Keycloak'sclient-secretauthenticator accepts both POST-body and Basic, so this needs no per-client setting — but verify rather than assume, since it was an explicit workaround for each of those four.groupsin the ID token, not just userinfo — that is what Authelia'sclaims_policies.defaultwas for.Secrets in the realm file — resolve this first
Client secrets cannot sit in the realm JSON: it is a Nix store path and world-readable, the same reason the LDAP bind went behind the vault SPI in #271.
#277 disabled
import.var-substitutionon the theory that it would mangle${vault.ldapbind}. That may have been over-cautious — keycloak-config-cli's substitution uses$(env VAR)syntax, which likely does not collide. Establish, before writing any client:var-substitutioncan be re-enabled safely alongside the existing${vault.…}reference.Whichever route, the secrets arrive through the unit's
EnvironmentFileor the vault directory — never as literals in the JSON. State the finding in the PR description.ADR note
Record one consequence in
docs/adr/0022-keycloak-and-scoped-kerberos.md: Authelia stored client secrets as one-way pbkdf2 hashes; Keycloak stores them recoverably — the admin console can display them. After M6 every client secret is readable by anyone with Keycloak admin access, its database, or a database backup. Normal for Keycloak, but a posture downgrade worth stating, and an argument for rotating all of them once the migration settles.Out of scope
claude-mcp-forgejo— its Authelia client andhosts/fw/vms/web/mcp-forgejo.nixstay exactly as they are; removal is separate cleanup.authelia.nix— untouched.Verification
scripts/pre-commitdry-buildsweb-arm.import.managed.*staysfull.Human, after deploy — no application changes are needed for any of this:
systemctl status keycloak-config; admin console shows six clients with the right redirect URIs, and roles assigned to the four groups.dominik.polakovicssession still reacheshttps://sso.cloonar.com/realms/cloonar/accountwith no prompt.https://sso.cloonar.com/realms/cloonar/protocol/openid-connect/auth?client_id=immich&redirect_uri=https://immich.cloonar.com/auth/login&response_type=code&scope=openidAn authorized user is redirected back with a
code; an unauthorized one is denied. Repeat for one client per policy (giteafor admin-only,nextcloudfor internal,immich), ideally with an account that is not in the relevant group — a policy that admits everyone looks identical to a working one otherwise.Definition of done
authelia.nixunchanged.Closes #<this issue>, stating how client secrets are delivered and confirming step 4 passed.M4a: define the seven OIDC clients and the authorization model in Keycloak (no cutover)to M4a: define the six OIDC clients and the authorization model in Keycloak (no cutover)