fix(ops): pin the kube-context — never run destructive steps on the ambient one #76

Merged
arcodange merged 1 commits from arcodange/kube-context-guard into main 2026-07-25 23:43:45 +02:00
Owner

Near-miss, 2026-07-25. A bin/arcodange sandbox checkpoint refresh was issued while the current kube-context was do-nyc3-kissmetrics-prod-k8s-cluster — a client production cluster. sandbox-lifecycle.sh never checked the context: it patched the ArgoCD Application, scaled erp-sandbox to zero and copied a prod secret, all against the client's cluster.

Nothing was damaged — that cluster has no application CRD and no erp/erp-sandbox namespaces, so every call failed silently under || true. That is luck, not a control. The same script on a cluster that happened to have an erp-sandbox namespace would have scaled it to zero and run DROP OWNED ... CASCADE against it.

Fix:

  • ERP_KUBE_CONTEXT (default default) pins the target; all 20 kubectl calls now route through K(), so nothing inherits the ambient context.
  • assert_arcodange_cluster() proves the target by positive fingerprint — the erp, erp-sandbox and argocd namespaces and the erp-sandbox ArgoCD Application. A client cluster cannot match all four by accident. Wired into all three entry points, before any mutation.

Verified live: refuses the client context (with the reason and the remedy), refuses an unknown context, passes on the homelab and completes sync-documents normally.

This is the kube-side twin of the Playwright host guard shipped in #75 — same failure mode (ambient environment pointing at something that is not the sandbox), same remedy (explicit target, proven before acting).

🤖 Generated with Claude Code

https://claude.ai/code/session_01VRShc4QhLLU73FLHx9vskh

**Near-miss, 2026-07-25.** A `bin/arcodange sandbox checkpoint refresh` was issued while the current kube-context was **`do-nyc3-kissmetrics-prod-k8s-cluster` — a client production cluster**. `sandbox-lifecycle.sh` never checked the context: it patched the ArgoCD Application, scaled `erp-sandbox` to zero and copied a prod secret, all against the client's cluster. Nothing was damaged — that cluster has no `application` CRD and no `erp`/`erp-sandbox` namespaces, so every call failed silently under `|| true`. **That is luck, not a control.** The same script on a cluster that happened to have an `erp-sandbox` namespace would have scaled it to zero and run `DROP OWNED ... CASCADE` against it. Fix: - `ERP_KUBE_CONTEXT` (default `default`) pins the target; all 20 `kubectl` calls now route through `K()`, so nothing inherits the ambient context. - `assert_arcodange_cluster()` proves the target by **positive fingerprint** — the `erp`, `erp-sandbox` and `argocd` namespaces **and** the `erp-sandbox` ArgoCD Application. A client cluster cannot match all four by accident. Wired into all three entry points, before any mutation. Verified live: refuses the client context (with the reason and the remedy), refuses an unknown context, passes on the homelab and completes `sync-documents` normally. This is the kube-side twin of the Playwright host guard shipped in #75 — same failure mode (ambient environment pointing at something that is not the sandbox), same remedy (explicit target, proven before acting). 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01VRShc4QhLLU73FLHx9vskh
arcodange added 1 commit 2026-07-25 23:43:40 +02:00
sandbox-lifecycle.sh scales deployments to zero, patches the ArgoCD Application
and runs DROP OWNED ... CASCADE. Every one of those ran against whatever
kube-context happened to be current.

This workstation also carries a CLIENT production cluster. On 2026-07-25 a
`checkpoint refresh` was issued while the current context was
do-nyc3-kissmetrics-prod-k8s-cluster: the script patched the ArgoCD Application,
scaled `erp-sandbox` to zero and copied a prod secret — all against the client's
cluster. Nothing was damaged only because that cluster has no `application` CRD
and no erp/erp-sandbox namespaces, so each call failed silently under `|| true`.
That is luck, not a control.

- ERP_KUBE_CONTEXT (default: "default") pins the target; every kubectl call now
  goes through K(), so nothing inherits the ambient context.
- assert_arcodange_cluster() proves the target by positive fingerprint — the
  erp, erp-sandbox and argocd namespaces AND the erp-sandbox ArgoCD Application.
  A client cluster cannot match all four by accident. Wired into all three
  entry points, before any mutation.

Verified: refuses the client context, refuses an unknown context, passes on the
homelab and completes normally.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01VRShc4QhLLU73FLHx9vskh
arcodange merged commit aad6a89e19 into main 2026-07-25 23:43:45 +02:00
arcodange deleted branch arcodange/kube-context-guard 2026-07-25 23:43:46 +02:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: arcodange-org/erp#76