WARDEN-WP-0026 T01: capabilities-safe lane verification + incident note
T01 (done): canonical capabilities-based verify pattern in the fleet promotion checklist (catalog-lane-promotion.md) and applied to the railiance-backup and forgejo-admin lane playbooks. Verification proves allow/deny via `bao token capabilities` against the KV v2 data path, never `bao kv get`; a denied default-policy token-create is a pass, not a privileged-fallback trigger. T07 (progress): lessons-learned note for the 2026-07-16 CCR-2026-0004 disclosure (three root causes). Live re-verify + rotation block remain (depend on T06). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
2ad8a53781
commit
ea98d6bf39
5 changed files with 161 additions and 3 deletions
|
|
@ -22,12 +22,50 @@ Before changing `status: draft` → `status: active`:
|
|||
| 5 | **Resolvable** | `warden route show <id> --json` shows `resolvable: true` when placeholders are documented |
|
||||
| 6 | **Tests** | Routing test or smoke proving lookup + handoff shape (no secret values in fixtures) |
|
||||
| 7 | **Review date** | Update `reviewed:` in catalog entry |
|
||||
| 8 | **Verification** | Positive + negative proof via **`bao token capabilities`** — never `bao kv get` (see below) |
|
||||
|
||||
Promotion PR touches: `registry/routing/catalog.yaml`, playbook, optional
|
||||
`tests/test_routing.py`, and a one-line note in `wiki/CredentialRouting.md` draft table.
|
||||
|
||||
---
|
||||
|
||||
## Capabilities-safe lane verification (WARDEN-WP-0026 T01)
|
||||
|
||||
**Verifying a lane must never read the secret *data*.** A negative deny-test that
|
||||
runs `bao kv get <path>` will, if the deny fails (e.g. a privileged token
|
||||
fallback), print the secret value into a logged context — this is exactly the
|
||||
2026-07-16 CCR-2026-0004 disclosure. Prove *allow/deny* with
|
||||
`bao token capabilities`, which returns the capability list, not the value.
|
||||
|
||||
For KV v2, capabilities are checked against the **API data path**
|
||||
(`<mount>/data/<lane-path>`), not the `kv get` logical path.
|
||||
|
||||
```bash
|
||||
# Positive: the lane's own OIDC identity can read the data path.
|
||||
bao login -method=oidc -path=netkingdom role=<lane-role> # caller identity
|
||||
bao token capabilities "$(bao print token)" platform/data/<lane-path>
|
||||
# → expect the list to include: read
|
||||
|
||||
# Negative: a default-only identity is denied — no value is ever read.
|
||||
DEFAULT_TOKEN=$(bao token create -policy=default -field=token) # if denied, STOP — do not fall back
|
||||
bao token capabilities "$DEFAULT_TOKEN" platform/data/<lane-path>
|
||||
# → expect: deny
|
||||
```
|
||||
|
||||
- **Never** substitute `bao kv get` for the checks above. Reading a value to
|
||||
"confirm it's there" is the anti-pattern; presence is proven by `read` in the
|
||||
capability list.
|
||||
- If `bao token create -policy=default` is itself denied for your identity, that
|
||||
is a *pass for the deny direction* — **do not** fall back to your privileged
|
||||
login token to force the read.
|
||||
- Fetching a value **for use** (`--field` into an env var or file, or
|
||||
`warden access … --field`) is a separate, intended action — not verification.
|
||||
|
||||
Record the capability lists (allow/deny) as the promotion evidence; they contain
|
||||
no secret material and are safe for CCRs, State Hub, and Git.
|
||||
|
||||
---
|
||||
|
||||
## Worked examples (already active)
|
||||
|
||||
**`ops-warden-warden-sign-token`** — promoted 2026-07-01 after RAILIANCE-WP-0005:
|
||||
|
|
|
|||
|
|
@ -95,6 +95,30 @@ After CCR approval and policy apply:
|
|||
|
||||
---
|
||||
|
||||
## Verify the lane (capabilities-safe — never read the value)
|
||||
|
||||
Prove allow/deny with `bao token capabilities`, **not** `bao kv get -field=…`.
|
||||
`bao kv metadata get` (above) is fine — it shows versions, not values. Reading the
|
||||
data field to "confirm" it is the anti-pattern
|
||||
(`wiki/playbooks/catalog-lane-promotion.md#capabilities-safe-lane-verification`).
|
||||
|
||||
```bash
|
||||
# Positive: lane OIDC identity can read the data path
|
||||
bao login -method=oidc -path=netkingdom role=forgejo-admin-workload-kv-read
|
||||
bao token capabilities "$(bao print token)" platform/data/workloads/forgejo/forgejo-admin
|
||||
# → expect: read
|
||||
|
||||
# Negative: default-only identity is denied
|
||||
DEFAULT_TOKEN=$(bao token create -policy=default -field=token) # if denied, that IS the pass — do NOT fall back
|
||||
bao token capabilities "$DEFAULT_TOKEN" platform/data/workloads/forgejo/forgejo-admin
|
||||
# → expect: deny
|
||||
```
|
||||
|
||||
Confirming the PAT works against Forgejo is a separate, value-using action — fetch
|
||||
`--field API_TOKEN` into an env var and call `/api/v1/user`; never paste the token.
|
||||
|
||||
---
|
||||
|
||||
## Consumers (downstream wiring — after lane verified)
|
||||
|
||||
| Consumer | Repo |
|
||||
|
|
|
|||
|
|
@ -56,4 +56,28 @@ Used by `railiance-backup` (workstation) and `forgejo-backup` (platform).
|
|||
tools/cmd/forgejo-backup
|
||||
```
|
||||
|
||||
`AGE_PRIVATE_KEY` in the same path is recovery escrow — fetch only for restore drills.
|
||||
`AGE_PRIVATE_KEY` in the same path is recovery escrow — fetch only for restore drills.
|
||||
|
||||
---
|
||||
|
||||
## Verify the lane (capabilities-safe — never read the value)
|
||||
|
||||
Prove allow/deny with `bao token capabilities`, **not** `bao kv get`. Reading the
|
||||
value to "confirm" it triggered the 2026-07-16 disclosure of `NC_WEBDAV_TOKEN` /
|
||||
`NC_WEBDAV_URL` / `AGE_PRIVATE_KEY` (see `history/2026-07-16-credential-disclosure-lessons.md`).
|
||||
|
||||
```bash
|
||||
# Positive: lane OIDC identity can read the data path
|
||||
bao login -method=oidc -path=netkingdom role=railiance-backup-workload-kv-read
|
||||
bao token capabilities "$(bao print token)" platform/data/workloads/railiance/backup/offsite-lane
|
||||
# → expect: read
|
||||
|
||||
# Negative: default-only identity is denied (no value is read)
|
||||
DEFAULT_TOKEN=$(bao token create -policy=default -field=token) # if this is denied, that IS the pass — do NOT fall back
|
||||
bao token capabilities "$DEFAULT_TOKEN" platform/data/workloads/railiance/backup/offsite-lane
|
||||
# → expect: deny
|
||||
```
|
||||
|
||||
The capability lists contain no secret material — safe to record on
|
||||
`CCR-2026-0004` as promotion evidence. Full pattern:
|
||||
`wiki/playbooks/catalog-lane-promotion.md#capabilities-safe-lane-verification`.
|
||||
Loading…
Add table
Add a link
Reference in a new issue