Assistant: codex Assistant-Model: gpt-5.6-sol Assistant-Session: 01a0291a-1e87-7151-9934-fcbfe3f65eb1
6.3 KiB
Zone-aware policy-gated signing
Ops-warden asks flex-auth for a decision before SSH certificate issuance. The gate is resource-scoped through security-zone membership; there is no repo-wide enable switch.
Authority stays split:
- flex-auth owns the versioned pre-sign stance (
enforced,advisory, orexempt) and returns the decision; - ops-warden owns what the PEP does when flex-auth is unavailable or invalid;
- the workload owner declares identity and zone membership;
- zone-engine owns
security-zones_v0.1admission and resolution semantics.
Binding decisions: ADR-0009 (current) and ADR-0006 (superseded rationale).
Request path
warden sign <actor>
-> inventory, principal, actor-type, and TTL checks
-> resource id ssh-cert:actor/<actor>
-> read compiled security_zone for that resource
-> POST flex-auth /v1/check with authenticated caller identity
allow -> sign; record decision and zone evidence
audit_only -> sign; record advisory decision and zone evidence
deny -> refuse before the CA backend
unavailable/invalid
-> apply that zone's PEP failure mode
-> record fail_open when issuance proceeds
The request contains actor id/type, requested principals, TTL, and a SHA-256 fingerprint of the public key. It never contains a private key or secret value.
Compiled membership
scripts/build_flex_auth_registry.py compiles inventory actor resources. Each
actor carries an explicit zone_subject:
actors:
agt-state-hub-bridge:
type: agt
principals: [agt-task-bridge]
ttl_hours: 24
zone_subject:
applicability: applicable
workload_id: ops-bridge-tunnel
The compiler consumes zone-engine's resolved view when available and emits resource attributes:
{
"workload_id": "ops-bridge-tunnel",
"security_zone": "z2-continuity",
"security_zone_admission": "satisfied",
"security_zone_revision": "sha256:..."
}
If the workload reference or resolved membership is absent, the resource says
security_zone: unknown with a reason. A native non-workload actor/action says
security_zone_admission: not-applicable. The compiler never parses a path or
repository name to repair missing identity.
trust_zone: platform was a dormant, unrelated field and is retired. It must
not coexist with security_zone as a competing membership source.
Build the snapshot:
python3 scripts/build_flex_auth_registry.py \
~/.config/warden/inventory.yaml \
--zone-resolutions /path/to/zone-resolved-view.json \
-o registry/flex-auth/production_registry_snapshot.json
Omitting --zone-resolutions is safe: applicable actors resolve unknown, not
to a guessed zone.
PEP failure modes
The initial build profile accepted by ops-warden is:
| Zone/result | Dependency failure |
|---|---|
z0-experimental |
fail_open |
z1-operational |
fail_open |
z2-protected |
fail_open |
z2-continuity |
fail_open |
z3-critical |
fail_closed |
unknown |
fail_open under the versioned build profile |
not-applicable |
fail_closed for this pre-sign PEP |
These are dependency failure modes, not policy stance. A rendered deny always
blocks. The unknown row does not grant membership or an exception; it is the
explicit build-stage treatment until authoritative declarations land.
Configuration:
policy:
flex_auth_url: http://127.0.0.1:19090
zone_registry_path: registry/flex-auth/production_registry_snapshot.json
failure_modes:
z0-experimental: fail_open
z1-operational: fail_open
z2-protected: fail_open
z2-continuity: fail_open
z3-critical: fail_closed
unknown: fail_open
not-applicable: fail_closed
caller_auth:
mode: command
command:
- kubectl
- create
- token
- ops-warden
- -n
- ops-warden
- --audience
- flex-auth
- --duration
- 10m
policy.enabled and the global policy.fail_closed are retired. The loader
rejects either key so old and new controls cannot coexist as two sources of
truth.
Caller identity
The production flex-auth pin authenticates ops-warden with Kubernetes
TokenReview and binds resource.system: ops-warden to
system:serviceaccount:ops-warden:ops-warden. Supported token sources are:
file— projected ServiceAccount token for an in-cluster PEP;command— short-livedkubectl create tokenon a workstation;env— attended fallback;none— no identity header; only useful for an intentionally unauthenticated development evaluator.
Tokens are resolved per call, never cached, logged, or echoed. Under a fail-closed zone, an unavailable caller token blocks. Under a fail-open zone it becomes a recorded evaluator failure; ops-warden never retries anonymously with a secret copied into its own state.
Re-establish the value-safe caller proof:
python3 scripts/check_policy_caller_identity.py \
--url http://127.0.0.1:19090
Expected evidence is HTTP 200 with a decision id and anonymous HTTP 401 on the enforcing pin. The script reports only token length and a truncated fingerprint.
Audit evidence
Successful signing records:
policy_decision_idwhen flex-auth returned one;policy_zone;policy_failure_mode;policy_outcome(allow,audit_only, orfail_open).
A fail-open result must therefore be visible rather than indistinguishable from an unevaluated request. Denies do not reach the CA backend and produce no certificate.
Rollout and rollback
- Validate
tenancy.yamland the workload declarations referenced by actorzone_subjectentries. - Compile the registry and inspect unknown/not-applicable results.
- Run
scripts/check_policy_caller_identity.pyagainst the enforcing pin. - Deploy the same compiled registry revision and matching flex-auth policy package.
- Smoke an allow/advisory path, an enforced deny, and evaluator loss for one fail-open and one fail-closed zone.
Rollback is a versioned profile or registry rollback. Do not reintroduce
policy.enabled: false: that would erase per-zone evidence and recreate the
global control ADR-0009 supersedes.
See also
tenancy.yamldocs/evidence/security-zone-admission-2026-08-22.mdwiki/OpsWardenConfig.mdwiki/WorkloadSecurityPosture.mdhistory/2026-08-19-flex-auth-caller-identity-evidence.md