RPF-WP-0035-T14: flex-auth signer custody founder gate, runbook and review dispositions
- CCR-2026-0050: record secrets-engine-owner approval (two receipt conditions) and platform runbook review; link founder decision 18731e84. - Attended creation runbook with preconditions, rollback and negatives. - Warden routing receipt: openbao-platform-admin-login / oidc_login now selected. - Native adapter: owner-review digest excludes expires_at so reviews can precede the 120 s window (close-down checklist row 27). Nothing generated, written or applied. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 809600@bnt-lap001 Assistant-Session: 40ba228f-c8a0-4ec9-bb29-3129951afdc4
This commit is contained in:
parent
1228cd8407
commit
20db368aee
7 changed files with 317 additions and 5 deletions
103
docs/evidence/2026-10-21-flex-auth-signer-warden-route.json
Normal file
103
docs/evidence/2026-10-21-flex-auth-signer-warden-route.json
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
{
|
||||
"need": "attended OpenBao platform administration of flex-auth signer CAS0 seed and KV metadata setup",
|
||||
"verdict": "founder_required",
|
||||
"organization_posture": "build",
|
||||
"policy_gate": "zone-aware \u2014 fetches gated by flex-auth-ops-warden-fetch at http://127.0.0.1:19091",
|
||||
"lane_id": "openbao-platform-admin-login",
|
||||
"lane_title": "Attended OpenBao platform administration login",
|
||||
"match_score": 9,
|
||||
"commands": [
|
||||
"warden access openbao-platform-admin-login --exec -- <reviewed-command>",
|
||||
"# owner login is contained by warden; do not invoke it separately"
|
||||
],
|
||||
"founder_act": {
|
||||
"kind": "oidc_login",
|
||||
"summary": "Interactive OIDC/MFA login via railiance-platform",
|
||||
"details": {
|
||||
"lane_id": "openbao-platform-admin-login",
|
||||
"auth_method": "attended KeyCape OIDC/MFA at OpenBao auth mount netkingdom, role platform-admin",
|
||||
"fetch_command": "warden access openbao-platform-admin-login --exec -- <reviewed-command>",
|
||||
"desk_hint": "warden desk --from-plan (act=oidc_login); execute only through: warden access openbao-platform-admin-login --exec -- <reviewed-command>"
|
||||
}
|
||||
},
|
||||
"ccr_stub": null,
|
||||
"catalog": {
|
||||
"path": "/home/worsch/ops-warden/registry/routing/catalog.yaml",
|
||||
"source": "repo",
|
||||
"content_hash": "b007b9598551",
|
||||
"mtime_iso": "2026-10-20T11:42:31.833164+00:00",
|
||||
"package_version": "0.1.2",
|
||||
"entry_count": 49,
|
||||
"active_count": 41,
|
||||
"newest_reviewed": "2026-10-20",
|
||||
"oldest_reviewed": "2026-06-18",
|
||||
"stale_entry_count": 18,
|
||||
"using_bundled": false,
|
||||
"warnings": [
|
||||
"18 catalog entries past 90d review cadence",
|
||||
"17 interim delegations need re-verifying (risk-scaled blocker cadence) \u2014 see `warden route gaps`"
|
||||
]
|
||||
},
|
||||
"candidates": [
|
||||
{
|
||||
"id": "openbao-platform-admin-login",
|
||||
"title": "Attended OpenBao platform administration login",
|
||||
"score": 9,
|
||||
"status": "active",
|
||||
"resolvable": true,
|
||||
"exec_capable": true,
|
||||
"warden_executes": false,
|
||||
"lane": "login",
|
||||
"risk": "high"
|
||||
},
|
||||
{
|
||||
"id": "openbao-structure-survey",
|
||||
"title": "OpenBao existing-structure survey for a construction plan (section 2)",
|
||||
"score": 9,
|
||||
"status": "active",
|
||||
"resolvable": false,
|
||||
"exec_capable": false,
|
||||
"warden_executes": false,
|
||||
"lane": "ceremony",
|
||||
"risk": "high"
|
||||
},
|
||||
{
|
||||
"id": "openbao-api-key",
|
||||
"title": "API key, DB credential, or dynamic lease",
|
||||
"score": 6,
|
||||
"status": "active",
|
||||
"resolvable": false,
|
||||
"exec_capable": true,
|
||||
"warden_executes": false,
|
||||
"lane": "secret",
|
||||
"risk": "high"
|
||||
},
|
||||
{
|
||||
"id": "openbao-shamir-recovery-ceremony",
|
||||
"title": "Attended OpenBao Shamir seal and unseal recovery ceremony",
|
||||
"score": 6,
|
||||
"status": "active",
|
||||
"resolvable": false,
|
||||
"exec_capable": false,
|
||||
"warden_executes": false,
|
||||
"lane": "ceremony",
|
||||
"risk": "high"
|
||||
},
|
||||
{
|
||||
"id": "ops-warden-warden-sign-token",
|
||||
"title": "Scoped OpenBao token for ops-warden SSH signing (warden-sign)",
|
||||
"score": 6,
|
||||
"status": "active",
|
||||
"resolvable": false,
|
||||
"exec_capable": false,
|
||||
"warden_executes": false,
|
||||
"lane": "secret",
|
||||
"risk": "high"
|
||||
}
|
||||
],
|
||||
"reasons": [
|
||||
"login lane requires interactive founder/operator identity act"
|
||||
],
|
||||
"actor": null,
|
||||
"domain": null
|
||||
}
|
||||
143
docs/runbooks/flex-auth-envelope-signing-attended-creation.md
Normal file
143
docs/runbooks/flex-auth-envelope-signing-attended-creation.md
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
# Attended creation: flex-auth decision-envelope signing key (CCR-2026-0050)
|
||||
|
||||
Prepared 2026-10-21 under RPF-WP-0035-T14 (founder gate) for the custody route
|
||||
`flex-auth-decision-envelope-signing-key`. **Nothing in this file has been
|
||||
executed.** Every step that changes OpenBao or the cluster needs the founder's
|
||||
explicit go for that change, given at the time of the change. No value of the
|
||||
seed is printed, stored in Git, the hub, chat or a log at any point.
|
||||
|
||||
Inputs that are fixed by reviewed source:
|
||||
|
||||
| Item | Value |
|
||||
| --- | --- |
|
||||
| Read CCR | `credential-change-requests/CCR-2026-0050-flex-auth-envelope-signing.yaml` |
|
||||
| Native action | `docs/credential-lane-designs/flex-auth-envelope-signing-native-plan.json` (`RPF35-PDP-SIGNER-NATIVE-20261016-R1`) |
|
||||
| KV document | `platform/data/workloads/flex-auth/envelope-signing`, fields `ED25519_PRIVATE_KEY`, `ED25519_PUBLIC_KEY`, `kid` |
|
||||
| kid | `flex-auth-lifecycle-20261016-r1` |
|
||||
| Metadata contract | `cas_required=true`, `max_versions=2`, `delete_version_after=0s`; bootstrap is CAS 0 |
|
||||
| Read policy | `workload-kv-read-flex-auth-envelope-signing` (`openbao/policies/workload-kv-read-flex-auth-envelope-signing.hcl`) |
|
||||
| Read role | `auth/kubernetes/role/external-secrets-flex-auth-envelope-signing`, SA `flex-auth-envelope-signing-eso` in `flex-auth`, audience `openbao`, 15m, no default policy |
|
||||
| ESO projection | `docs/credential-lane-designs/flex-auth-envelope-signing-native-projection.yaml.proposed` (seed property only) |
|
||||
| Writer | `scripts/flex_auth_signer_native.py` + `scripts/flex_auth_signer_custody.py` (`sha256:f169ad12…110c`, as pinned in the native plan) |
|
||||
| Route | `warden access openbao-platform-admin-login --exec -- <reviewed-command>`; routing receipt `docs/evidence/2026-10-21-flex-auth-signer-warden-route.json` (`sha256:ad7d6a1829de53678b008b23627475e7570c71cf50f9f2ad5301635022a7f467`, lane `openbao-platform-admin-login`, verdict `founder_required`, act `oidc_login`) |
|
||||
| Private receipt dir | `/home/worsch/.local/state/railiance-platform/flex-auth-envelope-signing` (0700, files 0600) |
|
||||
|
||||
The 2026-10-16 routing blocker (warden selected `openbao-structure-survey`) no
|
||||
longer reproduces: the 2026-10-21 plan query selects the correct lane.
|
||||
|
||||
## 0. Preconditions (all must hold; any miss = stop)
|
||||
|
||||
1. Five dispositions on CCR-2026-0050 recorded in `review.comments`:
|
||||
founder, railiance-platform-owner, access-engine-owner, secrets-engine-owner,
|
||||
cluster-runtime-storage-owner (railiance-cluster).
|
||||
2. Founder decision on the hub (see RPF-WP-0035-T14) resolved with
|
||||
`APPROVE: CCR-2026-0050 ...`; then a platform session, not the requester,
|
||||
runs `make credential-change-sync-decision CREDENTIAL_CHANGE=CCR-2026-0050`.
|
||||
3. Access (FLEX-WP-0024-T06) has the retained deployment
|
||||
`flex-auth/flex-auth-secrets-engine` on the immutable candidate image
|
||||
`forgejo.coulomb.social/coulomb/flex-auth@sha256:09a83b27…981f` **stopped**:
|
||||
replicas 0, no owned ReplicaSet replicas, no pods using the signer SA or
|
||||
mounting the seed, no HPA. Protected consumers (secrets-engine) fenced.
|
||||
GitOps/Helm writers for the release frozen for the window.
|
||||
4. Cluster owner has reviewed the outbox PVC (1Gi local-path) and PV Retain.
|
||||
5. Secrets owner is ready to admit the public receipt through
|
||||
`secrets-engine/docs/founder-admission.md` (SECRETS-WP-0014-T04,
|
||||
decision e4eee965).
|
||||
6. Local tree is clean on the reviewed commit; tests pass:
|
||||
`uv run python -m pytest -q tests/test_flex_auth_signer_native.py tests/test_flex_auth_signer_custody.py`.
|
||||
|
||||
## 1. Read lane: policy and role (founder go #1)
|
||||
|
||||
```bash
|
||||
make credential-change-status CREDENTIAL_CHANGE=CCR-2026-0050 # expect status approved
|
||||
make credential-change-applier-dry-run CREDENTIAL_CHANGE=CCR-2026-0050
|
||||
make credential-change-applier-apply-plan CREDENTIAL_CHANGE=CCR-2026-0050
|
||||
# founder go, then:
|
||||
warden access openbao-platform-admin-login --exec -- \
|
||||
python3 scripts/credential-change.py applier-apply CCR-2026-0050 \
|
||||
--actor founder --record-state-hub
|
||||
# the founder types the confirmation phrase the tool prompts for
|
||||
```
|
||||
|
||||
Then verify native readback of the bound claims (no JWT or Secret printed) and
|
||||
record it: `python3 scripts/credential-change.py confirm-binding CCR-2026-0050
|
||||
--reviewer railiance-platform-owner --comment "<readback summary>"`.
|
||||
|
||||
Rollback: delete role `external-secrets-flex-auth-envelope-signing`, then
|
||||
policy `workload-kv-read-flex-auth-envelope-signing`. No KV data exists yet.
|
||||
|
||||
Negatives (record with `record-evidence`): login with a wrong SA, namespace and
|
||||
audience is refused; with the right identity, sibling path read, `list`,
|
||||
`metadata` read, write and delete are denied.
|
||||
|
||||
## 2. Seed creation (founder go #2; single attended window)
|
||||
|
||||
Do this inside one window of at most 120 seconds after building the admission.
|
||||
|
||||
1. Founder builds the admission file (owner-private, 0600, outside Git) in the
|
||||
schema `railiance-platform.signer-native-admission.v1` with
|
||||
`action=bootstrap`, the kid above, the routing receipt digest above, the
|
||||
immutable image, `caller_binding` and the five `owner_reviews`. Each review
|
||||
binds `review_scope_digest(admission)`, which excludes `owner_reviews` and
|
||||
`expires_at`, so reviewers can approve the exact scope ahead of the window.
|
||||
Set `expires_at` to now + 90 s last.
|
||||
2. Run, in the founder's terminal only:
|
||||
|
||||
```bash
|
||||
warden access openbao-platform-admin-login --exec -- \
|
||||
python3 scripts/flex_auth_signer_native.py \
|
||||
--admission <abs-path-admission.json> --admission-sha256 sha256:<hex> \
|
||||
--receipt-directory /home/worsch/.local/state/railiance-platform/flex-auth-envelope-signing
|
||||
```
|
||||
|
||||
The child checks the cluster ID, unseal, non-root platform-admin entity, audit
|
||||
enabled, read policy/role readback and the stop fence (rechecked before
|
||||
metadata setup and before CAS). It requires metadata absence (HTTP 404), sets the
|
||||
metadata contract, generates the seed in memory, writes with CAS 0, reads back
|
||||
version 1, and writes a receipt that holds public fields only. It is silent; the
|
||||
outcome is the receipt `status`:
|
||||
|
||||
- `custody-verified-pending-independent-public-admission`: success.
|
||||
- anything else, or no receipt: **unknown**. Keep everything fenced. Do not
|
||||
rerun bootstrap. Use the read-only `recover` action with a fresh admission
|
||||
that binds the original receipt digest.
|
||||
|
||||
Rollback before CAS: abort; nothing private was written. After CAS: no restore
|
||||
and no automatic retry. Forward-only recovery under a newly reviewed action.
|
||||
|
||||
## 3. Public receipt for secrets-engine (no private read)
|
||||
|
||||
Derive the consumer receipt from the writer receipt, never from a KV read (the
|
||||
KV document also holds the seed and reads are not field-filtered):
|
||||
|
||||
```bash
|
||||
R=/home/worsch/.local/state/railiance-platform/flex-auth-envelope-signing/<operation_id>.json
|
||||
jq '{kid, public_key, public_fingerprint, kv_version, writer_sha256,
|
||||
image_sha256: .bindings.image_sha256, policy_sha256: .bindings.policy_sha256,
|
||||
executed_revision_sha256: .bindings.executed_revision_sha256,
|
||||
deployment_sha256: .bindings.deployment_sha256, proposal_sha256: .bindings.proposal_sha256}' "$R" \
|
||||
> flex-auth-envelope-signing-public-receipt.json
|
||||
```
|
||||
|
||||
Then add the policy pin fields secrets-engine requires (`evaluator`, `mode`,
|
||||
`policy_package`, `policy_version`, `policy_package_digest`) from Access's
|
||||
published pin for the candidate release; these are not in the writer receipt.
|
||||
`public_key` is unpadded base64url, 43 characters. `public_fingerprint` is
|
||||
`sha256:` + hex SHA-256 of that ASCII string, not of the raw bytes. The founder
|
||||
delivers the file to the secrets-engine owner over an authenticated channel and
|
||||
signs the consumer trust file per `secrets-engine/docs/founder-admission.md`.
|
||||
|
||||
## 4. Delivery and signer start (founder go #3, Access-owned)
|
||||
|
||||
1. Access/GitOps applies the reviewed ESO projection (SA, SecretStore,
|
||||
ExternalSecret). Do not use client-side apply on Secrets.
|
||||
2. Check readiness with the safe helpers only:
|
||||
`scripts/eso_adoption_readiness.py` and
|
||||
`scripts/secret_annotation_maintenance.py` (inspect mode). Never run
|
||||
go-template or jsonpath against the Secret.
|
||||
3. Access restarts the single signer replica. Verify kid/source/image, a signed
|
||||
non-effect lifecycle allow and deny, and independent consumer verification
|
||||
before reopening protected consumers.
|
||||
|
||||
Negatives: unknown or old kid, wrong policy, tampered, expired or unsigned
|
||||
envelope, wrong caller and missing seed are all refused. No taskpilot enablement.
|
||||
Loading…
Add table
Add a link
Reference in a new issue