10 KiB
| id | type | title | domain | repo | status | owner | topic_slug | planning_priority | planning_order | depends_on_workplans | related_workplans | created | updated | state_hub_workstream_id | ||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| FLEX-WP-0015 | workplan | Tenancy posture declaration and inbound caller authentication | infotech | flex-auth | active | claude | netkingdom | P1 | 150 |
|
2026-08-17 | 2026-08-18 | 31846b19-c2a3-428e-950b-5985bc9146eb |
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
The original five axes described flex-auth unevenly; draft-8's sixth availability axis and canonical provider declaration now cover the remaining shape. Assessing against them turned up two things we did not know we were carrying:
/v1/checkauthenticates 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 levelA0.- 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
id: FLEX-WP-0015-T01
status: done
priority: high
state_hub_task_id: "0a32500d-511a-4bd7-972e-de4cd3e62dd9"
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.
id: FLEX-WP-0015-T02
status: wait
priority: high
state_hub_task_id: "b2e87a81-b63d-4be4-ad44-01426b7e6f74"
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.
Waiting on ops-warden (asked 2026-08-17): it owns how workloads prove
identity in this estate, and it is a flex-auth PEP, so it implements the
calling side of whatever we choose. warden route find was run first per the
credential-routing rules — the catalog holds credential lanes and has no entry
for service-to-service caller authentication as a pattern, which is part of
what was asked. Our stated instinct is ServiceAccount TokenReview; the
question is open pending their answer. Nothing is built until it lands.
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-engineask questions about resources owned bytenant-engine? A caller allowlist persystemis the natural shape and flex-auth's registry already keys onsystem. - Fail-closed vs fail-open during rollout. Given
policy.enabledis 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
A1honestly 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.
2026-08-18 source outcome: ADR 0004 selects audience-scoped Kubernetes
ServiceAccount TokenReview with a separately projected reviewer identity and
exact resource.system → ServiceAccount bindings. Both check endpoints share
the choke point; missing/mismatched identity fails 401/403 and reviewer outage
fails 503. Unit and handler tests include the required unbound request. Desired
manifests select enforce mode and carry narrow TokenReview RBAC. The running
digest is unchanged, so tenancy.current.A honestly remains 0 while
tenancy.implemented.A is 2. Source committed as 1e1e077.
Remaining, in this order — the sequence is a constraint, not a preference.
ops-warden ruled on rollout (2026-08-17) and user-engine asked for the same:
- Build and pin a new immutable digest carrying the caller-auth code.
- Promote it in
warnmode, notenforce. The reviewed desired manifests indeploy/selectenforcebecause that is the end state — applying them directly is the hazard. Warn authenticates and logs failures without rejecting, so it is safe for an unmigrated caller. - Confirm the warn logs are clean of unauthenticated callers.
user-engineis migrated and deployed as of 2026-08-18 (imagesha256:c501aeb2…, token at/var/run/secrets/flex-auth-caller/token).tenant-engine's caller status is unconfirmed — asked 2026-08-18; promotingenforcewhile their client sends no token 401s every check and blocks their write paths.ops-wardenadopts the calling side on its own schedule. - Flip to
enforce, then capture the live negative probe.user-enginehas the three assertions written and ready in theirdocs/flex-auth-caller-identity.md; they deliberately have not run it, because against an unenforced digest "no token returns 401" comes back as a normal decision and would record as false evidence. - Only then may
policy.enabledflip anywhere —ops-wardennames that the real deadline, and it is the same gate as FLEX-WP-0007.
Operator gate. Steps 1–4 need cluster credentials this session does not
have: kubectl returns Unauthorized and the context is default, which
tenant-engine documented on 2026-08-16 as indistinguishable from a
wrong-cluster KUBECONFIG. Promotion follows FLEX-WP-0011 staged promotion and
the CI image build, not a hand-built image.
RISK-F-0001 (risk-nexus) tracks this A0 externally. Their NetworkPolicy
question was answered 2026-08-18: both Deployments carry an ingress policy
restricted to one namespace and one pod label, predating this work — so the
reachable set was never "any pod in the cluster", and a sentence of mine that
said so was corrected to them.
id: FLEX-WP-0015-T03
status: done
priority: medium
state_hub_task_id: "64eb7652-3b67-4bfb-879b-8588deeec8b5"
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.
Completed 2026-08-18 by deletion. Repository-wide search found no non-test
caller and no policy consuming tenant_roles; wiring it would add a
synchronous dependency without a decision that uses the result. The adapter
and its isolated tests were removed, and the review records that any future
live-role policy must introduce the dependency explicitly.
id: FLEX-WP-0015-T04
status: wait
priority: low
state_hub_task_id: "06432560-c28c-4de2-a672-87e74be54a6a"
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.
id: FLEX-WP-0015-T05
status: done
priority: medium
state_hub_task_id: "9195ba20-ab0f-4d75-b293-86978073beb1"
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.
Completed 2026-08-18. make verify-posture checks the current/implemented
distinction, both authenticated handlers, enforce-mode desired manifests,
TokenReview RBAC, absence of a latent tenant-engine caller, and the stateless
deployment assumptions behind R: n/a.