fix(secrets-engine): v2 adds the tenant rule v1 never had
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:
parent
a81697a589
commit
d98323b2bb
12 changed files with 690 additions and 28 deletions
|
|
@ -4,7 +4,7 @@ type: workplan
|
|||
title: "secrets-engine consumer policy package and cluster-local pin"
|
||||
domain: infotech
|
||||
repo: flex-auth
|
||||
status: active
|
||||
status: finished
|
||||
owner: claude
|
||||
topic_slug: netkingdom
|
||||
planning_priority: P1
|
||||
|
|
@ -122,6 +122,19 @@ supersession (`FLEX-DEC-2026-006`). Per the `FLEX-WP-0010-T02` precedent the
|
|||
denial ladder has no `action_not_granted` branch, because one subject holding
|
||||
all twelve actions could never reach it.
|
||||
|
||||
**Reopened and re-closed 2026-09-06 at v2 — the stated gate had not been met.**
|
||||
This task's gate named "wrong-tenant deny" among the required fixtures and the
|
||||
package shipped without one, because it had no tenant rule at all: a `rotate`
|
||||
under `tenant: tenant:coulomb` returned `allow` against the deployed v1
|
||||
(`decision:066e629bbf0c0924`). Found while answering `glas-harness`'s
|
||||
tenant-alignment request, which had asked for exactly that evidence. v2 adds
|
||||
`wrong_tenant` above `wrong_system`, three Rego tests and three fixtures
|
||||
(28/28 and 32/32 passing), and supersedes v1 rather than amending it because
|
||||
the defect failed **open** — see `FLEX-DEC-2026-008`. The `T03` replay digests
|
||||
are unchanged at v2; only `policy_version` and `policy_package_digest` moved.
|
||||
The sweep this prompted found the same shape in `tenant-engine`
|
||||
(`FLEX-WP-0022`).
|
||||
|
||||
## 3. Confirm the digest join against a real decision record
|
||||
|
||||
```task
|
||||
|
|
@ -217,7 +230,7 @@ Note this also changes what `callerAuth.mode: warn` is warning about, so the
|
|||
|
||||
```task
|
||||
id: FLEX-WP-0021-T05
|
||||
status: wait
|
||||
status: done
|
||||
priority: medium
|
||||
state_hub_task_id: "f0828871-fd65-5d8c-adfd-28b13fedd2b0"
|
||||
```
|
||||
|
|
@ -233,6 +246,34 @@ Owner: `flex-auth`.
|
|||
Gate: secrets-engine can set its required configuration to published values and
|
||||
reach a pin; nothing about the fallback-free shape of that configuration changed.
|
||||
|
||||
**Done 2026-09-06, with one coordinate that is not flex-auth's to supply.**
|
||||
Handed back to `secrets-engine` and `glas-harness`:
|
||||
|
||||
```text
|
||||
Service: http://flex-auth-secrets-engine.flex-auth.svc.cluster.local:8080
|
||||
Package: secrets-engine.catalog-lane.lifecycle
|
||||
Version: v2 <- not v1; v1 is deployed and superseded
|
||||
callerAuth.mode: warn (not enforced caller authentication)
|
||||
```
|
||||
|
||||
`SCOPE.md` lists secrets-engine among the shipped consumers and
|
||||
`examples/secrets-engine/README.md` carries the coordinates at source.
|
||||
|
||||
Two things are stated rather than closed, because closing them is not ours:
|
||||
|
||||
1. **The pin serves v1 until a redeploy lands.** The over-permissive package is
|
||||
live now. Deployment approval is the user's; `FLEX-DEC-2026-008` records the
|
||||
defect and does not grant it.
|
||||
2. **There is still no verified access path for a workstation CLI.** Ingress
|
||||
admits namespace `secrets-engine` with pod label
|
||||
`app.kubernetes.io/name=secrets-engine`; a CLI on an operator's machine is
|
||||
not that, and Service DNS is not workstation connectivity. `T04`'s three
|
||||
shapes remain undecided, and `glas-harness` named the same gap independently.
|
||||
`secrets-engine` cannot complete adoption on coordinates alone.
|
||||
|
||||
The fallback-free, fail-closed shape of
|
||||
`SECRETS_ENGINE_AUTHORIZATION_POLICY_PACKAGE` / `_VERSION` is unchanged.
|
||||
|
||||
## Production deployment — 2026-09-06
|
||||
|
||||
User authorized production deployment in the Glas session. Installed dedicated
|
||||
|
|
|
|||
108
workplans/FLEX-WP-0022-tenant-scope-coverage.md
Normal file
108
workplans/FLEX-WP-0022-tenant-scope-coverage.md
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
---
|
||||
id: FLEX-WP-0022
|
||||
type: workplan
|
||||
title: "Tenant scoping is unstated in tenant-engine and untested in two more packages"
|
||||
domain: infotech
|
||||
repo: flex-auth
|
||||
status: proposed
|
||||
owner: claude
|
||||
topic_slug: netkingdom
|
||||
planning_priority: P1
|
||||
planning_order: 220
|
||||
depends_on_workplans:
|
||||
- FLEX-WP-0021
|
||||
related_workplans:
|
||||
- FLEX-WP-0010
|
||||
- FLEX-WP-0014
|
||||
created: "2026-09-06"
|
||||
updated: "2026-09-06"
|
||||
---
|
||||
|
||||
# FLEX-WP-0022 — Tenant scoping is unstated in tenant-engine and untested in two more packages
|
||||
|
||||
Opened by the sweep in `FLEX-DEC-2026-008`, which found
|
||||
`secrets-engine.catalog-lane.lifecycle` v1 allowing a foreign tenant and then
|
||||
asked whether the other packages shared the defect.
|
||||
|
||||
## What the sweep found
|
||||
|
||||
| Package | Fixture tenants | Tenant rule | State |
|
||||
| --- | --- | --- | --- |
|
||||
| `secrets-engine` | 3 distinct | added at v2 | fixed, `FLEX-DEC-2026-008` |
|
||||
| `qonto-assistant` | 2 distinct | `wrong_tenant` | fine |
|
||||
| `user-engine` | 2 distinct | `cross_tenant` | fine |
|
||||
| `ops-warden` | constant | `wrong_tenant` | rule real, fixtures do not vary it |
|
||||
| `railiance-platform` | constant | `wrong_tenant` | same |
|
||||
| `tenant-engine` | constant | **none** | deployed, unscoped |
|
||||
|
||||
Verified against `tenant-engine`: an allowed `tenant.create` re-sent under
|
||||
`tenant: tenant:coulomb` returns `allow` / `write_api_policy_matched`.
|
||||
|
||||
## Why `tenant-engine` is not the same fix
|
||||
|
||||
`secrets-engine` sends its own calling identity's tenant, so a constant
|
||||
`known_tenant` is the right rule. `tenant-engine` is different in kind: its
|
||||
subjects all sit in `tenant:platform` while its fixtures send
|
||||
`tenant:friendly:binky`, so the request tenant names the **target** of the
|
||||
operation. A service whose purpose is creating and retiring tenants acts across
|
||||
them by design, and a constant would break it on its first real call.
|
||||
|
||||
The defect today is therefore not "the rule is missing" but **"nobody can tell
|
||||
whether it is missing"**: a deliberate cross-tenant scope and an omitted rule
|
||||
look identical in the artifact. That is the same publishing-shape argument
|
||||
`gate-house` made for §12's derived-artifact rule.
|
||||
|
||||
## 1. Get the intended tenant relation from tenant-engine
|
||||
|
||||
```task
|
||||
id: FLEX-WP-0022-T01
|
||||
status: todo
|
||||
priority: high
|
||||
```
|
||||
|
||||
Owner: `flex-auth` to ask; `tenant-engine` owns the answer.
|
||||
|
||||
- Ask what the CheckRequest `tenant` denotes on the write API: the caller's
|
||||
tenant, the target tenant record, or the tenant a guardrail applies to.
|
||||
- Ask whether any of the nine write actions must be refused cross-tenant, and
|
||||
whether `tenant.guardrail.read` (which `flex-auth` itself calls) differs.
|
||||
|
||||
Gate: the relation is named by `tenant-engine`, not inferred here. This is the
|
||||
`FLEX-WP-0021-T01` rule applied to a field rather than to an action list.
|
||||
|
||||
## 2. Encode the relation, or record that there is none
|
||||
|
||||
```task
|
||||
id: FLEX-WP-0022-T02
|
||||
status: wait
|
||||
priority: high
|
||||
```
|
||||
|
||||
Owner: `flex-auth`.
|
||||
|
||||
If a relation exists, encode it in `tenant-engine.write-api.mutate` as a new
|
||||
version — fail-open corrections are visible as version changes
|
||||
(`FLEX-DEC-2026-008`) — with a fixture per side.
|
||||
|
||||
If the scope is genuinely unrestricted, say so **in the package**: a stated
|
||||
"this package is deliberately cross-tenant, because the caller administers
|
||||
tenants" is a rule a reviewer can check. Silence is not.
|
||||
|
||||
Either way the fixtures must vary `tenant`, so the suite reports on the field.
|
||||
|
||||
## 3. Vary tenant in the two suites that hold it constant
|
||||
|
||||
```task
|
||||
id: FLEX-WP-0022-T03
|
||||
status: todo
|
||||
priority: medium
|
||||
```
|
||||
|
||||
Owner: `flex-auth`.
|
||||
|
||||
`ops-warden` and `railiance-platform` have working `wrong_tenant` rules
|
||||
exercised only by Rego tests. Add a wrong-tenant deny fixture to each so the
|
||||
fixture suite covers what the rule claims. No policy change and no version bump:
|
||||
the behaviour is already correct, only the evidence is thin.
|
||||
|
||||
Gate: no package's fixture suite holds `tenant` constant.
|
||||
Loading…
Add table
Add a link
Reference in a new issue