fix(secrets-engine): v2 adds the tenant rule v1 never had
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Build and Publish Container Image / build-and-push (push) Successful in 41s

secrets-engine.catalog-lane.lifecycle v1 contained no reference to
input.tenant — not in well_formed, not in the denial ladder, not in a
test. A rotate on lane:glas-primary under tenant:coulomb returned allow
against the deployed package (decision:066e629bbf0c0924).

Found while answering glas-harness's tenant-alignment request, which had
asked for wrong-tenant denial evidence. There was none to return.

Three covers failed the same way: every one of the 29 fixtures carried
tenant:platform, so the suite could not report on the field; T02's own
gate named "wrong-tenant deny" and was recorded done unmet; and the
engine hashes tenant into request_digest but never compares it. Four
other published packages carry the branch — this one was the outlier.

v2 adds wrong_tenant above wrong_system, three Rego tests and three
fixtures (28/28, 32/32). The absent-tenant test caught a second defect
in the first draft: a bare input.tenant != comparison is undefined on a
missing key, so the branch dropped and the ladder reported the wrong
rung. request_tenant := object.get(input, "tenant", "") fixes it.

v2 supersedes rather than amends v1 because the defect failed open: a
consumer pinned to _VERSION=v1 would keep receiving allows with no
signal the rule beneath the version string had changed. The earlier
dual-control correction stayed at v1 because it denied everything.

Replay envelopes regenerated at v2; both request_digest values are
byte-identical, so secrets-engine's digest join needs no re-pinning.

The sweep this prompted found tenant-engine unscoped on tenant as well —
deployed, and verified allowing tenant:coulomb. Not the same fix: its
request tenant names the target rather than the caller, so a constant
would break it. Recorded and carried by FLEX-WP-0022 rather than patched
unilaterally.

FLEX-WP-0021 closes at T05; the pin still serves v1 until a redeploy.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014aQMM1dPXaPiXVn6DwwtLd

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 715613@bnt-lap001
Assistant-Session: fabd95c1-4c9e-4080-8849-8707ae025f80
This commit is contained in:
tegwick 2026-09-06 20:38:45 +02:00
parent a81697a589
commit d98323b2bb
12 changed files with 690 additions and 28 deletions

View file

@ -6,11 +6,11 @@ catalog-lane operations. Opened by `FLEX-DEC-2026-005`, carried by
| File | What it is |
| --- | --- |
| `policy_package.md` | `secrets-engine.catalog-lane.lifecycle` v1, `allow_ttl: 15m` |
| `policy_package.md` | `secrets-engine.catalog-lane.lifecycle` v2, `allow_ttl: 15m` |
| `protected_system_manifest.yaml` | the `secret-catalog-lane` resource type and twelve actions |
| `subject_manifest.yaml` | the single `secrets-engine` service identity |
| `registry_snapshot.json` | loadable snapshot combining both manifests |
| `policy_fixtures.yaml` | 29 fixtures — 11 allows, dual control both ways, and every denial branch |
| `policy_fixtures.yaml` | 32 fixtures — 11 allows, dual control both ways, and every denial branch |
| `check_request_*.json` | standalone requests for `POST /v1/check` |
The action vocabulary is **secrets-engine's**, delivered under
@ -25,12 +25,30 @@ go run ./cmd/flex-auth validate -kind policy -file examples/secrets-engine/polic
go run ./cmd/flex-auth load-registry -file examples/secrets-engine/registry_snapshot.json
```
25 Rego tests and 29 fixtures.
28 Rego tests and 32 fixtures.
## Not yet deployed
## Deployed, and the version to pin
There is no `flex-auth-secrets-engine` pin yet (`FLEX-WP-0021-T04`), so
secrets-engine has no address to call. Their policy pin
(`SECRETS_ENGINE_AUTHORIZATION_POLICY_PACKAGE` / `_VERSION`) stays **unset and
fail-closed** until `FLEX-WP-0021-T05` hands them the published package and the
Service DNS. Do not configure it from this directory.
`FLEX-WP-0021-T04` deployed the `flex-auth-secrets-engine` pin on 2026-09-06:
```text
Service: http://flex-auth-secrets-engine.flex-auth.svc.cluster.local:8080
Package: secrets-engine.catalog-lane.lifecycle
Version: v2
callerAuth.mode: warn (not enforced caller authentication)
```
Ingress admits namespace `secrets-engine` with pod label
`app.kubernetes.io/name=secrets-engine` and default-denies everything else. A
workstation CLI process is not that, and Service DNS is not workstation
connectivity — an operator-run consumer needs a decided access path before it
can call this pin at all (`FLEX-WP-0021-T04`'s three shapes).
**Pin `_VERSION` to `v2`, never `v1`.** `v1` is deployed and superseded: it had
no tenant rule and allowed a foreign tenant. See the correction section in
`policy_package.md`. The pin still serves `v1` until the redeploy lands, which
is why the version is stated here rather than left to be read off the running
service.
`SECRETS_ENGINE_AUTHORIZATION_POLICY_PACKAGE` / `_VERSION` remain fallback-free
and fail-closed by design; nothing here changes that.