Review NetKingdom Tenancy Posture v0.1 and declare flex-auth posture
Publishes tenancy.yaml (I1 A0 E2 P n/a R n/a, enables A3), the review answering rapp-postgres's three asks, and FLEX-WP-0015 to close the gaps. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
c84f34cf5f
commit
f2b3eeefd8
3 changed files with 485 additions and 0 deletions
270
docs/tenancy-posture-review.md
Normal file
270
docs/tenancy-posture-review.md
Normal file
|
|
@ -0,0 +1,270 @@
|
||||||
|
# flex-auth review — NetKingdom Tenancy Posture v0.1 (draft-5)
|
||||||
|
|
||||||
|
**Reviewer:** flex-auth
|
||||||
|
**Date:** 2026-08-17
|
||||||
|
**Subject:** `net-kingdom/canon/standards/tenancy-posture_v0.1.md`
|
||||||
|
**Requested by:** `rapp-postgres`, message `82ec43ce`
|
||||||
|
**Verdict:** accept the framework, with six amendments. Two of them are
|
||||||
|
corrections of statements the document makes about flex-auth specifically.
|
||||||
|
|
||||||
|
The framework asked to be judged by whether it can describe the reviewing
|
||||||
|
repos (§20.2). It describes flex-auth on three axes and cannot describe it on
|
||||||
|
two. Both failures are informative rather than fatal, and §3.1/§3.2 below
|
||||||
|
propose the smallest changes that fix them.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Posture vector
|
||||||
|
|
||||||
|
Declared in `tenancy.yaml` at repo root, per §5.1. Summary:
|
||||||
|
|
||||||
|
```
|
||||||
|
current: I1 A0 E2 P n/a R n/a enables A3 for consumers
|
||||||
|
target: I1 A2 E2 P n/a R n/a
|
||||||
|
```
|
||||||
|
|
||||||
|
Three of these need defending.
|
||||||
|
|
||||||
|
**I1 is correct and is not a defect.** Subject and tenant arrive in the
|
||||||
|
`CheckRequest` body, asserted by the calling PEP; flex-auth verifies no token.
|
||||||
|
§4.1 says a service reading the tenant from the request is at I1 "however
|
||||||
|
canonical the string", and that rule catches flex-auth exactly. It should. A
|
||||||
|
policy decision point evaluates claims it is handed — it cannot also be the
|
||||||
|
verifier of its own inputs without becoming the identity provider that SCOPE.md
|
||||||
|
explicitly refuses to be. flex-auth is at I1 permanently and by design, and the
|
||||||
|
framework should be able to say that a permanent low rung is a decision rather
|
||||||
|
than a stalled trajectory.
|
||||||
|
|
||||||
|
**A0 is the finding this review actually produced.** `POST /v1/check` and
|
||||||
|
`POST /v1/batch_check` authenticate no caller (`cmd/flex-auth/main.go:349`).
|
||||||
|
Any workload with network reach to the ClusterIP Service can assert any
|
||||||
|
subject and any tenant and receive an authoritative allow. flex-auth is the
|
||||||
|
estate's authorization oracle and it currently trusts its own callers
|
||||||
|
completely. That is A0 — "no authorization" — on its own inbound surface, and
|
||||||
|
it is not something we knew we were carrying before this exercise. The
|
||||||
|
framework earned its keep here. Target A2 under `FLEX-WP-0015-T02`.
|
||||||
|
|
||||||
|
**E2 is asserted, not evidenced.** Tenant scoping runs through one choke
|
||||||
|
point (`internal/decision/engine.go:188` normalisation, `:248` relationship
|
||||||
|
tenant match). But §13.2 rules E2 evidence adversarial and explicitly says a
|
||||||
|
passing CI run is not E2 evidence. We have unit tests, not a cross-tenant
|
||||||
|
adversarial probe. E2 is therefore claimed with its gap stated rather than
|
||||||
|
claimed clean, which we read as what §6 requires.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Ruling on A4
|
||||||
|
|
||||||
|
**A4 is the right shape for the top of the ladder. Accept it. flex-auth
|
||||||
|
records it as a target, not a current level, and not an active project.**
|
||||||
|
|
||||||
|
Three qualifications, one of which is a correction.
|
||||||
|
|
||||||
|
### 2.1 Correction — AuthZEN does not remove the coupling §4.2 attributes to it
|
||||||
|
|
||||||
|
§4.2 justifies A4 by the action strings copied verbatim between repos, calling
|
||||||
|
that "exactly the coupling AuthZEN removes". It is not. AuthZEN 1.0
|
||||||
|
standardises the *envelope* — subject, action, resource, context, and the
|
||||||
|
evaluation endpoint — and deliberately does not standardise the action
|
||||||
|
vocabulary or the policy language. At A4, `tenant.guardrail.set` still has to
|
||||||
|
be agreed between flex-auth and tenant-engine and still gets copied verbatim.
|
||||||
|
The wire format becomes portable; the semantics do not.
|
||||||
|
|
||||||
|
This matters because the two problems have different and independently
|
||||||
|
achievable fixes, and the cheaper one is not A4:
|
||||||
|
|
||||||
|
| Problem | Fix | Cost |
|
||||||
|
|---|---|---|
|
||||||
|
| PEP coupled to flex-auth's request shape | AuthZEN endpoint (A4) | New adapter, needs a second PEP to be worth anything |
|
||||||
|
| Action strings re-derived by copying | Publish the vocabulary flex-auth already holds | Small — `registry.Actions` is already structured |
|
||||||
|
|
||||||
|
flex-auth's registry already carries `[]ActionDefinition` per system. Serving
|
||||||
|
it read-only would end the copying without touching the wire protocol.
|
||||||
|
Recommend §4.2 drop the vocabulary argument as its justification for A4 and
|
||||||
|
carry the interoperability argument, which is sound on its own.
|
||||||
|
|
||||||
|
### 2.2 The A4 evidence artifact proves less than it appears to
|
||||||
|
|
||||||
|
§13 requires "a second PDP substituted without PEP change". That is
|
||||||
|
achievable and worth having, but it demonstrates *interface* portability, not
|
||||||
|
*decision* equivalence — the substitute PDP must be independently authored to
|
||||||
|
the same policy semantics, and a substitution that returns different answers
|
||||||
|
still passes the artifact as written. §13.4 sets the standard that an artifact
|
||||||
|
must assert something achievable; by the same standard it should assert what
|
||||||
|
it actually establishes. Recommend the artifact read: "a second PDP
|
||||||
|
substituted without PEP change, with the decision differences between the two
|
||||||
|
recorded" — differences expected and enumerated, not asserted absent.
|
||||||
|
|
||||||
|
### 2.3 Adoption stance
|
||||||
|
|
||||||
|
An AuthZEN `POST /access/v1/evaluation` endpoint mapping onto the existing
|
||||||
|
engine is a small non-breaking addition — a translation layer beside
|
||||||
|
`/v1/check`, not a replacement. flex-auth declines to build it now: it buys
|
||||||
|
swappability for PEPs, and today every PEP in the estate is ours and there is
|
||||||
|
no second engine to swap to. It becomes worth building when a consumer arrives
|
||||||
|
that we do not control, or when a delegated backend under FLEX-WP-0004 needs
|
||||||
|
to be addressed uniformly. Recorded as `FLEX-WP-0015-T04`, status `wait`, with
|
||||||
|
that trigger written down so it is a decision rather than a backlog item.
|
||||||
|
|
||||||
|
The coupling is therefore carried **deliberately**, which was the question
|
||||||
|
asked.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Ruling on the internal-hop question
|
||||||
|
|
||||||
|
**The assertion is right. Its factual premise about flex-auth is wrong today,
|
||||||
|
and the scope needs one amendment.**
|
||||||
|
|
||||||
|
### 3.1 Correction — flex-auth does not call tenant-engine on the authorization path
|
||||||
|
|
||||||
|
§4.2 states as fact: "`flex-auth` calls `tenant-engine` synchronously on the
|
||||||
|
authorization path." It does not, as of this commit.
|
||||||
|
|
||||||
|
`internal/adapters/tenantengine/` exists and is complete — an HTTP client for
|
||||||
|
`GET /tenants/{id}/roles/live`, a `LiveRolesResult`, and `AttachToContext` to
|
||||||
|
fold live roles into decision context. It has **no non-test caller**. The
|
||||||
|
decision engine has no field for it and no hook that would invoke it
|
||||||
|
(`internal/decision/engine.go:21`). The IAM Profile's live re-query capability
|
||||||
|
is built and unwired.
|
||||||
|
|
||||||
|
Two consequences for the framework:
|
||||||
|
|
||||||
|
- The internal-hop anti-pattern it worries about is not live in flex-auth,
|
||||||
|
because the internal hop is not live at all.
|
||||||
|
- flex-auth's `I` cannot reach I3 today for the same reason. We had assumed
|
||||||
|
otherwise before this review; that assumption is now corrected in
|
||||||
|
`tenancy.yaml` and tracked as `FLEX-WP-0015-T03`, whose honest outcome is
|
||||||
|
either wiring the adapter or deleting it. A built-and-unwired adapter is the
|
||||||
|
worst of the three states because it reads as capability.
|
||||||
|
|
||||||
|
### 3.2 The assertion itself — correct, with a scoping amendment
|
||||||
|
|
||||||
|
"A service identity acting on behalf of a tenant must carry and revalidate
|
||||||
|
tenant context to claim A2 or above" is right in intent and too strong as
|
||||||
|
written. Read literally it requires revalidation on *every* internal hop, which
|
||||||
|
in our estate means a synchronous tenant-engine call on every authorization
|
||||||
|
decision — making tenant-engine a hard availability dependency of every
|
||||||
|
decision the estate makes, and putting a network round trip inside a path
|
||||||
|
`tenant-engine` itself calls latency-critical. That is a worse system, and it
|
||||||
|
is the kind of requirement services quietly stop meeting.
|
||||||
|
|
||||||
|
Recommend:
|
||||||
|
|
||||||
|
> Tenant context MUST be carried on every internal hop and MUST NOT be
|
||||||
|
> re-derived from a service identity. It MUST be revalidated against
|
||||||
|
> `tenant-engine` at least once per request chain — at the service that holds
|
||||||
|
> or mutates the tenant's data, or before a privileged, destructive,
|
||||||
|
> credential-vending or `aal2`-class decision, whichever comes first. A hop
|
||||||
|
> that neither holds tenant data nor makes such a decision may carry the
|
||||||
|
> context without revalidating it.
|
||||||
|
|
||||||
|
This preserves the OWASP intent exactly — no hop may treat "internal" as
|
||||||
|
grounds for skipping the check, and nothing may substitute a service identity
|
||||||
|
for the tenant — while putting the revalidation where it can actually be
|
||||||
|
evidenced.
|
||||||
|
|
||||||
|
### 3.3 The real internal-hop gap in flex-auth is on a different axis
|
||||||
|
|
||||||
|
Worth stating plainly because the framework would not surface it: flex-auth
|
||||||
|
cannot today distinguish "user-engine asking on behalf of tenant X" from "any
|
||||||
|
pod in the cluster asking on behalf of tenant X". The tenant context is
|
||||||
|
carried; the *caller* is not authenticated at all. So the internal-hop
|
||||||
|
weakness here is not a missing revalidation, it is a missing caller identity —
|
||||||
|
axis A, not axis I. A framework question aimed at I surfaced an A0. Recommend
|
||||||
|
§4.2's internal-hop paragraph note that carrying tenant context is worthless
|
||||||
|
without an authenticated hop to carry it over.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Further amendments
|
||||||
|
|
||||||
|
### 4.1 The A ladder is written from the PEP's seat; the PDP has no seat
|
||||||
|
|
||||||
|
§3 names flex-auth the vocabulary owner for axis A, and then the ladder's top
|
||||||
|
three rungs describe delegating *to* flex-auth. flex-auth cannot occupy A3 —
|
||||||
|
"decisions delegated to flex-auth as PDP" is not a thing the PDP can do. The
|
||||||
|
document's own worked-examples table (§5) omits flex-auth, which we read as
|
||||||
|
this problem surfacing implicitly.
|
||||||
|
|
||||||
|
Recommend the A ladder state that it describes **enforcement points**, and
|
||||||
|
that a decision point declares two numbers: its own inbound level, and the
|
||||||
|
maximum level it enables for consumers. flex-auth then reads `A0, enables A3`
|
||||||
|
— which is both accurate and considerably more alarming than `A3`, correctly.
|
||||||
|
`tenancy.yaml` uses `enables_for_consumers` pending a canonical field name.
|
||||||
|
|
||||||
|
### 4.2 P and R have no rung for a service with no datastore
|
||||||
|
|
||||||
|
flex-auth holds nothing at rest. Registry snapshot and policy package are
|
||||||
|
baked into the image and mounted read-only; production runs with no `--log`,
|
||||||
|
so not even the decision log persists (`deploy/flex-auth-user-engine.yaml`).
|
||||||
|
|
||||||
|
P0 is the framework's floor and it presupposes a database — "shares a database
|
||||||
|
with another consumer". R0 presupposes retained data — "kept indefinitely by
|
||||||
|
default". Both would be false of flex-auth. §6 makes overclaiming the only
|
||||||
|
violation, so a missing rung *forces* the violation it prohibits: a stateless
|
||||||
|
service must either fabricate a level or stay outside the framework, which is
|
||||||
|
the exact failure §1 says draft-1 was rejected for.
|
||||||
|
|
||||||
|
Recommend an explicit `n/a` value on P and R, admissible only with a stated
|
||||||
|
reason, and a line in §6 that `n/a` is a conformant declaration. `tenancy.yaml`
|
||||||
|
uses it already.
|
||||||
|
|
||||||
|
### 4.3 No declared location makes §12's "guard" unimplementable
|
||||||
|
|
||||||
|
§5.1 says the vector is declared in the repo. It does not say where, or in
|
||||||
|
what format. §12 requires continuous verification "against the service's own
|
||||||
|
declaration" — which needs a path a checker can read without per-repo
|
||||||
|
archaeology. Recommend fixing `tenancy.yaml` at repo root, with the schema in
|
||||||
|
§5. We have adopted that name speculatively; happy to move it.
|
||||||
|
|
||||||
|
### 4.4 §13.1 makes the most common declaration unevidenceable
|
||||||
|
|
||||||
|
"A level is claimed only with its evidence artifact present." The evidence
|
||||||
|
table defines artifacts from I2, A2, E1, P1 and R2 upward. It defines none for
|
||||||
|
I0/I1, A0/A1, E0, P0, R0/R1 — precisely the rungs of the newly absorbed repo
|
||||||
|
that §6 was written to accommodate, and precisely the rungs flex-auth declares
|
||||||
|
on two axes. Under a literal reading of §13.1 our `A0` is unclaimable.
|
||||||
|
|
||||||
|
Recommend: at or below the "no control" rung, a declaration requires a stated
|
||||||
|
reason rather than an artifact. Evidence is what stops you overclaiming; there
|
||||||
|
is nothing to overclaim at the bottom of a ladder.
|
||||||
|
|
||||||
|
### 4.5 Minor — `service_class` has no home in the vector schema
|
||||||
|
|
||||||
|
§8.3.2 requires a declared service class; §5's schema has no field for it.
|
||||||
|
`tenancy.yaml` puts it beside `reviewed`. Suggest §5 add it explicitly.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. On BOLA coverage and `whitehat-security`
|
||||||
|
|
||||||
|
Noted and welcomed. flex-auth's E2 claim above is exactly the kind that should
|
||||||
|
not be self-certified, and we would rather have it tested by a facility with
|
||||||
|
no stake in the answer than assert it from code review. When
|
||||||
|
`WHITEHAT-WP-0001` is ready to take targets, flex-auth volunteers its
|
||||||
|
cross-tenant decision path: the specific question is whether a
|
||||||
|
`CheckRequest` asserting tenant A can obtain an allow against a resource
|
||||||
|
belonging to tenant B through relationship inheritance
|
||||||
|
(`internal/decision/engine.go:248` is the only thing preventing it).
|
||||||
|
|
||||||
|
We agree with the reasoning in §19.3 and note it matches our own and
|
||||||
|
tenant-engine's August position: the fleet-level answer needed a home, not a
|
||||||
|
volunteer.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. What flex-auth is doing about this
|
||||||
|
|
||||||
|
`FLEX-WP-0015` — five tasks, one of which is the real work:
|
||||||
|
|
||||||
|
| Task | |
|
||||||
|
|---|---|
|
||||||
|
| T01 | Publish the posture vector and this review; reply to `rapp-postgres` |
|
||||||
|
| T02 | **Close the A0**: decide and record how `/v1/check` authenticates its callers |
|
||||||
|
| T03 | Wire or delete the tenant-engine live-roles adapter |
|
||||||
|
| T04 | AuthZEN endpoint — `wait`, with a written trigger |
|
||||||
|
| T05 | Guard: mechanical check that `tenancy.yaml` still matches the code |
|
||||||
|
|
||||||
|
Nothing in this review changes a running system, and T02 will not be applied
|
||||||
|
to production without the usual staged-promotion path.
|
||||||
62
tenancy.yaml
Normal file
62
tenancy.yaml
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
# flex-auth tenancy posture declaration
|
||||||
|
# Framework: net-kingdom/canon/standards/tenancy-posture_v0.1.md (§5)
|
||||||
|
# Conformance rule (§6): accuracy, not altitude. This file overclaims nothing.
|
||||||
|
# Reasoning and evidence: docs/tenancy-posture-review.md
|
||||||
|
|
||||||
|
service: flex-auth
|
||||||
|
role: policy-decision-point
|
||||||
|
|
||||||
|
tenancy:
|
||||||
|
current:
|
||||||
|
I: 1
|
||||||
|
A: 0
|
||||||
|
E: 2
|
||||||
|
P: "n/a"
|
||||||
|
R: "n/a"
|
||||||
|
target:
|
||||||
|
I: 1
|
||||||
|
A: 2
|
||||||
|
E: 2
|
||||||
|
P: "n/a"
|
||||||
|
R: "n/a"
|
||||||
|
reviewed: "2026-08-17"
|
||||||
|
service_class: latency-critical
|
||||||
|
|
||||||
|
# flex-auth is the PDP, not a PEP. The A ladder as written describes
|
||||||
|
# enforcement points delegating outward; flex-auth is the thing delegated to.
|
||||||
|
# Two numbers are therefore needed and only one has a slot (see review §3.1).
|
||||||
|
enables_for_consumers: 3
|
||||||
|
|
||||||
|
gap:
|
||||||
|
I: >-
|
||||||
|
Subject and tenant arrive in the CheckRequest body, asserted by the
|
||||||
|
calling PEP. flex-auth verifies no token and holds no session. This is
|
||||||
|
I1 by §4.1's literal rule and is structurally correct for a PDP: the
|
||||||
|
decision point judges asserted claims, it cannot be the verifier of its
|
||||||
|
own inputs. Not a defect and not a target for movement.
|
||||||
|
A: >-
|
||||||
|
POST /v1/check and /v1/batch_check authenticate no caller. Any workload
|
||||||
|
with network reach to the ClusterIP Service can assert any subject and
|
||||||
|
any tenant and receive an authoritative decision. Mitigated only by
|
||||||
|
cluster-internal exposure. Target A2 (single inbound choke point binding
|
||||||
|
caller identity) under FLEX-WP-0015-T02.
|
||||||
|
E: >-
|
||||||
|
No tenant data at rest. Tenant scoping in decisions runs through one
|
||||||
|
choke point (internal/decision/engine.go normalizeRequest and the
|
||||||
|
relationship tenant match). E2 evidence is adversarial per §13.2 and is
|
||||||
|
not yet produced; the claim rests on code review only, which §13
|
||||||
|
does not accept. Treat E2 as asserted-pending-evidence.
|
||||||
|
P: >-
|
||||||
|
No rung applies. flex-auth holds no datastore: registry snapshot and
|
||||||
|
policy package are baked into the image and mounted read-only, and the
|
||||||
|
production Deployment runs with no --log flag. P0 is the framework's
|
||||||
|
floor and presupposes a database. Declaring P0 would be a fabrication.
|
||||||
|
R: >-
|
||||||
|
No rung applies, same reason. No tenant data is persisted in production,
|
||||||
|
so there is nothing to retain or erase. R0 ("kept indefinitely by
|
||||||
|
default") would misdescribe a service that keeps nothing.
|
||||||
|
|
||||||
|
evidence:
|
||||||
|
A: "None. A0 is a declared absence, not a claim; see review §3.5 on low-rung evidence."
|
||||||
|
E2: "Pending adversarial artifact. Code choke point: internal/decision/engine.go:188,248"
|
||||||
|
deployment: "deploy/flex-auth-user-engine.yaml, deploy/flex-auth-tenant-engine.yaml"
|
||||||
153
workplans/FLEX-WP-0015-tenancy-posture-conformance.md
Normal file
153
workplans/FLEX-WP-0015-tenancy-posture-conformance.md
Normal file
|
|
@ -0,0 +1,153 @@
|
||||||
|
---
|
||||||
|
id: FLEX-WP-0015
|
||||||
|
type: workplan
|
||||||
|
title: "Tenancy posture declaration and inbound caller authentication"
|
||||||
|
domain: infotech
|
||||||
|
repo: flex-auth
|
||||||
|
status: active
|
||||||
|
owner: claude
|
||||||
|
topic_slug: netkingdom
|
||||||
|
planning_priority: P1
|
||||||
|
planning_order: 150
|
||||||
|
depends_on_workplans: []
|
||||||
|
related_workplans:
|
||||||
|
- FLEX-WP-0004
|
||||||
|
- FLEX-WP-0011
|
||||||
|
created: "2026-08-17"
|
||||||
|
updated: "2026-08-17"
|
||||||
|
---
|
||||||
|
|
||||||
|
# FLEX-WP-0015 - Tenancy posture declaration and inbound caller authentication
|
||||||
|
|
||||||
|
Answer `rapp-postgres`'s review request on **NetKingdom Tenancy Posture v0.1**
|
||||||
|
(message `82ec43ce`), publish flex-auth's posture vector, and close the one
|
||||||
|
real defect the review surfaced.
|
||||||
|
|
||||||
|
**Review output:** `docs/tenancy-posture-review.md`
|
||||||
|
**Declaration:** `tenancy.yaml` (repo root, per framework §5.1)
|
||||||
|
|
||||||
|
## Why this workplan exists
|
||||||
|
|
||||||
|
Three of the framework's five axes describe flex-auth cleanly. Assessing
|
||||||
|
against them turned up two things we did not know we were carrying:
|
||||||
|
|
||||||
|
1. **`/v1/check` authenticates no caller.** Any workload with network reach to
|
||||||
|
the ClusterIP Service can assert any subject and any tenant and receive an
|
||||||
|
authoritative allow. flex-auth is the estate's authorization oracle and
|
||||||
|
trusts its callers completely. Framework level `A0`.
|
||||||
|
2. **The tenant-engine live-roles adapter is built and unwired.** No non-test
|
||||||
|
caller exists. The IAM Profile live re-query reads as a capability we have
|
||||||
|
and is not one.
|
||||||
|
|
||||||
|
The framework itself needs six amendments, two of them corrections of
|
||||||
|
statements it makes about flex-auth. Those are in the review document and go
|
||||||
|
back to `rapp-postgres` as review output, not as work here.
|
||||||
|
|
||||||
|
**Scope discipline:** this workplan closes flex-auth's own gaps and answers
|
||||||
|
the review. It does not build fleet-level conformance tooling, does not adopt
|
||||||
|
AuthZEN, and does not touch production enforcement (`policy.enabled` stays off
|
||||||
|
per FLEX-WP-0007).
|
||||||
|
|
||||||
|
## Tasks
|
||||||
|
|
||||||
|
```task
|
||||||
|
id: FLEX-WP-0015-T01
|
||||||
|
status: done
|
||||||
|
priority: high
|
||||||
|
```
|
||||||
|
|
||||||
|
**Publish posture and answer the review.** Write `tenancy.yaml` and
|
||||||
|
`docs/tenancy-posture-review.md`; reply to `rapp-postgres` with the posture
|
||||||
|
vector, the A4 ruling, the internal-hop ruling, and the two factual
|
||||||
|
corrections. Done 2026-08-17.
|
||||||
|
|
||||||
|
```task
|
||||||
|
id: FLEX-WP-0015-T02
|
||||||
|
status: todo
|
||||||
|
priority: high
|
||||||
|
```
|
||||||
|
|
||||||
|
**Close the A0 — authenticate callers of `/v1/check`.** Decide first, build
|
||||||
|
second: this is a production-facing security boundary on a service two
|
||||||
|
consumers already depend on, and the wrong instrument is worse than the delay.
|
||||||
|
|
||||||
|
Record an ADR covering at minimum:
|
||||||
|
|
||||||
|
- What identifies a caller — Kubernetes ServiceAccount token review, mTLS via
|
||||||
|
the cluster's own CA, or a shared secret header. Note that the Deployment
|
||||||
|
currently sets `automountServiceAccountToken: false`, so option one changes
|
||||||
|
the pod spec.
|
||||||
|
- Whether caller identity only authenticates, or also constrains: may
|
||||||
|
`user-engine` ask questions about resources owned by `tenant-engine`? A
|
||||||
|
caller allowlist per `system` is the natural shape and flex-auth's registry
|
||||||
|
already keys on `system`.
|
||||||
|
- Fail-closed vs fail-open during rollout. Given `policy.enabled` is off in
|
||||||
|
production and both consumers are ours, a warn-only phase that logs
|
||||||
|
unauthenticated callers before rejecting them is available and probably
|
||||||
|
right.
|
||||||
|
- Whether NetworkPolicy is a sufficient interim control to declare `A1`
|
||||||
|
honestly before the real fix lands.
|
||||||
|
|
||||||
|
Target level `A2` — a single inbound choke point binding caller identity —
|
||||||
|
with the §13 evidence artifact: a test that an unbound request is refused.
|
||||||
|
|
||||||
|
Ship through the FLEX-WP-0011 staged-promotion path, not by direct apply.
|
||||||
|
|
||||||
|
```task
|
||||||
|
id: FLEX-WP-0015-T03
|
||||||
|
status: todo
|
||||||
|
priority: medium
|
||||||
|
```
|
||||||
|
|
||||||
|
**Wire or delete `internal/adapters/tenantengine`.** The adapter is complete
|
||||||
|
and has no non-test caller; `decision.Engine` has no field for it. Both
|
||||||
|
outcomes are conformant, silence is not.
|
||||||
|
|
||||||
|
Deciding factor is whether any live policy needs `tenant_roles` for a
|
||||||
|
privileged, destructive, credential-vending or `aal2`-class decision — the
|
||||||
|
IAM Profile's own trigger list. If yes, wire it into the decision path with a
|
||||||
|
timeout and an explicit unavailable-path decision (`AttachToContext` already
|
||||||
|
carries `tenant_roles_available: false` for exactly this), and note the new
|
||||||
|
synchronous dependency on tenant-engine's availability. If no, delete it and
|
||||||
|
say so in the review record rather than leaving it as latent capability.
|
||||||
|
|
||||||
|
Update `tenancy.yaml` `I` either way.
|
||||||
|
|
||||||
|
```task
|
||||||
|
id: FLEX-WP-0015-T04
|
||||||
|
status: wait
|
||||||
|
priority: low
|
||||||
|
```
|
||||||
|
|
||||||
|
**AuthZEN evaluation endpoint (framework `A4`).** Deliberately deferred, not
|
||||||
|
backlogged. `POST /access/v1/evaluation` translating onto the existing engine
|
||||||
|
beside `/v1/check`, non-breaking.
|
||||||
|
|
||||||
|
**Trigger to start:** either a PEP arrives that flex-auth's operator does not
|
||||||
|
control, or a delegated backend under FLEX-WP-0004 needs uniform addressing.
|
||||||
|
Until one of those, A4 buys swappability nobody can use.
|
||||||
|
|
||||||
|
Note when starting: AuthZEN standardises the envelope, not the action
|
||||||
|
vocabulary — it does not end action-string copying between repos. The cheaper
|
||||||
|
fix for that is serving `registry.Actions` read-only, which is independent of
|
||||||
|
this task and may be worth doing first.
|
||||||
|
|
||||||
|
```task
|
||||||
|
id: FLEX-WP-0015-T05
|
||||||
|
status: todo
|
||||||
|
priority: medium
|
||||||
|
```
|
||||||
|
|
||||||
|
**Guard the declaration.** Framework §12 requires verifying the declared
|
||||||
|
posture still holds — against our own declaration, not a universal maximum.
|
||||||
|
|
||||||
|
Small mechanical check, `make verify-posture`, in the shape tenant-engine used
|
||||||
|
for `verify-pin` (bash, no dependency tree). Assert what code can actually
|
||||||
|
prove: that the `/v1/check` handler has caller authentication iff
|
||||||
|
`tenancy.yaml` claims `A >= 2`; that `tenantengine` has a non-test caller iff
|
||||||
|
`I >= 3`; that the production Deployment carries no `--log` and no volume iff
|
||||||
|
`R` is `n/a`. Exit 1 on drift.
|
||||||
|
|
||||||
|
Designed for invisible failure per §12: posture regression returns
|
||||||
|
correct-looking responses, exactly like the two silent pin rollbacks the
|
||||||
|
estate has already been bitten by.
|
||||||
Loading…
Add table
Add a link
Reference in a new issue