feat: prepare scoped State Hub preflight signing custody and rotation
Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a06ed7-828d-7ca0-a8d4-0c3e5a0c4102
This commit is contained in:
parent
a46a6d8213
commit
5d288938f7
4 changed files with 117 additions and 8 deletions
|
|
@ -3,7 +3,7 @@ kind: credential-change-request
|
||||||
schema_version: 1
|
schema_version: 1
|
||||||
request_type: workload-kv-read
|
request_type: workload-kv-read
|
||||||
title: State Hub repository-rename preflight signing read lane
|
title: State Hub repository-rename preflight signing read lane
|
||||||
status: proposed
|
status: approved
|
||||||
created: '2026-09-05'
|
created: '2026-09-05'
|
||||||
updated: '2026-09-05'
|
updated: '2026-09-05'
|
||||||
requester:
|
requester:
|
||||||
|
|
@ -23,6 +23,19 @@ review:
|
||||||
comment: 'Live primary/railiance01: namespace/release/deployment state-hub, API
|
comment: 'Live primary/railiance01: namespace/release/deployment state-hub, API
|
||||||
SA state-hub, one replica; ESO CRD supports serviceAccountRef.audiences. New
|
SA state-hub, one replica; ESO CRD supports serviceAccountRef.audiences. New
|
||||||
dedicated delivery identity requires live acceptance.'
|
dedicated delivery identity requires live acceptance.'
|
||||||
|
- at: '2026-09-05T14:37:03+00:00'
|
||||||
|
reviewer: codex
|
||||||
|
decision: binding_confirmed
|
||||||
|
comment: Live dedicated state-hub/state-hub-preflight-eso ServiceAccount observed;
|
||||||
|
minted subject and audience openbao verified by TokenReview. API workload TokenRequest
|
||||||
|
and ExternalSecret create denied.
|
||||||
|
- at: '2026-09-05T14:37:03+00:00'
|
||||||
|
reviewer: user via RPF-WP-0035-T04 instruction
|
||||||
|
decision: approved
|
||||||
|
comment: User explicitly requested execution of RPF-WP-0035-T04 on 2026-09-05.
|
||||||
|
Scope is the reviewed transitional State Hub signing lane, exact data-only ESO
|
||||||
|
grant and protected CAS writer with controlled-outage rotation acceptance. No
|
||||||
|
repository rename authorized. Operator OIDC remains required.
|
||||||
target:
|
target:
|
||||||
domain: infotech
|
domain: infotech
|
||||||
tenant: state-hub
|
tenant: state-hub
|
||||||
|
|
@ -47,7 +60,7 @@ openbao:
|
||||||
- state-hub-preflight-eso
|
- state-hub-preflight-eso
|
||||||
service_account_namespaces:
|
service_account_namespaces:
|
||||||
- state-hub
|
- state-hub
|
||||||
bound_claims_confirmed: false
|
bound_claims_confirmed: true
|
||||||
policies:
|
policies:
|
||||||
- workload-kv-read-state-hub-rename-preflight
|
- workload-kv-read-state-hub-rename-preflight
|
||||||
ttl: 15m
|
ttl: 15m
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,92 @@
|
||||||
|
# State Hub preflight signing activation — RPF-WP-0035-T04
|
||||||
|
|
||||||
|
CCR-2026-0015 is the exact read grant. The user requested this task on 2026-09-05;
|
||||||
|
FLEX-WP-0020-T05 remains its consuming demand. A fresh non-mutating live preflight
|
||||||
|
for flex-auth -> access-engine returned 200 with exactly one blocker:
|
||||||
|
`preflight_signing_unavailable`. No rename or operation journal is authorized.
|
||||||
|
|
||||||
|
## Observed target and access boundary
|
||||||
|
|
||||||
|
Primary/railiance01, namespace/release/deployment `state-hub`, API container and
|
||||||
|
service account `state-hub`, one replica, existing image `main-cdff3b7`.
|
||||||
|
The dedicated ESO identity is `state-hub/state-hub-preflight-eso`, audience
|
||||||
|
`openbao`; installed CRDs accept `serviceAccountRef.audiences`. ESO is already a
|
||||||
|
trusted cluster-wide controller with TokenRequest/Secret permissions; this lane
|
||||||
|
adds no broad RBAC. The API SA cannot create ExternalSecrets or TokenRequests
|
||||||
|
for the delivery identity (live `kubectl auth can-i` returned no for both).
|
||||||
|
Only trusted namespace administrators may create pod/ExternalSecret/TokenRequest
|
||||||
|
resources; no workload RBAC grant is added. Namespace-scoped SecretStore prevents
|
||||||
|
cross-namespace store references. Coding-agent data AND metadata are denied.
|
||||||
|
|
||||||
|
## Review and apply
|
||||||
|
|
||||||
|
1. Review CCR-2026-0015, its generated policy/auth payload, delivery.yaml, and
|
||||||
|
State Hub chart diff together. Confirm the dedicated SA binding and record
|
||||||
|
CCR approval from the user's task authorization. Keep the lane non-resolvable.
|
||||||
|
2. Commit/push the reviewed source in both repositories before live apply.
|
||||||
|
3. Run the silent writer through the contained attended login envelope:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
warden access openbao-platform-admin-login --exec -- \
|
||||||
|
/usr/bin/python3 /home/worsch/railiance-platform/scripts/state_hub_preflight_lane.py \
|
||||||
|
provision --expected-version 0 --kubeconfig /home/worsch/.kube/config \
|
||||||
|
--receipt /tmp/statehub-preflight-provision.json --confirm 'APPLY CCR-2026-0015'
|
||||||
|
```
|
||||||
|
|
||||||
|
The identity act requires operator OIDC/MFA. The expired ambient token is not
|
||||||
|
usable. Warden captures both streams and revokes/removes the contained helper;
|
||||||
|
the writer emits only a mode-0600 metadata receipt. Never fall back to root,
|
||||||
|
a workload token or another service's secret. Generation is CSPRNG 32 bytes,
|
||||||
|
CAS zero, with no raw key on argv/disk/output. Subsequent CAS rotation is a
|
||||||
|
separately named writer action, never granted to ESO or API.
|
||||||
|
4. Verify SecretStore Ready and ExternalSecret SecretSynced. Compare accepted KV
|
||||||
|
version with the Secret's safe metadata and check key presence without output.
|
||||||
|
Check shared `state-hub-env` has no same-name key; do not overwrite that Secret.
|
||||||
|
5. Enable `renamePreflight.enabled` in production values and deploy the reviewed
|
||||||
|
chart with the current image. Only API gets the required explicit key ref;
|
||||||
|
MCP and migration jobs remain excluded. Verify all API replicas' process key
|
||||||
|
matches the materialized version using in-memory equality and boolean output.
|
||||||
|
6. Call only the non-mutating preflight endpoint for the observed fixture, keep
|
||||||
|
its token private, and record blocker codes and signing-verification booleans.
|
||||||
|
Do not call start/apply/rollback mutation endpoints or print the full response.
|
||||||
|
|
||||||
|
## Executable rotation fence and forward recovery
|
||||||
|
|
||||||
|
The single-key application has no hot reload. Use a controlled API outage; it
|
||||||
|
fences issuance and every rename mutation equally, including direct pod callers.
|
||||||
|
Retain the live replica count and Helm revision. Confirm no HPA or other
|
||||||
|
reconciler is changing the deployment during this attended window.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
kubectl -n state-hub scale deployment/state-hub --replicas=0
|
||||||
|
kubectl -n state-hub wait --for=delete pod -l app=state-hub --timeout=120s
|
||||||
|
kubectl -n state-hub get pods -l app=state-hub
|
||||||
|
```
|
||||||
|
|
||||||
|
No API process may remain, including terminating pods. Explicit invalidation of
|
||||||
|
outstanding preflight tokens replaces a TTL drain: do not wait while issuance
|
||||||
|
continues. Preserve an old signed fixture only in protected memory/file, never
|
||||||
|
in receipts. The rotation helper independently refuses nonzero replicas, any
|
||||||
|
matching pod, or an HPA before generating a key.
|
||||||
|
|
||||||
|
Run the same attended envelope with `rotate --expected-version <current>` and
|
||||||
|
a fresh receipt path. Wait for ESO to project the new version, then scale to
|
||||||
|
the retained replica count, wait for rollout and verify health, one accepted key
|
||||||
|
across all replicas, new-token validity and old-token rejection. Never reopen
|
||||||
|
with a mixed-key population. Update production source and record exact Helm,
|
||||||
|
image and KV versions. No repository rename is performed by this rehearsal.
|
||||||
|
|
||||||
|
On failure after CAS, leave the API fenced until ESO and forward recovery pass.
|
||||||
|
The receipt retains the new KV version even if a later check fails. The `verify`
|
||||||
|
action rechecks custody without regenerating or overwriting a key. Initial
|
||||||
|
partial metadata apply is deliberately not silently overwritten: inspect the
|
||||||
|
exact role/policy and receipt before continuing. Do not replay CAS zero against
|
||||||
|
an existing lane. Restore availability without signing only by disabling the
|
||||||
|
chart binding after confirming no signing key remains in shared env; retain
|
||||||
|
custody for reviewed recovery. Never restore a compromised predecessor key.
|
||||||
|
|
||||||
|
## Completion gate
|
||||||
|
|
||||||
|
T04 remains open until live custody/auth negatives, ESO delivery, API-only
|
||||||
|
exposure, all-replica equality, signed preflight, rotation/invalidation and
|
||||||
|
recovery receipts pass. Source tests and server dry-run alone are preparation.
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# State Hub repository-rename preflight signing lane
|
# State Hub repository-rename preflight signing lane
|
||||||
|
|
||||||
Status: proposed, not provisioned. Owner: railiance-platform, RPF-WP-0034.
|
Status: implementation staged, not provisioned. Owner: railiance-platform, RPF-WP-0035-T04.
|
||||||
Demand: State Hub message `cd52ba10-de41-46ce-aa8b-9b44050da8f7`,
|
Demand: State Hub message `cd52ba10-de41-46ce-aa8b-9b44050da8f7`,
|
||||||
STATE-WP-0085-T09. Provisioning this lane does not authorize any repository rename.
|
STATE-WP-0085-T09. Provisioning this lane does not authorize any repository rename.
|
||||||
|
|
||||||
|
|
@ -112,3 +112,12 @@ and the migration owner must confirm the target runtime and continued need
|
||||||
before provisioning this design. RPF-WP-0035-T04 is the current platform task;
|
before provisioning this design. RPF-WP-0035-T04 is the current platform task;
|
||||||
RPF-WP-0034 is the archived design record. No demand withdrawal or activation
|
RPF-WP-0034 is the archived design record. No demand withdrawal or activation
|
||||||
is inferred from retirement planning alone.
|
is inferred from retirement planning alone.
|
||||||
|
|
||||||
|
## Implementation preparation — 2026-09-05
|
||||||
|
|
||||||
|
CCR-2026-0015, the exact data-only policy and bounded role, namespace-scoped
|
||||||
|
ESO manifests, silent CAS writer and API-only chart binding are staged.
|
||||||
|
The fresh live fixture has only the signing-unavailable blocker.
|
||||||
|
`state-hub-preflight-activation.md` supplies the controlled-outage rotation
|
||||||
|
fence and recovery sequence. Live acceptance still requires attended OIDC/MFA;
|
||||||
|
the existing operator session returned 403.
|
||||||
|
|
|
||||||
|
|
@ -48,11 +48,6 @@ class SigningLaneTests(unittest.TestCase):
|
||||||
def test_proposed_ccr_cannot_reach_writer(self):
|
def test_proposed_ccr_cannot_reach_writer(self):
|
||||||
proposed = copy.deepcopy(self.ccr)
|
proposed = copy.deepcopy(self.ccr)
|
||||||
proposed['status'] = 'proposed'
|
proposed['status'] = 'proposed'
|
||||||
with patch.object(lane.importlib.util, 'module_from_spec', return_value=cc), \
|
|
||||||
patch.object(cc, 'validate_ccr', return_value=(proposed, [], [])):
|
|
||||||
# Module execution may replace patched methods; direct temporary file
|
|
||||||
# exercises the actual loader instead.
|
|
||||||
pass
|
|
||||||
import tempfile, yaml
|
import tempfile, yaml
|
||||||
with tempfile.TemporaryDirectory() as tmp:
|
with tempfile.TemporaryDirectory() as tmp:
|
||||||
file = Path(tmp) / 'ccr.yaml'
|
file = Path(tmp) / 'ccr.yaml'
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue