RPF-WP-0015-T02: contained-login only, single silent attended window
Harness used the retracted public OpenBao URL and a token file; lane used a standing token file. Both now run only inside the contained ops-warden login (tunnel, helper token), and t02-attended-window.py runs project, exercise and cleanup under one login without printing (warden fails closed on child output). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 680842@bnt-lap001 Assistant-Session: 54eb9e8e-7d6b-4f0c-b7a6-a6db62075883
This commit is contained in:
parent
badb91c71a
commit
311aa86eef
10 changed files with 773 additions and 109 deletions
|
|
@ -85,46 +85,86 @@ driver as `AUDIT_T02_SOURCE`/`AUDIT_T02_TENANT`; the confirm string is
|
|||
`platform/workloads/audit-core/senders` → ExternalSecret `audit-core-senders`
|
||||
→ env `AUDIT_CORE_SENDERS`, so both projection and cleanup restart
|
||||
`deploy/audit-core`. The restarts must stay outside the restart-free proof:
|
||||
project → restart → T02 exercise → evidence → cleanup → restart.
|
||||
|
||||
1. `make audit-synthetic-sender-preflight` — read-only; reads registry metadata
|
||||
only (add `AUDIT_SYNTHETIC_SENDER_PREFLIGHT_ARGS=--inspect-registry` for an
|
||||
attended in-memory conflict check).
|
||||
2. `make audit-synthetic-sender-project` — only within the first 3 minutes of
|
||||
the window and with ≥10 minutes of identity lifetime left. Writes the
|
||||
bearer to a new mode-`0600` file (`AUDIT_T02_TOKEN_FILE`, absolute, in an
|
||||
operator-owned `0700` directory outside every Git work tree; never stdout),
|
||||
adds the one identity, forces `audit-core-senders` sync and runs
|
||||
`rollout restart deploy/audit-core`, then waits for one Ready restarted pod
|
||||
and prints its UID. Any failure removes the identity (restarting again) and
|
||||
shreds the bearer file.
|
||||
3. The T02 exercise (`make audit-database-lease-recovery-exercise`). Its
|
||||
baseline must capture the post-projection pod UID; never project while the
|
||||
exercise is running.
|
||||
4. Record the exercise's JSON result (it is the evidence file).
|
||||
5. `make audit-synthetic-sender-cleanup AUDIT_T02_EVIDENCE=<result.json>` —
|
||||
refuses unless that evidence names this `approval_id`, or an explicit
|
||||
`AUDIT_SYNTHETIC_SENDER_ABORT_REASON` is given for an aborted run. Removes
|
||||
only the exact named identity (refusing if the name carries a different
|
||||
scope), restarts audit-core only if something was removed, shreds the
|
||||
bearer file, and confirms 1/1 Ready. Re-running is a no-op.
|
||||
**Contained OpenBao access only.** Every live command (lane `project` and
|
||||
`cleanup`, harness `exercise`) requires `--contained` and refuses unless it runs
|
||||
as the child of `scripts/openbao-attended-exec.py`, i.e. inside `warden access
|
||||
openbao-platform-admin-login --exec`: `WARDEN_ROUTING_CATALOG` must be set,
|
||||
`BAO_ADDR` must be the operator tunnel `http://127.0.0.1:18200`, and
|
||||
`BAO_TOKEN`/`VAULT_TOKEN`/`OPENBAO_TOKEN` must be absent; `bao` authenticates
|
||||
through Warden's isolated token helper. There is no token-file path any more:
|
||||
the harness's former default `https://bao.coulomb.social` was retracted on
|
||||
2026-09-15 (RMASTER-WP-0020-T09), and a standing 0600 platform-admin token file
|
||||
is outside the accepted custody path. Outside the login, the lane's `preflight`
|
||||
runs the cluster checks only; `--contained` adds registry metadata and
|
||||
`--inspect-registry` (attended, in-memory) adds the conflict check.
|
||||
|
||||
**One login per window.** `scripts/t02-attended-window.py` is the single reviewed
|
||||
child of the contained login, so the ≤15-minute window needs one MFA login:
|
||||
|
||||
```bash
|
||||
make audit-t02-attended-window \
|
||||
AUDIT_DATABASE_LEASE_APPROVAL=/path/to/approved-receipt.json \
|
||||
AUDIT_DATABASE_LEASE_DRIVER=/path/to/audit-core/scripts/t02_synthetic_load_driver.py \
|
||||
AUDIT_SYNTHETIC_SENDER_CONFIRM=audit-core-synthetic-sender:<approval_id>:attended \
|
||||
AUDIT_T02_TOKEN_FILE=/abs/0700-dir/t02.bearer \
|
||||
AUDIT_T02_EVIDENCE_DIR=/abs/0700-dir/evidence
|
||||
```
|
||||
|
||||
It runs, in order:
|
||||
|
||||
1. lane `project` — only within the first 3 minutes of the window and with
|
||||
≥10 minutes of identity lifetime left. Writes the bearer to a new mode-`0600`
|
||||
file (absolute, operator-owned `0700` directory outside every Git work tree;
|
||||
never stdout), adds the one identity, forces `audit-core-senders` sync, runs
|
||||
`rollout restart deploy/audit-core`, and waits for one Ready restarted pod.
|
||||
Any failure removes the identity (restarting again) and shreds the file.
|
||||
2. port-forward — the lane and harness reach the cluster only via
|
||||
`ssh railiance01 kubectl`, so the window uses the same path: `ssh -L
|
||||
127.0.0.1:<port>:127.0.0.1:<port> railiance01 timeout 900 kubectl -n
|
||||
audit-core port-forward --address 127.0.0.1 deploy/audit-core <port>:8080`
|
||||
(default port 18081, `AUDIT_T02_PORT` to override). No local kubeconfig is
|
||||
needed; the remote forward is bounded by `timeout` even if SSH is cut.
|
||||
audit-core's NetworkPolicies admit only named namespaces, which is why the
|
||||
operator-side driver needs the forward.
|
||||
3. harness `exercise --contained` with the registered driver; the driver env is
|
||||
derived from the receipt: `AUDIT_T02_BASE_URL=http://127.0.0.1:<port>`,
|
||||
`AUDIT_T02_TOKEN_FILE`, `AUDIT_T02_SOURCE`/`AUDIT_T02_TENANT` from
|
||||
`synthetic_sender`, `AUDIT_T02_FIXTURE_ID=t02-<approval_id>`,
|
||||
`AUDIT_T02_OCCURRED_AT=window.start`. Its baseline captures the
|
||||
post-projection pod UID.
|
||||
4. evidence — the exercise JSON is written `0600` as
|
||||
`<evidence-dir>/<approval_id>-exercise.json`; a value-safe window summary is
|
||||
written as `<approval_id>-window.json` on success and on abort.
|
||||
5. lane `cleanup` with that evidence — refuses unless it names this
|
||||
`approval_id`. Removes only the exact named identity (refusing if the name
|
||||
carries a different scope), restarts audit-core only if something was
|
||||
removed, shreds the bearer file, and confirms 1/1 Ready.
|
||||
|
||||
The port-forward is stopped before cleanup and on every exit. Any failure after
|
||||
projection was attempted (including a failed `project`, whose registry write may
|
||||
already have reached OpenBao) runs `cleanup --abort-reason` before the window
|
||||
script exits non-zero. Children's stdout is captured and only their value-safe
|
||||
JSON is re-emitted.
|
||||
|
||||
The single-step targets (`audit-synthetic-sender-project`,
|
||||
`audit-database-lease-recovery-exercise`, `audit-synthetic-sender-cleanup`)
|
||||
remain for the abort path; each costs its own contained login. A missed cleanup
|
||||
is run as `make audit-synthetic-sender-cleanup
|
||||
AUDIT_SYNTHETIC_SENDER_ABORT_REASON=...` and is a no-op if nothing remains.
|
||||
|
||||
If cleanup is missed, audit-core's `expires_at` enforcement makes the identity
|
||||
inert at window end; the attended cleanup is still required to remove it.
|
||||
Projection and cleanup each restart audit-core once (Recreate, no surge), so
|
||||
production senders see two short retryable gaps per window; announce them with
|
||||
the window.
|
||||
|
||||
The driver runs operator-side. audit-core's NetworkPolicies admit only named
|
||||
namespaces, so reach the pod with `kubectl -n audit-core port-forward
|
||||
deploy/audit-core <local-port>:8080` and set `AUDIT_T02_BASE_URL` to that local
|
||||
URL; record the base URL and the driver's source/tenant (which must equal the
|
||||
receipt's `synthetic_sender` block) in the window notes. Projection and cleanup
|
||||
each restart audit-core once (Recreate, no surge), so production senders see two
|
||||
short retryable gaps per window; announce them with the window.
|
||||
|
||||
If `project` fails after the registry write may already have reached OpenBao (for
|
||||
example a transport error on the write itself), treat the identity as possibly
|
||||
present: run `cleanup --abort-reason "<reason>"` before leaving the window.
|
||||
The lane's `--token-file` is the attended operator token for the registry write;
|
||||
it must come from the contained ops-warden attended login accepted under RPF-WP-0017
|
||||
(isolated 0600 helper state, removed afterwards), not from a long-lived token file.
|
||||
The contained login discards child output and fails closed (exit 12, token
|
||||
revoked) on any byte the child prints. The window script therefore prints
|
||||
nothing: its summary, exercise evidence or `window-error-*.json` land only in the
|
||||
evidence directory. Single-step targets require `AUDIT_T02_RESULT_FILE` so the
|
||||
lane/harness write their JSON there instead of stdout.
|
||||
|
||||
## Attended sequence
|
||||
|
||||
|
|
@ -165,13 +205,11 @@ bodies and clear all transient JSON from memory or mode-`0600` temporary files.
|
|||
(which restarts audit-core after the evidence is captured). The replacement remains managed by ESO
|
||||
and expires through its normal database role TTL.
|
||||
|
||||
Only inside the approved window, with the receipt and reviewed driver:
|
||||
|
||||
```bash
|
||||
make audit-database-lease-recovery-exercise \
|
||||
AUDIT_DATABASE_LEASE_APPROVAL=/path/to/approved-receipt.json \
|
||||
AUDIT_DATABASE_LEASE_DRIVER=/path/to/approved-load-driver
|
||||
```
|
||||
Only inside the approved window, with the receipt and reviewed driver, run the
|
||||
whole window through `make audit-t02-attended-window` (see the synthetic sender
|
||||
section above); the exercise step alone is
|
||||
`make audit-database-lease-recovery-exercise`, which also runs inside the
|
||||
contained login.
|
||||
|
||||
The exact confirmation string is embedded in the target. The harness rechecks
|
||||
the Secret/pod/lease immediately before revocation, requires at least five
|
||||
|
|
|
|||
|
|
@ -25,10 +25,15 @@ Runbook: `docs/audit-core-database-lease-recovery.md`.
|
|||
`RAILIANCE-WP-0024-T02-SYNTHETIC-01` with driver revision
|
||||
`sha256:941ba251…` (audit-core `8c8bcf4`), and the `synthetic_sender` block.
|
||||
3. Announce two short audit-core restart gaps (projection and cleanup).
|
||||
4. In the window: attended ops-warden login → `make audit-synthetic-sender-project`
|
||||
→ port-forward audit-core → `make audit-database-lease-recovery-exercise` →
|
||||
record evidence → `make audit-synthetic-sender-cleanup`.
|
||||
5. Abort: stop load, `cleanup --abort-reason`, never restart the pod by hand.
|
||||
4. In the window: one contained ops-warden login runs the whole sequence —
|
||||
`make audit-t02-attended-window` (approval receipt, driver path, confirm
|
||||
string, bearer file and evidence dir outside Git). Through
|
||||
`scripts/openbao-attended-exec.py` it runs lane project (restart #1) →
|
||||
ssh-bounded port-forward → contained T02 exercise → 0600 evidence JSON →
|
||||
lane cleanup with that evidence (restart #2). No token file is used.
|
||||
5. Abort: the window script stops the port-forward and runs `cleanup --abort-reason`
|
||||
itself; if it could not, run `make audit-synthetic-sender-cleanup
|
||||
AUDIT_SYNTHETIC_SENDER_ABORT_REASON=...` (one more login). Never restart the pod by hand.
|
||||
|
||||
## T03 — coordinated railiance01 reboot
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue