hedgedoc: move the scout-hedgedoc OIDC client onto kanidm-operator #19

Closed
opened 2026-10-19 23:18:06 +11:00 by myagent · 3 comments
Collaborator

Follow-up from kanidm-operator WU-8 / homelab #18. Decided 2026-10-19: WU-8 deployed only the smoke client; hedgedoc stays on its OnePasswordItem until this ticket. The fuller notes are in the operator repo, docs/deploying.md § "Moving a real app onto the operator" and docs/oauth2-clients.md § "Adopting an existing Kanidm client".

Goal

hedgedoc's Kanidm OIDC client scout-hedgedoc is managed by an OAuth2Client CR (plus a Group CR for its users) in argo/hedgedoc/, and the OnePasswordItem hedgedoc-oidc-idmacnz is gone. Login at https://notes.m.ac.nz keeps working throughout.

Current state

  • argo/hedgedoc/deployment.yaml sets CMD_OAUTH2_CLIENT_ID=scout-hedgedoc and reads CMD_OAUTH2_CLIENT_SECRET from Secret hedgedoc-oidc-idmacnz, key password. CMD_OAUTH2_PKCE=true, scope openid email profile groups.
  • That Secret is owned by argo/hedgedoc/hedgedoc-oidc-idmacnz.yaml (OnePasswordItem, vaults/homelab/items/hedgedoc-oidc-idmacnz).
  • The Kanidm client scout-hedgedoc was made by hand (create-kanidm-oidcproxy.sh conventions). The operator will adopt it.

Gotchas (read before writing the CRs)

  1. Don't point the CR at hedgedoc-oidc-idmacnz. The operator refuses a Secret owned by another controller (SecretConflict). Use a new name, e.g. spec.secret.name: scout-hedgedoc-oidc, and repoint the Deployment at key client_secret. The operator's Secret keys are client_id, client_secret, issuer_url, discovery_url (not password).
  2. The spec is authoritative on adoption. Redirect URLs, scope maps and claim maps that aren't in the spec are removed, and a Group's members list replaces the Kanidm membership. Before writing anything, capture the live state as idm_admin:
    kanidm system oauth2 get scout-hedgedoc
    kanidm group get <the group in its scope map>
    
    and copy redirect URLs, scope maps, claim maps and members into the CRs verbatim.
  3. Adoption turns on strict redirect-URI matching. List hedgedoc's exact callback URL (https://notes.m.ac.nz/auth/oauth2/callback or whatever oauth2 get shows), not just the origin.
  4. Adoption keeps the existing basic secret, so the new Secret holds the same value the 1Password item has. hedgedoc does PKCE, so leave disablePkce unset.
  5. If the users group is left as a plain name: reference (not a Group CR), the operator doesn't touch its membership. That's the lower-risk option if the membership is managed elsewhere.

Steps

  1. Capture live state (gotcha 2).
  2. In argo/hedgedoc/: add kanidm-group.yaml (optional, see 5) and kanidm-oauth2client.yaml (spec.name: scout-hedgedoc, type: confidential, secret.name: scout-hedgedoc-oidc, the captured redirect URLs / scope maps / claim maps). Same shape as argo/scout/kanidm-operator-smoke/.
  3. Same PR: change the Deployment's CMD_OAUTH2_CLIENT_SECRET to secretKeyRef: {name: scout-hedgedoc-oidc, key: client_secret}. Keep the OnePasswordItem in this PR so a failed adoption can be reverted cleanly.
  4. Merge, then check:
    k() { kubectl --context scout-tailscale-operator.tuatara-puffin.ts.net "$@"; }
    k -n hedgedoc get oauth2clients.kanidm.m.ac.nz,groups.kanidm.m.ac.nz   # Ready=True, adopted: true
    k -n hedgedoc get secret scout-hedgedoc-oidc
    k -n hedgedoc rollout status deploy/hedgedoc
    
    then log in at https://notes.m.ac.nz with a real user.
  5. Second PR: delete argo/hedgedoc/hedgedoc-oidc-idmacnz.yaml. ArgoCD prunes the OnePasswordItem and its Secret. Archive the 1Password item afterwards.

Acceptance

  • OAuth2Client scout-hedgedoc is Ready=True with status.adopted: true; the Group (if used) is Ready=True.
  • hedgedoc login via id.m.ac.nz works; users keep their existing hedgedoc identity (same preferred_username).
  • OnePasswordItem hedgedoc-oidc-idmacnz no longer exists in the repo or on scout.

Rollback

