Pin signing-lane writes to the verified primary cluster
Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a06ecb-456a-71c2-b41e-0755d336e883
This commit is contained in:
parent
5d288938f7
commit
80793afe4f
5 changed files with 59 additions and 1 deletions
|
|
@ -24,6 +24,12 @@ cross-namespace store references. Coding-agent data AND metadata are denied.
|
||||||
State Hub chart diff together. Confirm the dedicated SA binding and record
|
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.
|
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.
|
2. Commit/push the reviewed source in both repositories before live apply.
|
||||||
|
Verify the selected kubeconfig reaches kube-system UID
|
||||||
|
`a553c742-0115-43d4-99a4-a5ca56fe0786` using a metadata-only namespace GET.
|
||||||
|
The writer enforces this identity before OpenBao access. The workstation's
|
||||||
|
default config uses a local port forward; if that listener is unavailable,
|
||||||
|
establish the approved cluster access path before starting attended login.
|
||||||
|
Do not substitute another cluster's context to make the command succeed.
|
||||||
3. Run the silent writer through the contained attended login envelope:
|
3. Run the silent writer through the contained attended login envelope:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
|
|
||||||
23
history/2026-09-05-preflight-signing-activation-readiness.md
Normal file
23
history/2026-09-05-preflight-signing-activation-readiness.md
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
# Signing activation readiness — RPF-WP-0035-T04
|
||||||
|
|
||||||
|
The user requested completion of T04. Concurrent session work landed platform
|
||||||
|
commit `5d28893` and prepared State Hub chart wiring and STATE-WP-0088. Avoid
|
||||||
|
overlapping live activation with that session; no credential mutation or API
|
||||||
|
outage was performed during this review.
|
||||||
|
|
||||||
|
Read-only SSH checks confirmed kube-system UID
|
||||||
|
`a553c742-0115-43d4-99a4-a5ca56fe0786`, one ready State Hub API replica, image
|
||||||
|
`main-cdff3b7`, and absence of ExternalSecret `state-hub-rename-preflight`.
|
||||||
|
The default workstation kubeconfig used localhost port 16443, whose listener
|
||||||
|
refused connection. The activation runbook now requires verified cluster access
|
||||||
|
before login, and the writer refuses a different cluster before OpenBao access
|
||||||
|
or key generation. A regression test covers this refusal.
|
||||||
|
|
||||||
|
Credential routing selected `openbao-platform-admin-login`, verdict
|
||||||
|
`founder_required`, identity act `oidc_login`. This requires attended operator
|
||||||
|
OIDC/MFA through the contained Warden envelope. It cannot be substituted with
|
||||||
|
root or workload authority. No new login was initiated while the other session's
|
||||||
|
activation ownership remained unconfirmed.
|
||||||
|
|
||||||
|
T04 remains in progress: live custody, ESO delivery, signed preflight, and
|
||||||
|
fenced rotation/invalidation/recovery evidence remain outstanding.
|
||||||
|
|
@ -19,6 +19,7 @@ FIELD = 'REPOSITORY_RENAME_PREFLIGHT_SECRET'
|
||||||
POLICY = 'workload-kv-read-state-hub-rename-preflight'
|
POLICY = 'workload-kv-read-state-hub-rename-preflight'
|
||||||
ROLE = 'state-hub-rename-preflight-eso'
|
ROLE = 'state-hub-rename-preflight-eso'
|
||||||
SA = 'state-hub-preflight-eso'
|
SA = 'state-hub-preflight-eso'
|
||||||
|
CLUSTER_UID = 'a553c742-0115-43d4-99a4-a5ca56fe0786'
|
||||||
|
|
||||||
|
|
||||||
class LaneError(Exception):
|
class LaneError(Exception):
|
||||||
|
|
@ -68,6 +69,12 @@ def approved_contract():
|
||||||
return expected, policy
|
return expected, policy
|
||||||
|
|
||||||
|
|
||||||
|
def assert_cluster(kube):
|
||||||
|
namespace = data(command(kube + ['get', 'namespace', 'kube-system', '-o', 'json']))
|
||||||
|
if namespace.get('metadata', {}).get('uid') != CLUSTER_UID:
|
||||||
|
raise LaneError('primary_cluster_identity_mismatch')
|
||||||
|
|
||||||
|
|
||||||
def assert_fenced(kube):
|
def assert_fenced(kube):
|
||||||
deployment = data(command(kube + ['-n', 'state-hub', 'get', 'deployment', 'state-hub', '-o', 'json']))
|
deployment = data(command(kube + ['-n', 'state-hub', 'get', 'deployment', 'state-hub', '-o', 'json']))
|
||||||
pods = data(command(kube + ['-n', 'state-hub', 'get', 'pods', '-l', 'app=state-hub', '-o', 'json']))
|
pods = data(command(kube + ['-n', 'state-hub', 'get', 'pods', '-l', 'app=state-hub', '-o', 'json']))
|
||||||
|
|
@ -147,10 +154,11 @@ def verify_access(kube, receipt):
|
||||||
|
|
||||||
def run(args, receipt):
|
def run(args, receipt):
|
||||||
role, policy = approved_contract()
|
role, policy = approved_contract()
|
||||||
|
kube = ['kubectl', '--kubeconfig', args.kubeconfig]
|
||||||
|
assert_cluster(kube)
|
||||||
identity = data(bao(['token', 'lookup', '-format=json']))['data']
|
identity = data(bao(['token', 'lookup', '-format=json']))['data']
|
||||||
if 'platform-admin' not in identity['policies'] or 'root' in identity['policies']:
|
if 'platform-admin' not in identity['policies'] or 'root' in identity['policies']:
|
||||||
raise LaneError('attended_platform_admin_required')
|
raise LaneError('attended_platform_admin_required')
|
||||||
kube = ['kubectl', '--kubeconfig', args.kubeconfig]
|
|
||||||
if args.action == 'verify':
|
if args.action == 'verify':
|
||||||
verify_access(kube, receipt)
|
verify_access(kube, receipt)
|
||||||
receipt['status'] = 'custody_verified_pending_eso_and_api_acceptance'
|
receipt['status'] = 'custody_verified_pending_eso_and_api_acceptance'
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,7 @@ class SigningLaneTests(unittest.TestCase):
|
||||||
args = SimpleNamespace(action='rotate', expected_version=1, kubeconfig='/fixture')
|
args = SimpleNamespace(action='rotate', expected_version=1, kubeconfig='/fixture')
|
||||||
result = SimpleNamespace(stdout=json.dumps({'data': {'policies': ['platform-admin']}}).encode())
|
result = SimpleNamespace(stdout=json.dumps({'data': {'policies': ['platform-admin']}}).encode())
|
||||||
with patch.object(lane, 'approved_contract', return_value=({}, '')), \
|
with patch.object(lane, 'approved_contract', return_value=({}, '')), \
|
||||||
|
patch.object(lane, 'assert_cluster'), \
|
||||||
patch.object(lane, 'bao', return_value=result), \
|
patch.object(lane, 'bao', return_value=result), \
|
||||||
patch.object(lane, 'assert_fenced', side_effect=lane.LaneError('fence')), \
|
patch.object(lane, 'assert_fenced', side_effect=lane.LaneError('fence')), \
|
||||||
patch.object(lane.secrets, 'token_hex') as generate:
|
patch.object(lane.secrets, 'token_hex') as generate:
|
||||||
|
|
@ -76,6 +77,18 @@ class SigningLaneTests(unittest.TestCase):
|
||||||
lane.run(args, {})
|
lane.run(args, {})
|
||||||
generate.assert_not_called()
|
generate.assert_not_called()
|
||||||
|
|
||||||
|
def test_wrong_cluster_precedes_any_openbao_access_or_generation(self):
|
||||||
|
args = SimpleNamespace(action='provision', expected_version=0, kubeconfig='/fixture')
|
||||||
|
response = SimpleNamespace(stdout=json.dumps({'metadata': {'uid': 'other-cluster'}}).encode())
|
||||||
|
with patch.object(lane, 'approved_contract', return_value=({}, '')), \
|
||||||
|
patch.object(lane, 'command', return_value=response), \
|
||||||
|
patch.object(lane, 'bao') as access, \
|
||||||
|
patch.object(lane.secrets, 'token_hex') as generate:
|
||||||
|
with self.assertRaisesRegex(lane.LaneError, 'primary_cluster_identity_mismatch'):
|
||||||
|
lane.run(args, {})
|
||||||
|
access.assert_not_called()
|
||||||
|
generate.assert_not_called()
|
||||||
|
|
||||||
def test_unapproved_key_format_or_policy_does_not_pass_contract(self):
|
def test_unapproved_key_format_or_policy_does_not_pass_contract(self):
|
||||||
import tempfile, yaml
|
import tempfile, yaml
|
||||||
ccr = copy.deepcopy(self.ccr)
|
ccr = copy.deepcopy(self.ccr)
|
||||||
|
|
|
||||||
|
|
@ -122,3 +122,11 @@ access checks and a non-mutating signed preflight pass; every API replica uses
|
||||||
the accepted version; rotation/invalidation and recovery are evidenced. No
|
the accepted version; rotation/invalidation and recovery are evidenced. No
|
||||||
repository rename is part of S3 lane acceptance. If demand is withdrawn, record
|
repository rename is part of S3 lane acceptance. If demand is withdrawn, record
|
||||||
the owning decision and cancel this task explicitly rather than provision it.
|
the owning decision and cancel this task explicitly rather than provision it.
|
||||||
|
|
||||||
|
2026-09-05 continuation: verified the live primary cluster identity and healthy
|
||||||
|
single API replica; the signing ExternalSecret is still absent. Added a writer
|
||||||
|
guard against wrong-cluster kubeconfigs before any OpenBao access. The default
|
||||||
|
workstation kubeconfig's local port-forward listener was unavailable. Activation
|
||||||
|
still needs the contained attended OIDC/MFA login and the acceptance evidence
|
||||||
|
above; source preparation is not live completion. See
|
||||||
|
`history/2026-09-05-preflight-signing-activation-readiness.md`.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue