feat(recovery): add p0 dr escrow checklist
Some checks failed
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Failing after 2m47s
CD Pipeline / build-and-deploy (push) Has been skipped
CD Pipeline / post-deploy-checks (push) Has been skipped

This commit is contained in:
Your Name
2026-06-29 14:00:47 +08:00
parent bd55386e6e
commit 4defe99bd8
4 changed files with 277 additions and 13 deletions

View File

@@ -0,0 +1,170 @@
#!/usr/bin/env python3
"""Build the single P0-005 DR escrow evidence checklist."""
from __future__ import annotations
import argparse
import json
from datetime import datetime
from pathlib import Path
from typing import Any
SCHEMA_VERSION = "awoooi_dr_escrow_evidence_checklist_v1"
OWNER_PACKET_SCHEMA = "awoooi_post_reboot_next_gate_owner_packets_v1"
OWNER_RESPONSE_SCHEMA = "awoooi_post_reboot_next_gate_owner_response_v1"
GATE_ID = "credential_escrow_evidence"
ITEMS = [
"restic_repository_password",
"offsite_provider_credentials",
"break_glass_admin_credentials",
"dns_registrar_recovery",
"oauth_ai_provider_recovery",
]
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description="Emit one no-secret checklist for the P0-005 DR escrow lane.",
)
parser.add_argument("--output", type=Path, help="Write JSON to this path.")
return parser.parse_args()
def evidence_item(item_id: str) -> dict[str, Any]:
return {
"item_id": item_id,
"required_fields": [
"non_secret_evidence_ref",
"recovery_owner",
"reviewer",
"last_reviewed_at",
"contains_secret_value=false",
],
"accepted_ref_examples": [
f"vault-item-id-for-{item_id}",
f"sealed-envelope-id-for-{item_id}",
f"recovery-checklist-id-for-{item_id}",
f"ticket-id-for-{item_id}",
],
"rejected_values": [
"passwords",
"tokens",
"private keys",
"recovery codes",
"secret URLs",
"session cookies",
],
"marker_dry_run_command": (
"/backup/scripts/mark-credential-escrow-verified.sh "
f"--item {item_id} --evidence-id <NON_SECRET_REF_FOR_{item_id}> --dry-run"
),
}
def owner_packet() -> dict[str, Any]:
return {
"schema_version": OWNER_PACKET_SCHEMA,
"source": {"next_required_gates": [GATE_ID]},
"owner_packets": [
{
"packet_id": GATE_ID,
"title": "P0-005 DR credential escrow evidence",
"priority": "P0",
"required_items": ITEMS,
}
],
}
def owner_response_skeleton() -> dict[str, Any]:
return {
"schema_version": OWNER_RESPONSE_SCHEMA,
"generated_at": datetime.now().astimezone().isoformat(timespec="seconds"),
"responses": [
{
"gate_id": GATE_ID,
"owner_role": "backup_dr_owner",
"owner_team": "platform_security",
"decision": "pending",
"decision_reason": "fill_only_after_all_redacted_refs_are_present",
"affected_scope": "P0-005 DR credential escrow evidence",
"redacted_evidence_refs": ["<ONE_PARENT_TICKET_OR_REVIEW_REF>"],
"followup_owner": "backup_dr_owner",
"runtime_action_requested": False,
"runtime_action_authorized": False,
"host_write_requested": False,
"host_write_authorized": False,
"secret_value_included": False,
"secret_value_collection_allowed": False,
"credential_marker_write_requested": False,
"credential_marker_write_authorized": False,
"escrow_items": [
{
"item_id": item_id,
"non_secret_evidence_ref": f"<NON_SECRET_REF_FOR_{item_id}>",
"recovery_owner": "backup_dr_owner",
"reviewer": "security_reviewer",
"last_reviewed_at": "YYYY-MM-DD",
"contains_secret_value": False,
}
for item_id in ITEMS
],
}
],
}
def build_payload() -> dict[str, Any]:
return {
"schema_version": SCHEMA_VERSION,
"generated_at": datetime.now().astimezone().isoformat(timespec="seconds"),
"workplan_id": "P0-005",
"status": "waiting_for_five_redacted_non_secret_evidence_refs",
"active_gate": GATE_ID,
"required_item_count": len(ITEMS),
"required_items": [evidence_item(item_id) for item_id in ITEMS],
"owner_packet": owner_packet(),
"owner_response_skeleton": owner_response_skeleton(),
"single_preflight_command": (
"python3 scripts/reboot-recovery/post-reboot-owner-response-preflight.py "
"--owner-packet-file <owner-packet.json> "
"--response-file <filled-owner-response.json> --json --no-color"
),
"scorecard_command": (
"python3 scripts/reboot-recovery/post-reboot-credential-escrow-intake-scorecard.py "
"--summary-file <summary.txt> --owner-packet-file <owner-packet.json> "
"--response-file <filled-owner-response.json> "
"--offsite-report-file <offsite-report.txt> "
"--escrow-status-file <escrow-status.txt> --json --no-color"
),
"exit_criteria": [
"preflight_status=ready_for_independent_reviewer_acceptance",
"owner_response_received_count=1",
"owner_response_accepted_count=1",
"forbidden_true_field_count=0",
"effective_escrow_missing_count=0 after marker dry-runs and accepted marker writes",
],
"execution_rules": [
"Use this as the only P0-005 intake packet.",
"Do not create per-item owner packets.",
"Do not include secret values in evidence refs.",
"Do not reopen cold-start or CI/CD while this checklist is pending.",
],
}
def main() -> int:
args = parse_args()
payload = build_payload()
text = json.dumps(payload, indent=2, ensure_ascii=False) + "\n"
if args.output:
args.output.parent.mkdir(parents=True, exist_ok=True)
args.output.write_text(text, encoding="utf-8")
else:
print(text, end="")
return 0
if __name__ == "__main__":
raise SystemExit(main())

