Configure the IdP admin client on the dogfood and verify Users & access end to end #346
Labels
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: Cordy/Cairn#346
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?
Follow-up to #345, which built the Users & access page. The page is correct today but the dogfood only exercises its degraded half, so the interesting path has never actually run against a real directory.
Current state on
cairn-encauth.groups.keycloak*is not configured, sospacesMgris nil, soAdmin.GroupMembersis nil. The page therefore shows:OpenID Connect · <issuer>· ConnectedThat is the designed behaviour (a partial list would look authoritative), but it means the resolved path — member count, administrator rows, "You", "Signed in now" / "Last active" — has only ever been exercised by unit tests with a stubbed
GroupMembers.Work
1. Keycloak service account
Against the test IdP (192.168.10.248, realm
cordyceps):realm-managementclient rolesview-usersandquery-groups.manage-users. Reading a group to display it never writes to the directory.manage-usersis only forauth.groups.manage, the IdP-managed-spaces opt-in (ADR 0001), and is a materially higher-privilege credential.nikola-test, so the list is visibly more than "just me".2. Config
Into the untracked
cairn-enc-configsecret (not GitOps, so Argo will not revert it):Note the side effect worth watching for: with
auth.mode: oidcand this client present,main.goalso enables live group resolution (#96) — group and admin changes stop riding the session snapshot and re-resolve per request behind a 60s cache. That is a behaviour change beyond this page and should be observed deliberately rather than discovered.3. Verify
auth.adminsand the groupTestAdminNamesIncompleteOnResolveErrormakes in the abstract — confirm it holds against a real failing Keycloak.4. The two-source behaviour
Deliberately confirm across a redeploy, because it looks like a bug and is not:
internal/auth/session.go), Active today keeps its number (audit log survives)If this reads as broken to a fresh pair of eyes in the beta, the row labels are the thing to change, not the sources.
5. Docs
docs/handbook/users-access.mdexists (shipped in #345) but is not linked fromdocs/handbook/index.md— add it.Related
Premise correction + decisions (Nikola, 2026-09-04).
The degraded state this issue describes no longer exists on either dogfood:
auth.groups.*→ custody client becomes the admin client withmanageon). Power mode and live groups have been active for some time. This is blessed as deliberate: keycloak-profile custody independently requiresmanage-users(it writes each user's age identity into profile attributes — key minting proves the role is present), and OIDC-group-backed spaces are a wanted feature, which also requiresmanage-users(group CRUD + membership edits sit under it;view-users/query-groupsare enumeration only). Given that feature set, the current single client is minimal privilege. Follow-up niceness: setauth.groups.*explicitly in the secret so the config states the choice instead of inheriting it via adoption.custody=openbao, live groups on, and no power-mode line — i.e. an explicitauth.groups.*read client withmanageoff, app-owned spaces. That is the least-privilege configuration step 1 of this issue asked for; it already exists. Remaining check: confirm in Keycloak that its service account holds onlyview-users+query-groupsand is not a reuse of the enc custody client (if it is, mint a dedicated read-only client — zero feature loss on bao).Key blast-radius insight for the docs (shipping now): on a keycloak-profile custody realm, even the read tier (
view-users) can read every user's age identity from profile attributes — andmanage-userscan replace them, or grant admin in every group-trusting app. OpenBao custody separates the radii: the bao token (prefix-scoped KV policy) touches keys but never accounts; the Keycloak client stays read-only and never touches keys. This contrast goes into the handbook as the operator decision driver.Still open on this issue: the step-3 verification checklist (operator pass, possibly via browser automation) and the bao client-identity check above. Docs items ship in the next commit.
Step-3 verification pass, run via browser automation against both dogfoods (2026-09-05, v0.6.127). Existing SSO sessions reused; no credentials entered.
Verified on both
files-baoandfiles(#admin/users), identical healthy state:cairn-adminswith member-count chip (2 members), matching the rendered list exactlymanu-admin,nikola-test— visibly more than "just me"manu-admin): name alone, no empty second lineOpenID Connect · id.c0rdyceps.ch/realms/cordyceps· ConnectedNot verifiable autonomously (and why):
manu-adminto actually sign in; no credential entry by automation, ever.auth.groupsservice account holds onlyview-users+query-groups, and is a distinct client from the enc custody client — the admin console presents a login form; stopped there. Read-only inspection can be driven by automation the moment an operator session exists.kubectl edit secret+ restart; the code path is covered byTestAdminNamesIncompleteOnResolveError, so this stays a nice-to-have live confirmation.Docs items (§5) shipped 2026-09-04 (
users-access.md+ index row + role matrix + live-groups side effect + custody blast-radius comparison).Keycloak inspection completed (2026-09-05, operator signed in to the console, automation drove read-only):
cordycepshas three Cairn clients:cairn(public PKCE login),cairn-bao-groups,cairn-keycustody. Bao's groups client is NOT a reuse of the custody client.cairn-bao-groupsis exactly least-privilege: service-account roles arerealm-management view-users+realm-management query-groups(+ default-roles) — nomanage-users, nothing else. The bao side of this issue is fully as specified.cairn-adminsmembership matches: exactly 2 members in Keycloak (manu-admin/ Manuel Novak,nikola-test) — matches the Users & access chip and rendered list on both instances.⚠️ Finding —
cairn-keycustodyis over-privileged relative to the blessed minimum. Its service account holdsmanage-users,view-users,query-users,query-groupsplusmanage-realmandquery-realms. The blessed power-mode set (per the users-access.md role matrix) ismanage-users+view-users+query-groups;query-usersis redundant besideview-usersbut harmless.manage-realmis not — it allows changing realm configuration itself (auth flows, realm settings, events config), which nothing in Cairn's custody or IdP-managed-spaces path uses. Recommendation: unassignmanage-realm(andquery-realms) fromservice-account-cairn-keycustody. If anything unexpected breaks (space create/membership edit, key minting for a new user), re-assigning is one click — but per the documented model, nothing should.Remaining live checks now unblocked by the console session, pending explicit operator go (both are directory writes): the role trim above, and the checklist's group-removal round trip (remove
manu-adminfromcairn-admins→ confirm both instances drop him from Administrators within the 60s live-groups cache → re-add → confirm restored).Directory writes executed with operator approval (2026-09-05), both verified live:
cairn-keycustody✅ —manage-realmandquery-realmsunassigned; the service account now holds exactlymanage-users+view-users+query-users+query-groups(+ default-roles). Custody smoke-tested afterwards: encrypted file preview onfilesdecrypts fine — the trim broke nothing, as the role matrix predicted.manu-adminfromcairn-admins; within the 60s live-groups cache window both the chip (1 members) and the Administrators list updated (list showed onlynikola-test). Re-added; page restored to 2 members withmanu-adminback. Live group resolution (#96) demonstrated end to end against the real directory.New copy nit found during the round trip: the admin-group chip reads "1 members" — the member count bypasses
tn(). Singular form needed (×4 locales). Will ride along with the next UI batch.Checklist state after today: everything is ticked except (a) second-admin presence rows — needs
manu-adminto sign in once; (b) the broken-secret live confirmation — optional, unit-covered; (c) the §4 redeploy observation — deliberately at the next release rollout. Plus the config nicety of statingauth.groups.*explicitly incairn-enc-configinstead of inheriting via adoption (operator kubectl, no behaviour change).Final verification rounds (2026-09-05) — checklist complete.
Second-admin presence (§3):
manu-adminsigned in live. Observed from his perspective:manu-admin — You · Signed in now,nikola-test — Last active 59 minutes ago, tallies 1 signed in / 2 active today. Later, from Nikola's perspective after the test cycle: rows flipped correctly (nikola-test — You · Signed in now,manu-admin — Last active 8 minutes ago). Every presence presentation now seen live: You-marker tracking the viewer, Signed in now, Last active N ago, and bare name.Broken-secret failure mode (§3, run for real):
auth.groups.keycloakClientSecretdeliberately broken on bao (operator kubectl, sed-verified one-line diff), pod restarted. Result:TestAdminNamesIncompleteOnResolveErrorholds against a real failing Keycloak.Secret restored from backup, pod restarted, healthy state confirmed (chip, list, presence all back).
§4 two-source behaviour, observed deliberately across the same restarts: sessions died each rollout (Signed in now dropped to just the fresh viewer), Active today kept 2 people from the audit log throughout, and sign-ins recovered the counts. The labels read fine to these eyes — no relabel needed for the beta yet.
Remaining on this issue: only the config nicety — stating
auth.groups.*explicitly incairn-enc-config(withmanage: trueto preserve power mode) instead of inheriting via adoption. Zero behaviour change; commands handed to the operator. Closeable once that lands (or closeable now with the nicety tracked as ops hygiene).Closing — every item done (2026-09-05).
Final step landed:
auth.groups.*now stated explicitly incairn-enc-config(mirrored from the custody block via jq,manage: true), pod restarted. Boot log confirms identical behaviour to the adopted path:storage encryption enabled custody=keycloak-profile, the power-mode WARN (IdP-managed spaces enabled … requires manage-users, ADR 0001), andlive group resolution enabled (#96). The config now states the choice instead of inheriting it.Where this issue ended up versus where it started: it asked for a least-privilege read client and a first-ever exercise of the resolved path. It closes with — both dogfoods verified end to end on the resolved path (all presence states, member counts matching the directory, You-marker tracking the viewer); bao's read client confirmed exactly
view-users+query-groupsand distinct from custody; enc's custody client trimmed of an over-grant (manage-realm,query-realms) found during verification, with custody smoke-tested after; the group-removal round trip proving live groups against the real directory; the broken-secret failure mode confirmed fail-closed (with one UX gap filed as #426 item 3); §4's two-source behaviour observed deliberately across real restarts; docs shipped 2026-09-04; and two net-new findings cut to #426.Both instances end the day more locked down and better verified than the issue asked for.