12 KiB
Phase 0 owner checklist (kof22)
These are the steps only James can take: items 6, 7, 8 and 10 of DESIGN.md section 11.0, plus a few decisions the phase 0 work surfaced. Agents prepared everything else. Nothing below has been run against production.
Links:
- cluster-gitops draft PR: KofTwentyTwo/cluster-gitops#72 (the identity bootstrap blueprint, the removal of the
presententries, the attribute-driven sign-in policy, inert webhook plumbing, and the blueprint lint) - Website-Backend and cluster-gitops wiring draft PRs: see
docs/STATUS.md - Spec: QRun-IO/qqq#1065 (revision 4: phase 0 findings, review fixes, owner decisions of 2026-10-12)
0. Decisions surfaced by phase 0 (read first)
| # | Decision | Recommendation |
|---|---|---|
| D1 | Repo visibility. Resolved: QRun-IO/qbit-authentik is public. |
Check that CircleCI follows the repo (it does for PR CI); nothing else to do. |
| D2 | Lockout risk. Authentik stops the service account from escalating, but not from locking out. With the phase 2/3 permissions it can do all of the following (contract test SuperuserFloorIT): remove members from authentik Admins, deactivate kof22-breakglass, strip qqq.io/protected, and rename the superuser group. The qbit guard refuses all of these; Authentik does not. |
Accept, with the mitigations in the spec (§5.1, revision 3). The real break-glass for a compromised QQQ is cluster access: kubectl exec into the Authentik worker and run ak shell to reactivate or re-add. Optionally, phase 2 grants remove_user_from_group per group (object permissions) instead of globally. |
| D3 | Self-minted tokens. Any authenticated user, including the service account, can create API tokens for itself without add_token, and read their keys. A compromised QQQ could therefore keep a second token alive after you rotate the first. |
Accept. The phase 1 drift report flags service-account tokens beyond the expected one. Check that list after every rotation (item 8). |
| D4 | Access matrix. check_access?for_user= only works for superusers. For the service account it silently answers for itself. The policy-test endpoint returns HTTP 500 for non-superusers on 2026.8.3. |
Accepted in the spec: the qbit computes app access itself from qqq.io/app-access and the app's bindings, and shows other expression policies as "not evaluated". |
Item 6: staging rehearsal (needs your go-ahead)
The agent cannot read production data. The rehearsal runs entirely in local Docker, on a dump you take.
- Take a dump of the Authentik database. This is a read-only
pg_dumpof the CNPG cluster that hosts it:Keep the dump on an encrypted disk. It contains every identity and password hash. Delete it after the rehearsal.# CNPG cluster kof22-website-pg (namespace kof22-website-prod) hosts the database "authentik" P=$(kubectl --context=k8s-galaxy -n kof22-website-prod get pod -l cnpg.io/cluster=kof22-website-pg,cnpg.io/instanceRole=primary -o name) kubectl --context=k8s-galaxy -n kof22-website-prod exec "$P" -c postgres -- \ pg_dump -Fc -d authentik > authentik-$(date +%F).dump - Get both blueprint versions:
git -C cluster-gitops show origin/main:tenants/kof22/website-authentik/blueprint-configmap.yaml > before.yaml git -C cluster-gitops show origin/feat/kof22-identity-bootstrap:tenants/kof22/website-authentik/blueprint-configmap.yaml > after.yaml - Run
docs/cluster-gitops-phase0/rehearse-phase0.sh authentik-<date>.dump before.yaml after.yamlfrom macOS, Linux or Git Bash. The script has already passed on a synthetic production-like dump: today'smainblueprint with James re-pinned by thepresententry, plus UI-made publishers.- The script restores the dump into a throwaway Postgres and starts Authentik 2026.8.3 on the current blueprints. It then records every user's groups, switches to the phase 0 blueprints, re-applies both twice, and checks:
- nobody's memberships changed
james.maeskeepsauthentik Adminsandkof22-admins- the service account, break-glass and seed groups exist
- It prints
REHEARSAL PASSEDor the differences. - Containers and the work directory are removed on exit. The script mints a rehearsal-only API token inside the throwaway copy, which is deleted with the container.
- The script restores the dump into a throwaway Postgres and starts Authentik 2026.8.3 on the current blueprints. It then records every user's groups, switches to the phase 0 blueprints, re-applies both twice, and checks:
- Reply "rehearsal passed" (or paste the output) on cluster-gitops#72.
Item 7: production apply
Do these in order, in one sitting.
- Freeze. Record the current memberships: Authentik admin → Directory → Users, or export each admin group's member list. Note who is in
authentik Admins,kof22-adminsandkof-website-publishers. - Merge cluster-gitops#72 (convert it from draft first). ArgoCD syncs the ConfigMap, and the worker sees the file change and applies both blueprints within about a minute.
- Verify in Authentik admin → Customization → Blueprints:
kof22-oauth2-appsandkof22-identity-bootstrapboth show successfuljames.maesstill hasauthentik Adminsandkof22-admins- user
qqq-kof22-admin-idadminexists: type Service account, no groups, roleqqq-kof22-admin-idadmin - user
kof22-breakglassexists and is a superuser - groups
identity-admins,kof22-admins,kof-website-publishers,kof-website-draftersexist; onlyidentity-adminshas attributes - you can still sign in to
https://admin.kof22.com(the name fallback in the sign-in policy keeps today's access) - Events → Notification rules:
qqq-kof22-admin-synchas no destination (inert)
- Prove it (optional, recommended): add a test user to
kof-website-publishersin the UI. Then Blueprints → kof22-oauth2-apps → Apply and the same forkof22-identity-bootstrap. Wait a minute and confirm the member is still there. Remove the test user afterwards. - Rollback if anything is wrong: revert the PR. This re-adds the old
presententries, which re-pin James's groups. Logins keep working throughout because of the fallback.
Item 8: service-account token and secrets
- Mint the API token:
- In Authentik admin, go to Directory → Tokens and App passwords → Create.
- Identifier:
qqq-kof22-admin-idadmin-api. User:qqq-kof22-admin-idadmin. Intent: API. Expiring: on, with an expiry 90 days out. - Copy the key once.
- Store it in Infisical at
/kof22-website-authentik-api/token. Paste it straight from the Authentik dialog into Infisical; it must never go in chat, git or a ticket. - Webhook secret (used in phase 2; create it now so the phase 2 PR can be merged directly):
openssl rand -base64 48into Infisical at/kof22-website-authentik/qqq-webhook-secret. - Check that Directory → Tokens shows exactly one token for
qqq-kof22-admin-idadmin(see D3). - Calendar a rotation reminder for day 76. The health widget will also warn 14 days before expiry.
Item 10: seed data (Authentik UI)
- Add yourself (
james.maes) toidentity-admins. - On group
kof22-admins→ Attributes, set the following. This is today'sADMIN_PERMISSIONSfrom Website-BackendAuthentikAuthenticationCustomizer, resolved to literal strings on 2026-10-11 againstorigin/mainf936cbd. Re-check if that list changed before you seed, because the phase 1 mapper will grant exactly this:Keep the existing attributes of the group. Add only these two keys.qqq.io/app-access: ["kof22-website-prod"] qqq.io/permissions: - authentik.users.read - esbView.hasAccess - esbOperate.hasAccess - esbDelete.hasAccess - analytics.hasAccess - quickSearchAdmin.hasAccess - quickSearchIndex.read - quickSearchIndex.write - quickSearchIndexRun.read - quickSearchIndexRun.write - quickSearchFailedEvent.read - quickSearchFailedEvent.write - dataHealth.hasAccessauthentik.users.readletskof22-adminsopen the Identity app; the seed groupidentity-adminsalready carriesauthentik.*(all identity permissions) from the blueprint, so you get everything through it once you join it (step 1). - On group
kof-website-publishers→ Attributes:qqq.io/app-access: ["kof22-website-prod"]. This keeps thekof-publisher-prodservice account's client-credentials tokens working once the name fallback is removed. kof-website-drafters: leave it withoutqqq.io/app-accessuntil you decide that drafters should sign in to the admin. Granting it later is that one attribute.- Break-glass password:
- Set a long random password on
kof22-breakglass(Directory → Users → kof22-breakglass → Set password) and store it in 1Password. - Sign in with it once in a private window to prove it works, then sign out.
- Its recovery path is
ak create_recovery_key 10 kof22-breakglasson the worker pod.
- Set a long random password on
After items 6-10
Tell the agent "phase 0 owner steps done". Next come item 11 (the qbit in read-only mode on staging, behind AUTHENTIK_QBIT_ENABLED), then the phase 1 mapper cut-over, then removal of the sign-in name fallback.
Item 12: turn on the full lifecycle (v1)
The qbit can now create and change users and groups, not just read. Do this only after items 6-11 are done and the read-only screens look right on staging. Nothing here is applied by an agent.
- Add the write permissions to the service account's role. In the identity bootstrap blueprint, append these to
qqq-kof22-admin-idadmin'spermissionslist (they are re-asserted additively on every apply; the PR is prepared as a separate commit on cluster-gitops#72):Never addauthentik_core.add_user authentik_core.change_user authentik_core.reset_user_password authentik_core.add_group authentik_core.change_group authentik_core.add_user_to_group authentik_core.remove_user_from_group authentik_core.view_token authentik_core.delete_token authentik_core.delete_authenticatedsession authentik_stages_authenticator_{totp,static,webauthn,email,sms,duo}.view_<kind>device authentik_stages_authenticator_{totp,static,webauthn,email,sms,duo}.delete_<kind>deviceenable_group_superuser,view_token_key,set_token_key,add_token,impersonateordelete_user. Invitations are not used at kof22 yet (there is no enrollment flow), so the invitation permissions andview_floware left out; add them with the enrollment flow. - Check the brand's recovery flow. The password reset email (also used by Create User) fails with "No recovery flow set." unless the brand that serves
auth.kof22.comhas a recovery flow. In Authentik admin, System → Brands, edit the brand and set Recovery flow tokof22-recovery. This is a one-time setting. - Create the two local tables (
authentik_admin_action,authentik_approval_request) with the Liquibase change in the Website-Backend PR (seedocs/schema.sqlfor the DDL it follows). - Turn the features on in the Website-Backend deployment (the draft PR defines the flags; the default stays read-only). Start with
AUTHENTIK_QBIT_WRITES=false, check the Identity home, then switch writes on. - Try it on a test user: create one, add them to
kof-website-publishers, deactivate them. Check that Authentik's Events showscustom_qqq_admin_actionevents naming you, and that Identity → Admin Actions shows the rows. - Two-person approval. Granting
authentik.*to a group needs a second person withauthentik.approve. While you are the only identity administrator, make those grants in the Authentik UI (the break-glass in the spec), or turntwoPersonApprovaloff until a second administrator exists.