Auto-generate the oauth2-proxy cookie-secret (Helm lookup-generate; drop the manual SSM param) #11

Closed
opened 2026-07-07 23:39:53 +00:00 by coilyco-ops · 2 comments
Member

Ask (Kai)

The oauth2-proxy cookie-secret is just a random cookie-signing key with no cross-system meaning, yet every public ward-mcp deploy requires a manual SSM param (/<svc>/oauth2-proxy/cookie-secret, created by hand with openssl rand). That is silly - autogenerate it.

Fix: generate-once in the chart, preserve on upgrade

In chart/templates/route.yaml, stop pulling cookie-secret from the SSM ExternalSecret and instead have the chart mint it:

{{- $secName := include "ward-mcp.oauth2ProxyName" . -}}
{{- $existing := lookup "v1" "Secret" .Release.Namespace $secName -}}
{{- $cookie := (get (default dict $existing.data) "cookie-secret") | default (randAlphaNum 32 | b64enc) -}}
  • Generate on first install, preserve on every upgrade via lookup of the existing Secret - so re-rolls do NOT rotate the cookie (which would invalidate live sessions).
  • No SSM write, no manual step, no external state. Keep client-secret on the SSM ExternalSecret (it is the shared claude hash the gate still needs); only cookie-secret moves to generated.

Note on "a rollout.sh thing": for the Helm path this Helm-lookup approach is the cleaner equivalent - idempotent, needs no SSM-write permission, no rollout plumbing. If you'd rather it live in the deploy rollout as kubectl get secret || create generate-if-absent, that is the alternative, but lookup-generate is the recommendation. Either way the manual SSM param is gone.

