flex-auth/workplans/FLEX-WP-0015-tenancy-posture-conformance.md
tegwick f2b3eeefd8
All checks were successful
CI Smoke / host-smoke (push) Successful in 1s
CI Smoke / container-smoke (push) Successful in 2s
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>
2026-08-17 18:09:59 +02:00

5.8 KiB

id type title domain repo status owner topic_slug planning_priority planning_order depends_on_workplans related_workplans created updated
FLEX-WP-0015 workplan Tenancy posture declaration and inbound caller authentication infotech flex-auth active claude netkingdom P1 150
FLEX-WP-0004
FLEX-WP-0011
2026-08-17 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

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.

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.

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.

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.

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.