RPF-WP-0015: repair recovery preflight for attest-chain Job pods and current audit-core digest
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
a86dfe2220
commit
63df822f6c
4 changed files with 49 additions and 4 deletions
|
|
@ -141,6 +141,6 @@
|
|||
"docs/openbao-reboot-snapshot-receipt.example.json": "b95903a3e1aacb607eecc3f0440f08e62480d09da47bd7ab96c3eb8814f2eaee",
|
||||
"docs/railiance01-coordinated-reboot.md": "9969c2f498587710154e8d9a650fb8a13159e9e7b99918291dbdf577b131b7e7",
|
||||
"scripts/audit-core-database-lease-recovery.py": "c87fb026c527fc17116067c5742d324340707ce4aecf440d0d4e618db9b872a1",
|
||||
"scripts/audit-core-recovery-preflight.py": "802f73fa4352f64bc0972d813e3dd874f3ec2f428e162b23158c9d8d6fd8e710"
|
||||
"scripts/audit-core-recovery-preflight.py": "248c663bad31860dd4f62870db00f6d624143d18c18f86634a39ceda12c7bc66"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ from typing import Any
|
|||
|
||||
EXPECTED_IMAGE = (
|
||||
"forgejo.coulomb.social/coulomb/audit-core@"
|
||||
"sha256:c2fe39a0185b99be3fc0cb14d2de69772b8e66e20490097c9d11d90cc39719a6"
|
||||
"sha256:ec15f63d49226bfe507af2bc38ffbd5e83f549ba2c6d1ae7a338e7e053f34615"
|
||||
)
|
||||
EXPECTED_DB_KEYS = {"username", "password", "host", "port", "dbname"}
|
||||
|
||||
|
|
@ -30,6 +30,18 @@ class PreflightError(RuntimeError):
|
|||
pass
|
||||
|
||||
|
||||
def serving_pods(pod_list: dict[str, Any]) -> list[dict[str, Any]]:
|
||||
"""Deployment pods only: audit-core attest-chain Job pods share the app label."""
|
||||
return [
|
||||
pod
|
||||
for pod in pod_list.get("items", [])
|
||||
if not any(
|
||||
owner.get("kind") == "Job"
|
||||
for owner in pod.get("metadata", {}).get("ownerReferences", [])
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
class Remote:
|
||||
def __init__(self, host: str) -> None:
|
||||
self.host = host
|
||||
|
|
@ -204,7 +216,7 @@ def common_state(remote: Remote, now: datetime) -> dict[str, Any]:
|
|||
["-n", "audit-core", "get", "pods", "-l", "app.kubernetes.io/name=audit-core"],
|
||||
label="read audit-core pod metadata",
|
||||
)
|
||||
pods = pod_list.get("items", [])
|
||||
pods = serving_pods(pod_list)
|
||||
if len(pods) != 1:
|
||||
raise PreflightError(f"expected one audit-core pod, observed {len(pods)}")
|
||||
pod = pods[0]
|
||||
|
|
|
|||
|
|
@ -19,6 +19,26 @@ SPEC.loader.exec_module(module)
|
|||
|
||||
|
||||
class RecoveryPreflightTests(unittest.TestCase):
|
||||
def test_serving_pods_ignores_job_owned_attest_chain_pods(self) -> None:
|
||||
def pod(name: str, kind: str) -> dict:
|
||||
return {"metadata": {"name": name, "ownerReferences": [{"kind": kind}]}}
|
||||
|
||||
pods = {
|
||||
"items": [
|
||||
pod("audit-core-66767ddd58-x", "ReplicaSet"),
|
||||
pod("audit-core-attest-chain-1", "Job"),
|
||||
pod("audit-core-attest-chain-2", "Job"),
|
||||
]
|
||||
}
|
||||
self.assertEqual(
|
||||
["audit-core-66767ddd58-x"],
|
||||
[item["metadata"]["name"] for item in module.serving_pods(pods)],
|
||||
)
|
||||
|
||||
def test_serving_pods_keeps_two_deployment_pods_visible(self) -> None:
|
||||
pods = {"items": [{"metadata": {"ownerReferences": [{"kind": "ReplicaSet"}]}}] * 2}
|
||||
self.assertEqual(2, len(module.serving_pods(pods)))
|
||||
|
||||
def test_condition_selects_named_condition(self) -> None:
|
||||
resource = {
|
||||
"status": {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ flavor: residual
|
|||
owner: codex
|
||||
topic_slug: railiance
|
||||
created: "2026-08-22"
|
||||
updated: "2026-10-03"
|
||||
updated: "2026-10-14"
|
||||
related:
|
||||
- AUDIT-WP-0008
|
||||
- WH-ENG-20260822-AUDIT-E2-01
|
||||
|
|
@ -365,3 +365,16 @@ T02/T03 remain waiting on fresh attended execution windows, named abort operator
|
|||
> critical path (identity → authz → secrets → SecurityCase execution). Tasks are unchanged;
|
||||
> promote back when demand or risk appears. See the-custodian
|
||||
> `history/20261011-netkingdom-security-progress-assessment.md`.
|
||||
|
||||
## Window preparation — 2026-10-14
|
||||
|
||||
The founder chose to prepare attended windows for T02 and T03 (D5 `2fd5849c` already
|
||||
authorizes the outage exercise; each window still needs its own GO). The read-only
|
||||
preflight had drifted: audit-core's attest-chain CronJob pods share the app label, so
|
||||
the single-pod check failed, and the reviewed digest pin was stale. The preflight now
|
||||
ignores Job-owned pods and pins audit-core's current deploy digest `ec15f63d…`
|
||||
(audit-core `deploy/audit-core.yaml`, descendant of the `abd22fa` expiry prerequisite).
|
||||
Both preflights then pass every automated check with no secret value observed; only
|
||||
operator gates remain. The review contract's preflight hash is refreshed, so owner
|
||||
receipts must be renewed against it. Unrelated pre-existing test failures (credential
|
||||
change, factory audit delivery, informed_decision import) are unchanged.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue