ADR-0006: enforcement is zone-scoped; defer the policy.enabled flip
flex-auth enforced its ops-warden pin (FLEX-WP-0016 T03) and the gate verified clean against it: readiness exits 0, decision:f3f7c88f9585582a, anonymous /v1/check now 401. Everything needed to set policy.enabled: true was in place. It stays false, by decision. policy.enabled is a single repo-wide boolean, and with fail_closed: true it makes flex-auth a hard dependency of every warden sign — including the certs the ops-bridge tunnels depend on, one of which carries the policy call itself. Uniform enforcement across an estate being actively rebuilt hardens the access needed to perform the rebuild. The repo already refuses one-dimensional posture: WP-0015 shipped environment and maturity axes, WP-0029 added organization_posture. A global flag ignores all three. ADR-0006 records that enforcement belongs to a zone, and binds future work — a zone-blind enforcement flag is out of order, not merely unwise. WARDEN-WP-0032 drafts the zone model, leading with the ownership question: whether this is ops-warden's to own or NetKingdom canon to consume (ADR-0005). WP-0031 is finished with T05 cancelled and resuming as WP-0032-T05. Also replaces the hand-run kubectl port-forward with a managed ops-bridge tunnel, flex-auth-ops-warden-railiance01 (-L 19090:10.43.1.165:8080). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
661176e37a
commit
cbf6828061
7 changed files with 273 additions and 9 deletions
|
|
@ -13,6 +13,7 @@ distinction that matters here.
|
|||
| `ADR-0003` | Cover gaps, but never silently own them |
|
||||
| `ADR-0004` | High-risk lanes refuse raw value streaming to agent sessions |
|
||||
| `ADR-0005` | Implement one lane narrowly, route everything else |
|
||||
| `ADR-0006` | Enforcement is zone-scoped, never a global flag |
|
||||
|
||||
### Owned versus inherited — check `owner:` before changing anything
|
||||
|
||||
|
|
|
|||
12
SCOPE.md
12
SCOPE.md
|
|
@ -153,6 +153,7 @@ superseding ADR, never an in-place edit.
|
|||
| `ADR-0003` | Cover gaps, but never silently own them |
|
||||
| `ADR-0004` | High-risk lanes refuse raw value streaming to agent sessions |
|
||||
| `ADR-0005` | Implement one lane narrowly, route everything else |
|
||||
| `ADR-0006` | Enforcement is zone-scoped, never a global flag (`policy.enabled` stays off until WP-0032) |
|
||||
|
||||
Rules we follow but do not own — NetKingdom canon, the IAM profile, the
|
||||
credential-management standard, the-custodian's ADR-001 workplan convention — are
|
||||
|
|
@ -277,7 +278,7 @@ for the rest.
|
|||
| WP | Status | Focus |
|
||||
| --- | --- | --- |
|
||||
| WP-0027 | `backlog` | Tamper-resistant credential governance + mass rotation/lockdown (Strand B follow-on to WP-0026) |
|
||||
| WP-0031 | `active` | Calling-side identity for flex-auth — code + gate done; flip sequence waits on operator + flex-auth enforce |
|
||||
| WP-0032 | `proposed` | Security zones — scope enforcement to bands of differing rigidity (`ADR-0006`); unblocks the pre-sign gate |
|
||||
| WP-0030 | `proposed` | Delegation register — record intended owner + blocker on every interim lane, `warden route gaps`, promotion gate |
|
||||
|
||||
Remaining production distance is also in other repos' lanes (see Known gaps).
|
||||
|
|
@ -286,7 +287,6 @@ Remaining production distance is also in other repos' lanes (see Known gaps).
|
|||
|
||||
| Gap | Owner | Notes |
|
||||
| --- | --- | --- |
|
||||
| `callerAuth.mode: enforce` on the ops-warden pin | flex-auth | **FLEX-WP-0016 T03** — waits on WARDEN-WP-0031 T04, then unblocks `policy.enabled: true` |
|
||||
| ops-bridge `cert_command` on live tunnels | ops-bridge | Playbook + readiness gate shipped (WP-0016); pilot cutover handed off, awaiting ops-bridge |
|
||||
| Principals sync warden ↔ railiance-infra | ops-warden + infra | `scripts/check_principals_drift.py` — operator runs periodically |
|
||||
| NK-WP-0009 joint SSH tutorial | net-kingdom | Parallel coordination track |
|
||||
|
|
@ -352,8 +352,12 @@ Remaining production distance is also in other repos' lanes (see Known gaps).
|
|||
- **Production sign:** verified 2026-06-18 (`history/2026-06-17-openbao-production-verify.md`)
|
||||
- **Access routing:** WP-0010 + WP-0011 shipped (`warden route`, pointer catalog)
|
||||
- **Policy gate:** caller shipped (WP-0007); registry + smoke complete (WP-0009 archived).
|
||||
`policy.enabled: false` — the `flex-auth-ops-warden` pin is in `warn` until
|
||||
ops-warden sends a caller identity (WP-0031)
|
||||
WP-0031 shipped the calling identity and flex-auth's pin now runs
|
||||
`callerAuth.mode: enforce` (FLEX-WP-0016) — the gate is **ready and verified**
|
||||
(`decision:f3f7c88f9585582a`, anonymous `/v1/check` -> 401). `policy.enabled`
|
||||
remains **false by decision**, not by blocker: `ADR-0006` scopes enforcement to
|
||||
security zones, which WP-0032 defines. Re-check with
|
||||
`scripts/check_policy_caller_identity.py`
|
||||
- **Workload posture:** WP-0015 shipped (standard, descriptors, `warden policy`,
|
||||
conformance checker, dev doubles); canon landing owner-driven
|
||||
- **ops-bridge cert_command:** WP-0016 shipped to pilot-ready (readiness gate +
|
||||
|
|
|
|||
101
docs/adr/ADR-0006-enforcement-is-zone-scoped.md
Normal file
101
docs/adr/ADR-0006-enforcement-is-zone-scoped.md
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
---
|
||||
id: ops-warden-adr-0006
|
||||
type: adr
|
||||
title: "ADR-0006 — Enforcement is zone-scoped, never a global flag"
|
||||
domain: infotech
|
||||
repo: ops-warden
|
||||
status: accepted
|
||||
version: "1.0"
|
||||
revision: "1"
|
||||
owner: ops-warden
|
||||
binds: "ops-warden"
|
||||
created: "2026-08-19"
|
||||
updated: "2026-08-19"
|
||||
last_reviewed: "2026-08-19"
|
||||
review_interval: 6m
|
||||
enforced_by: "warden.yaml policy.enabled; scripts/check_policy_caller_identity.py; WARDEN-WP-0032"
|
||||
supersedes: ""
|
||||
successor: ""
|
||||
---
|
||||
|
||||
# ADR-0006 — Enforcement is zone-scoped, never a global flag
|
||||
|
||||
## Status
|
||||
|
||||
Accepted 2026-08-19, at the moment `policy.enabled: true` was ready to be set
|
||||
and deliberately was not.
|
||||
|
||||
## Context
|
||||
|
||||
WARDEN-WP-0031 finished the calling side of the flex-auth pre-sign gate. The
|
||||
flex-auth pin `flex-auth-ops-warden` runs `callerAuth.mode: enforce`; ops-warden
|
||||
presents a bound ServiceAccount token; the readiness gate exits 0 against the
|
||||
enforcing pin and an anonymous `/v1/check` is 401. Everything needed to set
|
||||
`policy.enabled: true` was in place.
|
||||
|
||||
`policy.enabled` is a **single boolean over the whole repo**. Combined with
|
||||
`fail_closed: true` it makes flex-auth a hard dependency of *every* `warden
|
||||
sign` — including the certificates the ops-bridge tunnels depend on, one of
|
||||
which is the tunnel carrying the policy call itself. A dead tunnel or a
|
||||
recreated Service does not degrade signing; it stops it.
|
||||
|
||||
That cost might be acceptable for a settled production lane. It is not
|
||||
acceptable uniformly, because ops-warden signs across an estate that is being
|
||||
actively rebuilt. During deep refactors — CoulombCore's decommission, the
|
||||
issue-core move, cluster rebuilds — the same flag would harden exactly the
|
||||
access needed to *perform* the refactor. Security that stops the work stops
|
||||
being security and starts being an outage with good intentions.
|
||||
|
||||
The repo already refuses to treat posture as one-dimensional. WP-0015 shipped
|
||||
environment posture (`dev` / `test` / `prod`) and workload maturity (`M0`–`M3`);
|
||||
WP-0029 added `organization_posture: build` as a third axis precisely because
|
||||
the *organization's* state changes what is reasonable to demand. A global
|
||||
`policy.enabled` contradicts all of that: it is a fourth control that ignores
|
||||
the three axes already declared.
|
||||
|
||||
## Decision
|
||||
|
||||
**Enforcement posture is a property of a zone, not of the repo.** ops-warden
|
||||
does not enable a fail-closed authorization gate globally. Before
|
||||
`policy.enabled: true` is set anywhere, the zones must exist: named bands of
|
||||
differing rigidity, each declaring what is enforced, what is advisory, and what
|
||||
is exempt — and the gate must be scoped to them.
|
||||
|
||||
Concretely, until WARDEN-WP-0032 defines the zone model:
|
||||
|
||||
- `policy.enabled` stays `false`. Its readiness is evidence, not a mandate.
|
||||
- A gate that is *ready* is recorded as ready. Readiness is not a reason to
|
||||
enable; deferral with a stated reason is a legitimate terminal state for a
|
||||
task, not an unfinished one.
|
||||
- Any future enforcement control ships zone-aware or does not ship. A second
|
||||
global boolean is the defect this record exists to prevent.
|
||||
|
||||
## Consequences
|
||||
|
||||
**We accept** that the pre-sign gate remains unexercised in production longer,
|
||||
and that the WP-0031 evidence ages. Re-running
|
||||
`scripts/check_policy_caller_identity.py` re-establishes it cheaply, and the
|
||||
readiness gate exists precisely so this is a re-check rather than a re-do.
|
||||
|
||||
**We accept** that flex-auth's `flex-auth-ops-warden` pin sits enforcing with no
|
||||
enforcing consumer. That is not waste: it makes the anonymous path 401 rather
|
||||
than a decision, which was the ADHOC-2026-08-17-T01 condition regardless of
|
||||
whether ops-warden calls it.
|
||||
|
||||
**We reject** the framing that a ready control should be turned on because it is
|
||||
ready. The question is not "does it work" but "which zone is this, and does this
|
||||
zone want this failure mode."
|
||||
|
||||
**This binds future work.** A zone-blind enforcement flag proposed in any
|
||||
ops-warden workplan is out of order under this ADR, and should be sent back to
|
||||
the zone model rather than merged with a caveat in its description.
|
||||
|
||||
## Related
|
||||
|
||||
- `WARDEN-WP-0031` — the calling side that made the flip possible (T05 deferred
|
||||
under this ADR)
|
||||
- `WARDEN-WP-0032` — the zone model this record defers to
|
||||
- `wiki/WorkloadSecurityPosture.md` — the two axes already shipped (WP-0015)
|
||||
- `wiki/PolicyGatedSigning.md` — the gate itself
|
||||
- `history/2026-08-19-flex-auth-caller-identity-evidence.md` — readiness evidence
|
||||
- flex-auth `FLEX-WP-0016` — the enforcing pin
|
||||
|
|
@ -67,3 +67,4 @@ and the publication is a defect.
|
|||
| `ADR-0003` | Cover gaps, but never silently own them | ops-warden |
|
||||
| `ADR-0004` | High-risk lanes refuse raw value streaming to agent sessions | ops-warden, and any agent runtime calling `warden access` |
|
||||
| `ADR-0005` | Implement one lane narrowly, route everything else | ops-warden |
|
||||
| `ADR-0006` | Enforcement is zone-scoped, never a global flag | ops-warden |
|
||||
|
|
|
|||
|
|
@ -254,6 +254,25 @@ truncated fingerprint, never the value, so its output is safe to paste into a
|
|||
handoff message. A live `401` means the token was sent but rejected (audience or
|
||||
binding); `403` means it authenticated but may not represent `system: ops-warden`.
|
||||
|
||||
### Enablement is deferred by decision (ADR-0006)
|
||||
|
||||
`policy.enabled` is **false, and that is a decision rather than a blocker.** The
|
||||
gate is ready: flex-auth's pin runs `callerAuth.mode: enforce`, the readiness
|
||||
gate exits 0 against it, and an anonymous `/v1/check` returns 401.
|
||||
|
||||
It is not enabled because `policy.enabled` is one boolean over the whole repo,
|
||||
and with `fail_closed: true` it makes flex-auth a hard dependency of every
|
||||
`warden sign` — including the certs the ops-bridge tunnels depend on, one of
|
||||
which carries the policy call. Uniform enforcement across an estate under active
|
||||
refactor hardens the access needed to perform the refactor.
|
||||
|
||||
`ADR-0006` scopes enforcement to security zones; `WARDEN-WP-0032` defines them.
|
||||
Do not set `policy.enabled: true` outside that model — a zone-blind enablement
|
||||
is out of order under the ADR, not merely inadvisable.
|
||||
|
||||
The sequence below is retained as the *mechanics*, for whoever enables the gate
|
||||
for a zone once zones exist.
|
||||
|
||||
### Flip sequence
|
||||
|
||||
1. Configure `caller_auth`; `check_policy_caller_identity.py` exits 0 offline.
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ type: workplan
|
|||
title: "Calling-side identity for flex-auth, so policy.enabled can flip"
|
||||
domain: infotech
|
||||
repo: ops-warden
|
||||
status: active
|
||||
status: finished
|
||||
owner: ops-warden
|
||||
topic_slug: netkingdom
|
||||
planning_priority: P1
|
||||
|
|
@ -139,7 +139,7 @@ Evidence: `history/2026-08-19-flex-auth-caller-identity-evidence.md`.
|
|||
|
||||
```task
|
||||
id: WARDEN-WP-0031-T05
|
||||
status: wait
|
||||
status: cancel
|
||||
priority: high
|
||||
state_hub_task_id: "8b8e68c4-587d-490d-a98f-840aaff0fdb8"
|
||||
```
|
||||
|
|
@ -150,6 +150,21 @@ re-run the gate against the enforcing pin, and only then set
|
|||
`policy.enabled: true` with `fail_closed: true`. Flipping before enforce buys
|
||||
nothing; flipping before T04 401s every `warden sign`.
|
||||
|
||||
Asked flex-auth 2026-08-19 with the T04 evidence. Waiting on their enforce.
|
||||
The closing `warden sign` also needs a scoped `VAULT_TOKEN` via
|
||||
`ops-warden-warden-sign-token` (operator authenticates to OpenBao).
|
||||
**Deferred 2026-08-19 under `ADR-0006`** — not blocked, decided against for now.
|
||||
|
||||
flex-auth did enforce (FLEX-WP-0016 T03, Helm rev 2): the pin runs
|
||||
`--caller-auth-mode enforce`, the gate exits 0 against it
|
||||
(`HTTP 200, effect=allow, decision:f3f7c88f9585582a`), and an anonymous
|
||||
`/v1/check` is 401. Everything needed to flip was in place.
|
||||
|
||||
It was not flipped. `policy.enabled` is a single repo-wide boolean, and with
|
||||
`fail_closed: true` it makes flex-auth a hard dependency of every `warden sign`
|
||||
— including the certs the ops-bridge tunnels depend on, one of which carries
|
||||
the policy call itself. Uniform enforcement across an estate being actively
|
||||
rebuilt would harden the access needed to perform the rebuild. Enforcement
|
||||
belongs to a zone, not to the repo; that is `ADR-0006`, and the model is
|
||||
`WARDEN-WP-0032`. This task resumes as WARDEN-WP-0032-T05.
|
||||
|
||||
Also shipped while proving the flip: the gate URL is now a managed ops-bridge
|
||||
tunnel `flex-auth-ops-warden-railiance01` (`-L 19090:10.43.1.165:8080`) instead
|
||||
of a hand-run `kubectl port-forward`.
|
||||
|
|
|
|||
123
workplans/WARDEN-WP-0032-security-zones.md
Normal file
123
workplans/WARDEN-WP-0032-security-zones.md
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
---
|
||||
id: WARDEN-WP-0032
|
||||
type: workplan
|
||||
title: "Security zones — scope enforcement to bands of differing rigidity"
|
||||
domain: infotech
|
||||
repo: ops-warden
|
||||
status: proposed
|
||||
owner: ops-warden
|
||||
topic_slug: netkingdom
|
||||
planning_priority: P1
|
||||
depends_on_workplans:
|
||||
- WARDEN-WP-0015
|
||||
- WARDEN-WP-0029
|
||||
- WARDEN-WP-0031
|
||||
created: "2026-08-19"
|
||||
updated: "2026-08-19"
|
||||
---
|
||||
|
||||
# WARDEN-WP-0032 — Security zones
|
||||
|
||||
`ADR-0006` defers `policy.enabled: true` until enforcement can be scoped. This
|
||||
workplan builds the thing it defers to.
|
||||
|
||||
## The problem in one paragraph
|
||||
|
||||
ops-warden has three posture axes already: environment (`dev`/`test`/`prod`,
|
||||
WP-0015), workload maturity (`M0`–`M3`, WP-0015), and organization posture
|
||||
(`build`, WP-0029). What it does not have is a way to say *"this band of the
|
||||
estate is enforced, that one is advisory"* — so every enforcement control it
|
||||
ships is a repo-wide boolean. `policy.enabled` is the first one to actually
|
||||
become flippable, and flipping it would harden the access needed to perform the
|
||||
refactors currently underway. The axes describe posture; nothing acts on them.
|
||||
|
||||
## What a zone has to answer
|
||||
|
||||
A zone is only useful if it decides something. Minimum:
|
||||
|
||||
- **Membership** — what puts a lane, actor, or workload in this zone? Derived
|
||||
from the existing axes where possible rather than a fourth hand-maintained
|
||||
list.
|
||||
- **Enforcement stance per control** — for each gate (pre-sign policy, agent
|
||||
read-boundary, high-risk refusal): enforced, advisory, or exempt.
|
||||
- **Failure mode** — fail-closed or fail-open, per control per zone. This is the
|
||||
actual bite: `fail_closed` is what makes a dead tunnel an outage.
|
||||
- **Who may change it, and how it is observed** — a zone that can be quietly
|
||||
widened is not a boundary.
|
||||
|
||||
## Open questions to settle before designing
|
||||
|
||||
- Is a zone a property of the **actor** (`adm`/`agt`/`atm`), the **lane**
|
||||
(catalog entry), the **workload**, or the **moment** (an active refactor)? The
|
||||
CoulombCore decommission suggests the last one matters and is the hardest to
|
||||
model — a time-boxed relaxation is a different object than a standing zone.
|
||||
- Do zones compose with the three existing axes, or replace one? A fourth
|
||||
independent axis multiplies states; folding `organization_posture` in may be
|
||||
the honest move.
|
||||
- Is this ops-warden's model to own at all, or NetKingdom canon that ops-warden
|
||||
consumes? ADR-0005 says implement narrowly and route broadly. A security zone
|
||||
model that only ops-warden honours is worth much less than one flex-auth,
|
||||
secrets-engine, and tenant-engine also read — but ops-warden should not
|
||||
absorb an estate-wide model on its own authority either. **Settle this
|
||||
first**; it changes who writes the rest.
|
||||
|
||||
## Tasks
|
||||
|
||||
```task
|
||||
id: WARDEN-WP-0032-T01
|
||||
status: todo
|
||||
priority: high
|
||||
```
|
||||
|
||||
**Decide ownership before designing.** Take the third open question to
|
||||
net-kingdom (canon) and flex-auth (the PDP that would enforce it). If it is
|
||||
canon, ops-warden drafts and offers, as it did for the WP-0015 maturity model,
|
||||
and consumes the result. If it is ops-warden's, say so explicitly and record
|
||||
why. Do not build a model whose owner is unsettled.
|
||||
|
||||
```task
|
||||
id: WARDEN-WP-0032-T02
|
||||
status: todo
|
||||
priority: high
|
||||
```
|
||||
|
||||
**Name the zones against reality, not in the abstract.** Walk the current
|
||||
estate — the 27 catalog lanes, the actor inventory, the workloads with posture
|
||||
descriptors — and see what bands actually exist. A zone model that does not
|
||||
partition today's lanes cleanly is wrong, and the exceptions are the interesting
|
||||
part. Include the refactor case explicitly: what zone is a lane in while the
|
||||
component it fronts is being rebuilt?
|
||||
|
||||
```task
|
||||
id: WARDEN-WP-0032-T03
|
||||
status: todo
|
||||
priority: high
|
||||
```
|
||||
|
||||
**Model enforcement stance and failure mode per zone per control,** covering at
|
||||
minimum: the flex-auth pre-sign gate (`policy.enabled` / `fail_closed`), the
|
||||
agent read-boundary on `risk: high` lanes (ADR-0004), and the `warden plan`
|
||||
escalation verdicts (WP-0029). Each must be expressible as enforced / advisory /
|
||||
exempt with an explicit failure mode.
|
||||
|
||||
```task
|
||||
id: WARDEN-WP-0032-T04
|
||||
status: todo
|
||||
priority: medium
|
||||
```
|
||||
|
||||
**Make `policy.enabled` zone-aware and retire the global boolean.** The
|
||||
successor control reads the zone of the actor being signed for. Ship the
|
||||
migration in the same change as the deprecation — leaving both is exactly the
|
||||
second-source-of-truth failure ADR-0001 exists to prevent.
|
||||
|
||||
```task
|
||||
id: WARDEN-WP-0032-T05
|
||||
status: todo
|
||||
priority: medium
|
||||
```
|
||||
|
||||
**Close WARDEN-WP-0031 T05 through this model** — enable the pre-sign gate for
|
||||
whichever zone the SSH signing lane lands in, with that zone's failure mode.
|
||||
Re-run `scripts/check_policy_caller_identity.py` first; the WP-0031 evidence
|
||||
will be stale by then and re-establishing it is cheap by design.
|
||||
Loading…
Add table
Add a link
Reference in a new issue