net-kingdom/docs/tutorials/protected-system-flex-auth.md
tegwick 923e211365
Some checks are pending
CI Smoke / container-smoke (push) Waiting to run
CI Smoke / host-smoke (push) Waiting to run
Add flex-auth protected-system tutorial (NK-WP-0009-T05)
Offline steps exercised against the secrets-engine example; live caller-auth
steps remain unexercised.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: sonnet
Assistant-Process: 295952@bnt-lap001
Assistant-Session: e93f64ad-516c-46eb-9666-aad8d300c477
2026-09-28 23:44:44 +02:00

6.4 KiB

Add a protected system to flex-auth

Exercise status: unexercised Workplan task: NK-WP-0009-T05 Pattern(s): authorization boundary (docs/platform-identity-security-architecture.md, ADR-0006); IAM Profile v0.3 consumption

Exercise notes: the offline commands in steps 4 and 5 were run once by Claude on 2026-09-28 against the secrets-engine example in a local flex-auth checkout and produced the outcomes listed under Verification. The live caller-authentication steps (6 and 7) have not been run. Nobody has yet completed the whole tutorial, so it stays unexercised.

Outcome

Your system publishes its resources and actions to flex-auth, sends check requests carrying a projected caller identity, enforces the decision it gets back, and is refused when the caller is wrong.

Prerequisites

  • [owner: flex-auth] A local checkout of flex-auth with Go, to run go run ./cmd/flex-auth. Read flex-auth/docs/decision-record-contract.md.
  • [owner: your system's repo] A system id and a list of the things it protects and the actions it performs on them.
  • [owner: railiance-platform / package owner, ADR-0015] A Kubernetes ServiceAccount that will represent your system. Package and runtime declarations belong to the owner, not to this repo.
  • [owner: net-kingdom] Accepted IAM Profile v0.3 for the identity you present (canon/standards/iam-profile_v0.3.md).

Architecture context

flex-auth is the policy decision point; your system is the enforcement point. flex-auth never sees secret material, only coordinates (ids, types, attributes). The worked example throughout is secrets-engine (flex-auth/examples/secrets-engine/). Two gates protect a consumer: caller authentication (who is asking) and the policy decision (may they). A consumer running with caller auth in warn mode is not protected.

Steps

  1. [owner: your system's repo] Define the resource types and the action vocabulary. Actions are your system's own strings; do not infer them. Copy the shape of flex-auth/examples/secrets-engine/protected_system_manifest.yaml: resource_types (name, scope level, planes) and actions (name, capabilities, planes, exposure modes, required context). Keep a verb that is not a real gate out of the list (revoke is deliberately not an action in the example; the CLI verb gates as deactivate).
  2. [owner: your system's repo] Publish resource manifests and, for callers, a subject manifest: one service identity per protected system. Send empty lists rather than omitting or guessing attributes.
  3. [owner: flex-auth] Write the policy package (Markdown with Rego) and fixtures covering every allow and every denial branch, including wrong tenant, unknown subject and unknown action.
  4. [owner: flex-auth] Validate and load offline: go run ./cmd/flex-auth validate -kind policy -file <policy_package.md> then go run ./cmd/flex-auth load-registry -file <registry_snapshot.json>.
  5. [owner: flex-auth] Evaluate a check request locally: go run ./cmd/flex-auth check -registry <registry_snapshot.json> -policy <policy_package.md> -request <request.json>. A request is tenant, subject, action, resource and context. The result is a decision envelope (flex-auth.decision-record.v1) with effect, reason, and a deterministic decision id.
  6. [owner: package owner] Give the caller a projected identity: a short-lived, audience-scoped ServiceAccount token, and register the exact binding <system>=system:serviceaccount:<namespace>:<serviceaccount> on the flex-auth pin with audience flex-auth. Send the token as Authorization: Bearer. Use the in-cluster trailing-dot FQDN of the pin, never a bare service name.
  7. [owner: flex-auth] Set caller authentication to enforce and run the four negative tests from flex-auth/docs/operator-caller-access-path.md (no header, wrong ServiceAccount, wrong audience, expired token).
  8. [owner: your system's repo] Enforce the envelope: allow proceeds; deny blocks; redact and audit_only apply their obligations; any error or missing decision fails closed. Persist the decision id with every deny, redaction, export and privileged action.

Verification

Done when:

  • validate and load-registry exit 0 on your package and snapshot.
  • Offline check returns allow for a permitted request, and deny with reason wrong_tenant, unknown_subject and unknown_action for the matching negative requests. (Observed on the secrets-engine example: allow / catalog_lane_policy_matched; deny / wrong_tenant; deny / unknown_action for revoke; deny / unknown_subject.)
  • Against the live pin under enforce: a valid token returns 200 and an allow; no header returns 401; a token for the wrong ServiceAccount returns 403; a wrong-audience token returns 401; an expired token returns 401. All four negatives must be refused before policy is evaluated.
  • A pin whose logs still show warn is reported as not enforcing. That is a failed check, not a pass.

Rollback

  • Offline artifacts are files; revert them in git.
  • On the live pin, set caller authentication back only as a recorded, time-boxed exception owned by the package owner, and never leave a consumer in warn as the resting state.
  • Removing a protected system means removing its binding and manifests; consumers must then fail closed.

Threat checks

  • Never send a permanent token; mint short-lived tokens per use.
  • Never point a consumer at a bare in-cluster service name; workstation resolvers can answer it with an unrelated host. Use the trailing-dot FQDN.
  • A port-forward bypasses NetworkPolicy, so caller authentication is the only gate on an operator path.
  • Never put secret values in a resource attribute or a check request.
  • Do not give raw upstream group names platform meaning; map them explicitly per tenant.
  • Do not apply stale tenant-engine reference YAML; take manifests from the current owner package.

Ownership notes

Concern Owner
Action vocabulary, resource manifests, enforcement of decisions the protected system's repo
Policy packages, decision envelope, caller-auth verification flex-auth
ServiceAccount, package and runtime declaration package owner under ADR-0015
Identity contract (IAM Profile v0.3) net-kingdom
Credential routing for the token ops-warden (pointer only)