Assistant: codex Assistant-Model: gpt-5.6-sol Assistant-Session: 01a0291a-1e87-7151-9934-fcbfe3f65eb1
196 lines
6.3 KiB
Markdown
196 lines
6.3 KiB
Markdown
# 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`, or
|
|
`exempt`) 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.1` admission and resolution semantics.
|
|
|
|
Binding decisions: `ADR-0009` (current) and `ADR-0006` (superseded rationale).
|
|
|
|
## Request path
|
|
|
|
```text
|
|
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`:
|
|
|
|
```yaml
|
|
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:
|
|
|
|
```json
|
|
{
|
|
"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:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```yaml
|
|
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-lived `kubectl create token` on 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:
|
|
|
|
```bash
|
|
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_id` when flex-auth returned one;
|
|
- `policy_zone`;
|
|
- `policy_failure_mode`;
|
|
- `policy_outcome` (`allow`, `audit_only`, or `fail_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
|
|
|
|
1. Validate `tenancy.yaml` and the workload declarations referenced by actor
|
|
`zone_subject` entries.
|
|
2. Compile the registry and inspect unknown/not-applicable results.
|
|
3. Run `scripts/check_policy_caller_identity.py` against the enforcing pin.
|
|
4. Deploy the same compiled registry revision and matching flex-auth policy
|
|
package.
|
|
5. 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.yaml`
|
|
- `docs/evidence/security-zone-admission-2026-08-22.md`
|
|
- `wiki/OpsWardenConfig.md`
|
|
- `wiki/WorkloadSecurityPosture.md`
|
|
- `history/2026-08-19-flex-auth-caller-identity-evidence.md`
|