View File

@@ -0,0 +1,75 @@
from __future__ import annotations
import json
import subprocess
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[3]
SCRIPT = ROOT / "scripts" / "reboot-recovery" / "dr-escrow-evidence-checklist.py"
ITEMS = {
"restic_repository_password",
"offsite_provider_credentials",
"break_glass_admin_credentials",
"dns_registrar_recovery",
"oauth_ai_provider_recovery",
}
def load_checklist() -> dict:
result = subprocess.run(
[sys.executable, str(SCRIPT)],
text=True,
capture_output=True,
check=True,
)
return json.loads(result.stdout)
def test_checklist_is_single_p0_005_intake_packet() -> None:
payload = load_checklist()
assert payload["schema_version"] == "awoooi_dr_escrow_evidence_checklist_v1"
assert payload["workplan_id"] == "P0-005"
assert payload["active_gate"] == "credential_escrow_evidence"
assert payload["required_item_count"] == 5
assert {item["item_id"] for item in payload["required_items"]} == ITEMS
assert payload["owner_packet"]["source"]["next_required_gates"] == [
"credential_escrow_evidence"
]
assert len(payload["owner_packet"]["owner_packets"]) == 1
assert len(payload["owner_response_skeleton"]["responses"]) == 1
def test_checklist_never_authorizes_runtime_or_secret_collection() -> None:
payload = load_checklist()
response = payload["owner_response_skeleton"]["responses"][0]
forbidden_true_fields = [
"runtime_action_requested",
"runtime_action_authorized",
"host_write_requested",
"host_write_authorized",
"secret_value_included",
"secret_value_collection_allowed",
"credential_marker_write_requested",
"credential_marker_write_authorized",
]
for field in forbidden_true_fields:
assert response[field] is False
for item in response["escrow_items"]:
assert item["contains_secret_value"] is False
def test_checklist_outputs_marker_dry_run_commands_only() -> None:
payload = load_checklist()
for item in payload["required_items"]:
command = item["marker_dry_run_command"]
assert "--dry-run" in command
assert "--item " + item["item_id"] in command
assert " --evidence-id <NON_SECRET_REF_FOR_" in command
assert "--token" not in command
assert "password=" not in command.lower()