Revert the PR from step 3. The OnePasswordItem and its Secret are still there, so the Deployment goes back to the old secret. Do not delete the OAuth2Client CR while rolling back unless you mean to delete the Kanidm client too: the finalizer deletes the Kanidm object. To detach without deleting, remove the finalizer by hand first (see docs/oauth2-clients.md).

Follow-up from kanidm-operator WU-8 / homelab #18. Decided 2026-10-19: WU-8 deployed only the smoke client; hedgedoc stays on its `OnePasswordItem` until this ticket. The fuller notes are in the operator repo, `docs/deploying.md` § "Moving a real app onto the operator" and `docs/oauth2-clients.md` § "Adopting an existing Kanidm client". ## Goal hedgedoc's Kanidm OIDC client `scout-hedgedoc` is managed by an `OAuth2Client` CR (plus a `Group` CR for its users) in `argo/hedgedoc/`, and the `OnePasswordItem` `hedgedoc-oidc-idmacnz` is gone. Login at https://notes.m.ac.nz keeps working throughout. ## Current state - `argo/hedgedoc/deployment.yaml` sets `CMD_OAUTH2_CLIENT_ID=scout-hedgedoc` and reads `CMD_OAUTH2_CLIENT_SECRET` from Secret `hedgedoc-oidc-idmacnz`, key `password`. `CMD_OAUTH2_PKCE=true`, scope `openid email profile groups`. - That Secret is owned by `argo/hedgedoc/hedgedoc-oidc-idmacnz.yaml` (`OnePasswordItem`, `vaults/homelab/items/hedgedoc-oidc-idmacnz`). - The Kanidm client `scout-hedgedoc` was made by hand (`create-kanidm-oidcproxy.sh` conventions). The operator will **adopt** it. ## Gotchas (read before writing the CRs) 1. **Don't point the CR at `hedgedoc-oidc-idmacnz`.** The operator refuses a Secret owned by another controller (`SecretConflict`). Use a new name, e.g. `spec.secret.name: scout-hedgedoc-oidc`, and repoint the Deployment at key `client_secret`. The operator's Secret keys are `client_id`, `client_secret`, `issuer_url`, `discovery_url` (not `password`). 2. **The spec is authoritative on adoption.** Redirect URLs, scope maps and claim maps that aren't in the spec are removed, and a `Group`'s `members` list replaces the Kanidm membership. Before writing anything, capture the live state as `idm_admin`: ```sh kanidm system oauth2 get scout-hedgedoc kanidm group get <the group in its scope map> ``` and copy redirect URLs, scope maps, claim maps and members into the CRs verbatim. 3. **Adoption turns on strict redirect-URI matching.** List hedgedoc's exact callback URL (`https://notes.m.ac.nz/auth/oauth2/callback` or whatever `oauth2 get` shows), not just the origin. 4. Adoption keeps the existing basic secret, so the new Secret holds the same value the 1Password item has. hedgedoc does PKCE, so leave `disablePkce` unset. 5. If the users group is left as a plain `name:` reference (not a `Group` CR), the operator doesn't touch its membership. That's the lower-risk option if the membership is managed elsewhere. ## Steps 1. Capture live state (gotcha 2). 2. In `argo/hedgedoc/`: add `kanidm-group.yaml` (optional, see 5) and `kanidm-oauth2client.yaml` (`spec.name: scout-hedgedoc`, `type: confidential`, `secret.name: scout-hedgedoc-oidc`, the captured redirect URLs / scope maps / claim maps). Same shape as `argo/scout/kanidm-operator-smoke/`. 3. Same PR: change the Deployment's `CMD_OAUTH2_CLIENT_SECRET` to `secretKeyRef: {name: scout-hedgedoc-oidc, key: client_secret}`. Keep the `OnePasswordItem` in this PR so a failed adoption can be reverted cleanly. 4. Merge, then check: ```sh k() { kubectl --context scout-tailscale-operator.tuatara-puffin.ts.net "$@"; } k -n hedgedoc get oauth2clients.kanidm.m.ac.nz,groups.kanidm.m.ac.nz # Ready=True, adopted: true k -n hedgedoc get secret scout-hedgedoc-oidc k -n hedgedoc rollout status deploy/hedgedoc ``` then log in at https://notes.m.ac.nz with a real user. 5. Second PR: delete `argo/hedgedoc/hedgedoc-oidc-idmacnz.yaml`. ArgoCD prunes the `OnePasswordItem` and its Secret. Archive the 1Password item afterwards. ## Acceptance - `OAuth2Client` `scout-hedgedoc` is `Ready=True` with `status.adopted: true`; the `Group` (if used) is `Ready=True`. - hedgedoc login via id.m.ac.nz works; users keep their existing hedgedoc identity (same `preferred_username`). - `OnePasswordItem` `hedgedoc-oidc-idmacnz` no longer exists in the repo or on scout. ## Rollback Revert the PR from step 3. The `OnePasswordItem` and its Secret are still there, so the Deployment goes back to the old secret. **Do not delete the `OAuth2Client` CR while rolling back** unless you mean to delete the Kanidm client too: the finalizer deletes the Kanidm object. To detach without deleting, remove the finalizer by hand first (see `docs/oauth2-clients.md`).
Author
Collaborator

