fix(secrets): list via GET ?list=true — the LIST verb dies at the proxy #116

Merged
michal merged 1 commits from fix/openbao-list-verb into main 2026-08-20 22:03:41 +00:00
Owner

Found by the probe we just shipped

First run of the new readiness probe against production:

Secrets:    bao-k8s* ✗ auth failed: OpenBao list: HTTP 400 Bad Request

Real bug, not a probe artifact. bao-k8s is configured with the public ingress URL, and Cilium's ingress Envoy rejects the non-standard LIST HTTP method outright. Verified live from the mcpd pod:

call result
LIST https://bao.ad.itaz.eu/v1/secret/metadata/mcpctl/ 400 Bad Request
GET https://bao.ad.itaz.eu/v1/secret/metadata/mcpctl/?list=true 403 permission denied (bogus token — i.e. it reached bao)
LIST http://openbao.openbao.svc:8200/... (ClusterIP, no Envoy) 403 permission denied

So the verb was fine against bao and fatal through the ingress. OpenBao accepts both forms and documents the GET form for exactly this reason.

Never noticed because nothing called list() in production — it backs mcpctl migrate secrets, which would have failed with an opaque HTTP 400 against any ingress-fronted backend.

Also included

The per-server scoping primitives for the injector work, with tests:

  • buildServerSecretPolicyHcl — names each secret explicitly, no wildcards, read-only, stable under reordering (an unstable policy body would churn the bao audit log)
  • buildServerProvisioningPolicyHcl — prefix-confined, so mcpd cannot grant itself more than it already holds
  • ensureKubernetesAuthRole + idempotent delete helpers
  • ensureServerIdentity / removeServerIdentity on the driver
  • ServerIdentityService

These are inert until the matching infra grants land (separate change in kubernetes-deployment, on its own branch).

## Found by the probe we just shipped First run of the new readiness probe against production: ``` Secrets: bao-k8s* ✗ auth failed: OpenBao list: HTTP 400 Bad Request ``` Real bug, not a probe artifact. `bao-k8s` is configured with the public ingress URL, and Cilium's ingress Envoy rejects the non-standard `LIST` HTTP method outright. Verified live from the mcpd pod: | call | result | |---|---| | `LIST https://bao.ad.itaz.eu/v1/secret/metadata/mcpctl/` | **400 Bad Request** | | `GET https://bao.ad.itaz.eu/v1/secret/metadata/mcpctl/?list=true` | 403 permission denied (bogus token — i.e. it reached bao) | | `LIST http://openbao.openbao.svc:8200/...` (ClusterIP, no Envoy) | 403 permission denied | So the verb was fine against bao and fatal through the ingress. OpenBao accepts both forms and documents the GET form for exactly this reason. Never noticed because nothing called `list()` in production — it backs `mcpctl migrate secrets`, which would have failed with an opaque HTTP 400 against any ingress-fronted backend. ## Also included The per-server scoping primitives for the injector work, with tests: - `buildServerSecretPolicyHcl` — names each secret explicitly, no wildcards, read-only, stable under reordering (an unstable policy body would churn the bao audit log) - `buildServerProvisioningPolicyHcl` — prefix-confined, so mcpd cannot grant itself more than it already holds - `ensureKubernetesAuthRole` + idempotent delete helpers - `ensureServerIdentity` / `removeServerIdentity` on the driver - `ServerIdentityService` These are inert until the matching infra grants land (separate change in kubernetes-deployment, on its own branch).
michal added 1 commit 2026-08-20 22:03:34 +00:00
fix(secrets): list via GET ?list=true — the LIST verb dies at the proxy
Some checks failed
CI/CD / lint (pull_request) Successful in 1m19s
CI/CD / typecheck (pull_request) Successful in 2m44s
CI/CD / test (pull_request) Successful in 1m30s
CI/CD / build (pull_request) Successful in 2m23s
CI/CD / smoke (pull_request) Failing after 3m4s
CI/CD / publish (pull_request) Has been skipped
7b5136491f
Found by the readiness probe added in the previous commit, on its first
run against production: `mcpctl status` reported

  Secrets:    bao-k8s* ✗ auth failed: OpenBao list: HTTP 400 Bad Request

That is a real bug, not a probe artifact. `bao-k8s` is configured with
the public ingress URL, and Cilium's ingress Envoy rejects the
non-standard LIST HTTP method outright. Verified live from the mcpd pod:

  LIST   https://bao.ad.itaz.eu/v1/secret/metadata/mcpctl/            -> 400 Bad Request
  GET    https://bao.ad.itaz.eu/v1/secret/metadata/mcpctl/?list=true  -> 403 permission denied (bogus token, i.e. reached bao)
  LIST   http://openbao.openbao.svc:8200/... (ClusterIP, no Envoy)    -> 403 permission denied

So the verb was fine against bao and fatal through the ingress. OpenBao
accepts both forms and documents the GET form for exactly this reason.

This was never noticed because nothing called list() in production —
it backs `mcpctl migrate secrets`, which would have failed with an opaque
HTTP 400 against any ingress-fronted backend.

Also adds the per-server scoping primitives Phase 2 needs, with tests:
buildServerSecretPolicyHcl (no wildcards, read-only, stable under
reordering), buildServerProvisioningPolicyHcl (prefix-confined so mcpd
cannot grant itself more than it holds), ensureKubernetesAuthRole /
delete helpers, the driver-level ensureServerIdentity/removeServerIdentity
capability, and ServerIdentityService.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018vybEitX4FykeMatKe5Xki
michal merged commit 85e53e8d1e into main 2026-08-20 22:03:41 +00:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: michal/mcpctl#116