Scope + carry-over

  • Applies immediately to skillsmp (unblocks its cookie-secret with zero manual work) and forgejo-mcp (deploy#46).
  • The oauth2-proxy secret template moves to the deploy auth-ingress template under the refactor (the sibling epic). This cookie-generation pattern carries with it - authoring it now just means skillsmp/forgejo skip the manual step in the meantime.

Acceptance

  • A fresh public ward-mcp deploy needs no hand-created cookie-secret SSM param.
  • skillsmp's oauth2-proxy boots with a generated, stable cookie-secret; helm upgrade re-rolls preserve the same value (no forced re-login).
  • Docs (chart.md operator-prereqs) drop the cookie-secret from the manual SSM list.
## Ask (Kai) The oauth2-proxy **cookie-secret** is just a random cookie-signing key with no cross-system meaning, yet every public ward-mcp deploy requires a **manual** SSM param (`/<svc>/oauth2-proxy/cookie-secret`, created by hand with `openssl rand`). That is silly - autogenerate it. ## Fix: generate-once in the chart, preserve on upgrade In `chart/templates/route.yaml`, stop pulling `cookie-secret` from the SSM ExternalSecret and instead have the chart mint it: ``` {{- $secName := include "ward-mcp.oauth2ProxyName" . -}} {{- $existing := lookup "v1" "Secret" .Release.Namespace $secName -}} {{- $cookie := (get (default dict $existing.data) "cookie-secret") | default (randAlphaNum 32 | b64enc) -}} ``` - **Generate on first install, preserve on every upgrade** via `lookup` of the existing Secret - so re-rolls do NOT rotate the cookie (which would invalidate live sessions). - **No SSM write, no manual step, no external state.** Keep `client-secret` on the SSM ExternalSecret (it is the shared claude hash the gate still needs); only `cookie-secret` moves to generated. Note on "a rollout.sh thing": for the Helm path this Helm-`lookup` approach is the cleaner equivalent - idempotent, needs no SSM-write permission, no rollout plumbing. If you'd rather it live in the deploy rollout as `kubectl get secret || create` generate-if-absent, that is the alternative, but lookup-generate is the recommendation. Either way the manual SSM param is gone. ## Scope + carry-over - Applies immediately to **skillsmp** (unblocks its cookie-secret with zero manual work) and **forgejo-mcp** (deploy#46). - The oauth2-proxy secret template moves to the deploy auth-ingress template under the refactor (the sibling epic). This cookie-generation pattern carries with it - authoring it now just means skillsmp/forgejo skip the manual step in the meantime. ## Acceptance - A fresh public ward-mcp deploy needs no hand-created cookie-secret SSM param. - skillsmp's oauth2-proxy boots with a generated, stable cookie-secret; `helm upgrade` re-rolls preserve the same value (no forced re-login). - Docs (chart.md operator-prereqs) drop the cookie-secret from the manual SSM list.
Author
Member

🔒 Reserved by ward agent --driver claude — container engineer-claude-ward-mcp-11 on host KAI-DESKTOP-TOWER is carrying this issue (reserved 2026-07-07T23:40:00Z). Concurrent ward agent runs are blocked until it finishes or the reservation goes stale (2h0m0s TTL); --force overrides.

Do not comment on or edit this issue to steer the run while it is reserved. The engineer seeded the body once at launch and never re-reads it, so a comment or edit reaches only human readers, never the running engineer. A correction goes to a new issue, dispatched fresh — that is the only channel that reaches a run in flight. Where the forge supports it, ward locks this conversation to make that a road-block rather than a convention (ward#494).

run seed context — what this run is carrying (ward#609)
  • Resolved: coilyco-flight-deck/ward-mcp#11 · branch issue-11 · driver claude · workflow direct-main
  • Run: engineer-claude-ward-mcp-11 · ward v0.422.0 · dispatched 2026-07-07T23:40:00Z
  • Comment thread: 0 included in the pre-flight read, 0 stripped (ward's own automated comments).

Issue body as seeded:

## Ask (Kai)

The oauth2-proxy **cookie-secret** is just a random cookie-signing key with no cross-system meaning, yet every public ward-mcp deploy requires a **manual** SSM param (`/<svc>/oauth2-proxy/cookie-secret`, created by hand with `openssl rand`). That is silly - autogenerate it.

## Fix: generate-once in the chart, preserve on upgrade

In `chart/templates/route.yaml`, stop pulling `cookie-secret` from the SSM ExternalSecret and instead have the chart mint it:

` ` `
{{- $secName := include "ward-mcp.oauth2ProxyName" . -}}
{{- $existing := lookup "v1" "Secret" .Release.Namespace $secName -}}
{{- $cookie := (get (default dict $existing.data) "cookie-secret") | default (randAlphaNum 32 | b64enc) -}}
` ` `

- **Generate on first install, preserve on every upgrade** via `lookup` of the existing Secret - so re-rolls do NOT rotate the cookie (which would invalidate live sessions).
- **No SSM write, no manual step, no external state.** Keep `client-secret` on the SSM ExternalSecret (it is the shared claude hash the gate still needs); only `cookie-secret` moves to generated.

Note on "a rollout.sh thing": for the Helm path this Helm-`lookup` approach is the cleaner equivalent - idempotent, needs no SSM-write permission, no rollout plumbing. If you'd rather it live in the deploy rollout as `kubectl get secret || create` generate-if-absent, that is the alternative, but lookup-generate is the recommendation. Either way the manual SSM param is gone.

## Scope + carry-over

- Applies immediately to **skillsmp** (unblocks its cookie-secret with zero manual work) and **forgejo-mcp** (deploy#46).
- The oauth2-proxy secret template moves to the deploy auth-ingress template under the refactor (the sibling epic). This cookie-generation pattern carries with it - authoring it now just means skillsmp/forgejo skip the manual step in the meantime.

## Acceptance

- A fresh public ward-mcp deploy needs no hand-created cookie-secret SSM param.
- skillsmp's oauth2-proxy boots with a gener

… (truncated to 2000 chars; full body is on this issue)

Static container doctrine and seed boilerplate are identical every run and omitted here (they ride ward v0.422.0).

— Claude (she/her), via ward agent

<!-- ward-agent-reservation --> 🔒 Reserved by `ward agent --driver claude` — container `engineer-claude-ward-mcp-11` on host `KAI-DESKTOP-TOWER` is carrying this issue (reserved 2026-07-07T23:40:00Z). Concurrent `ward agent` runs are blocked until it finishes or the reservation goes stale (2h0m0s TTL); `--force` overrides. **Do not comment on or edit this issue to steer the run while it is reserved.** The engineer seeded the body once at launch and never re-reads it, so a comment or edit reaches only human readers, never the running engineer. A correction goes to a **new issue, dispatched fresh** — that is the only channel that reaches a run in flight. Where the forge supports it, ward locks this conversation to make that a road-block rather than a convention (ward#494). <details><summary>run seed context — what this run is carrying (ward#609)</summary> - **Resolved:** `coilyco-flight-deck/ward-mcp#11` · branch `issue-11` · driver `claude` · workflow `direct-main` - **Run:** `engineer-claude-ward-mcp-11` · ward `v0.422.0` · dispatched `2026-07-07T23:40:00Z` - **Comment thread:** 0 included in the pre-flight read, 0 stripped (ward's own automated comments). **Issue body as seeded:** ``` ## Ask (Kai) The oauth2-proxy **cookie-secret** is just a random cookie-signing key with no cross-system meaning, yet every public ward-mcp deploy requires a **manual** SSM param (`/<svc>/oauth2-proxy/cookie-secret`, created by hand with `openssl rand`). That is silly - autogenerate it. ## Fix: generate-once in the chart, preserve on upgrade In `chart/templates/route.yaml`, stop pulling `cookie-secret` from the SSM ExternalSecret and instead have the chart mint it: ` ` ` {{- $secName := include "ward-mcp.oauth2ProxyName" . -}} {{- $existing := lookup "v1" "Secret" .Release.Namespace $secName -}} {{- $cookie := (get (default dict $existing.data) "cookie-secret") | default (randAlphaNum 32 | b64enc) -}} ` ` ` - **Generate on first install, preserve on every upgrade** via `lookup` of the existing Secret - so re-rolls do NOT rotate the cookie (which would invalidate live sessions). - **No SSM write, no manual step, no external state.** Keep `client-secret` on the SSM ExternalSecret (it is the shared claude hash the gate still needs); only `cookie-secret` moves to generated. Note on "a rollout.sh thing": for the Helm path this Helm-`lookup` approach is the cleaner equivalent - idempotent, needs no SSM-write permission, no rollout plumbing. If you'd rather it live in the deploy rollout as `kubectl get secret || create` generate-if-absent, that is the alternative, but lookup-generate is the recommendation. Either way the manual SSM param is gone. ## Scope + carry-over - Applies immediately to **skillsmp** (unblocks its cookie-secret with zero manual work) and **forgejo-mcp** (deploy#46). - The oauth2-proxy secret template moves to the deploy auth-ingress template under the refactor (the sibling epic). This cookie-generation pattern carries with it - authoring it now just means skillsmp/forgejo skip the manual step in the meantime. ## Acceptance - A fresh public ward-mcp deploy needs no hand-created cookie-secret SSM param. - skillsmp's oauth2-proxy boots with a gener ``` … (truncated to 2000 chars; full body is on this issue) Static container doctrine and seed boilerplate are identical every run and omitted here (they ride ward v0.422.0). </details> <!-- ward-agent-signature --> — Claude (she/her), via `ward agent`
Author
Member

WARD-OUTCOME: done - oauth2-proxy cookie-secret is now chart-minted via Helm lookup-generate; the manual SSM param is gone.

Clean little change. The recommended lookup-generate path dropped in almost verbatim from the issue - the only real design call was where the generated value lives. Since the gate's ExternalSecret owns its Secret (creationPolicy: Owner), you can't cohabit a chart-managed key in it, so the cookie-secret got its own <release>-oauth2-proxy-cookie Secret and the deployment env now points there. client-secret stays SSM-backed, untouched.

Confidence is high: I pulled a real helm binary and rendered the chart (route enabled), confirmed the cookie Secret generates a 32-byte key, the ExternalSecret now carries only client-secret, and the deployment references the new Secret. helm template always hits the generate branch (lookup is empty without a live cluster), so the preserve-on-upgrade behavior is the one thing I verified by reasoning rather than execution - it's the standard lookup idiom, so I'm not worried, but it only truly exercises against a real cluster on the first skillsmp re-roll.

One rough edge worth naming: on the next reconcile, ESO will strip cookie-secret out of the old -oauth2-proxy Secret since it's no longer in the ExternalSecret's data - harmless (nothing reads it there anymore), but a running oauth2-proxy pod won't pick up the new Secret until it restarts. A helm upgrade that changes the deployment rolls the pod anyway, so in practice it's a non-issue. No follow-ups needed; the sibling epic (moving this template into the deploy auth-ingress template) already tracks carrying the pattern forward.

WARD-OUTCOME: done - oauth2-proxy cookie-secret is now chart-minted via Helm lookup-generate; the manual SSM param is gone. Clean little change. The recommended `lookup`-generate path dropped in almost verbatim from the issue - the only real design call was where the generated value lives. Since the gate's ExternalSecret owns its Secret (`creationPolicy: Owner`), you can't cohabit a chart-managed key in it, so the cookie-secret got its own `<release>-oauth2-proxy-cookie` Secret and the deployment env now points there. client-secret stays SSM-backed, untouched. Confidence is high: I pulled a real helm binary and rendered the chart (route enabled), confirmed the cookie Secret generates a 32-byte key, the ExternalSecret now carries only client-secret, and the deployment references the new Secret. `helm template` always hits the generate branch (lookup is empty without a live cluster), so the preserve-on-upgrade behavior is the one thing I verified by reasoning rather than execution - it's the standard lookup idiom, so I'm not worried, but it only truly exercises against a real cluster on the first skillsmp re-roll. One rough edge worth naming: on the next reconcile, ESO will strip `cookie-secret` out of the old `-oauth2-proxy` Secret since it's no longer in the ExternalSecret's data - harmless (nothing reads it there anymore), but a running oauth2-proxy pod won't pick up the new Secret until it restarts. A `helm upgrade` that changes the deployment rolls the pod anyway, so in practice it's a non-issue. No follow-ups needed; the sibling epic (moving this template into the deploy auth-ingress template) already tracks carrying the pattern forward.
Sign in to join this conversation.
No description provided.