Step 1 is up as #20.

Live state captured 2026-10-19 (via the operator's service-account token against GET /v1/oauth2/scout-hedgedoc and /v1/group/scout_hedgedoc_users; secrets not recorded):

attribute value
displayname notes.m.ac.nz
oauth2_rs_origin (redirect) https://notes.m.ac.nz/auth/oauth2/callback
oauth2_rs_origin_landing https://notes.m.ac.nz/
oauth2_rs_scope_map scout_hedgedoc_users → email groups openid profile
claim maps / sup scope maps none
oauth2_strict_redirect_uri already true
prefer short username / disable PKCE unset (defaults)
uuid 8d0b611d-787a-4bfd-bf2e-b5e18676a965
group scout_hedgedoc_users members sophie (uuid 55cb07f0-899d-4289-8a0f-018fdf7ec2ca)

The CRs in #20 reproduce exactly this, so adoption is a no-op in Kanidm. Step 2 (remove the OnePasswordItem) follows once login is confirmed after #20.

Step 1 is up as #20. Live state captured 2026-10-19 (via the operator's service-account token against `GET /v1/oauth2/scout-hedgedoc` and `/v1/group/scout_hedgedoc_users`; secrets not recorded): | attribute | value | |---|---| | displayname | `notes.m.ac.nz` | | oauth2_rs_origin (redirect) | `https://notes.m.ac.nz/auth/oauth2/callback` | | oauth2_rs_origin_landing | `https://notes.m.ac.nz/` | | oauth2_rs_scope_map | `scout_hedgedoc_users` → `email groups openid profile` | | claim maps / sup scope maps | none | | oauth2_strict_redirect_uri | already `true` | | prefer short username / disable PKCE | unset (defaults) | | uuid | `8d0b611d-787a-4bfd-bf2e-b5e18676a965` | | group `scout_hedgedoc_users` members | `sophie` (uuid `55cb07f0-899d-4289-8a0f-018fdf7ec2ca`) | The CRs in #20 reproduce exactly this, so adoption is a no-op in Kanidm. Step 2 (remove the `OnePasswordItem`) follows once login is confirmed after #20.
Author
Collaborator

#20 merged and verified on scout: OAuth2Client scout-hedgedoc and Group scout-hedgedoc-users are Ready with adopted: true (uuids match the capture above), Secret scout-hedgedoc-oidc has 4 keys, deploy/hedgedoc rolled out, and the hedgedoc log shows successful OAuth2 callback exchanges through the new pod. The only change in Kanidm was the group description (DescriptionUpdated event), as intended.

Step 2 is up as #21.

#20 merged and verified on scout: `OAuth2Client` scout-hedgedoc and `Group` scout-hedgedoc-users are Ready with `adopted: true` (uuids match the capture above), Secret scout-hedgedoc-oidc has 4 keys, deploy/hedgedoc rolled out, and the hedgedoc log shows successful OAuth2 callback exchanges through the new pod. The only change in Kanidm was the group description (DescriptionUpdated event), as intended. Step 2 is up as #21.
Author
Collaborator

#21 merged and verified on scout: Application hedgedoc Synced/Healthy at 63399f3, the OnePasswordItem and Secret hedgedoc-oidc-idmacnz are gone, scout-hedgedoc and scout-hedgedoc-users are still Ready, and the hedgedoc pod is unchanged (no restart). Remaining manual step: archive the 1Password item hedgedoc-oidc-idmacnz in vault homelab.

Closing: acceptance met.

#21 merged and verified on scout: Application hedgedoc Synced/Healthy at 63399f3, the `OnePasswordItem` and Secret `hedgedoc-oidc-idmacnz` are gone, `scout-hedgedoc` and `scout-hedgedoc-users` are still Ready, and the hedgedoc pod is unchanged (no restart). Remaining manual step: archive the 1Password item `hedgedoc-oidc-idmacnz` in vault `homelab`. Closing: acceptance met.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
sophie/homelab#19
No description provided.