#!/usr/bin/env bash set -euo pipefail EVIDENCE="${OPENBAO_EMERGENCY_EVIDENCE:-${1:-/tmp/netkingdom-openbao-emergency-drill/evidence.json}}" usage() { cat <<'USAGE' Usage: scripts/openbao-validate-emergency-drill-evidence.sh [evidence.json] Validates non-secret OpenBao emergency seal/unseal drill evidence. The evidence file should record timing, status checks, verification flags, and operator notes, but must not contain OpenBao tokens, unseal shares, root tokens, private keys, passwords, OTP seeds, or recovery codes. Environment: OPENBAO_EMERGENCY_EVIDENCE Evidence JSON path. Default: /tmp/netkingdom-openbao-emergency-drill/evidence.json USAGE } if [ "${1:-}" = "-h" ] || [ "${1:-}" = "--help" ]; then usage exit 0 fi python3 - "$EVIDENCE" <<'PY' from __future__ import annotations import json import sys from pathlib import Path path = Path(sys.argv[1]) if not path.exists(): print(f"[FAIL] emergency drill evidence file is missing: {path}", file=sys.stderr) sys.exit(1) try: data = json.loads(path.read_text(encoding="utf-8")) except json.JSONDecodeError as exc: print(f"[FAIL] emergency drill evidence is not valid JSON: {exc}", file=sys.stderr) sys.exit(1) if not isinstance(data, dict): print("[FAIL] emergency drill evidence root must be a JSON object", file=sys.stderr) sys.exit(1) required_strings = [ "drill_date", "operator", "source_cluster", "source_namespace", "source_pod", "seal_started_at", "sealed_status_observed_at", "unseal_started_at", "unseal_completed_at", "post_unseal_verification", ] required_true = [ "seal_command_issued", "sealed_status_confirmed", "unseal_quorum_available", "unseal_completed", "post_unseal_status_verified", "post_unseal_readiness_verified", "no_secret_material_recorded", ] errors: list[str] = [] for key in required_strings: value = data.get(key) if not isinstance(value, str) or not value.strip(): errors.append(f"missing non-empty string: {key}") for key in required_true: if data.get(key) is not True: errors.append(f"must be true: {key}") window = data.get("availability_window_minutes") if not isinstance(window, int) or window < 0: errors.append("availability_window_minutes must be a non-negative integer") encoded = json.dumps(data, sort_keys=True) secret_markers = [ "OPENBAO_ROOT_TOKEN", "VAULT_TOKEN", "BEGIN PRIVATE KEY", "BEGIN OPENSSH PRIVATE KEY", "AGE-SECRET-KEY-1", "-----BEGIN", "hvs.", ] for marker in secret_markers: if marker in encoded: errors.append(f"secret-looking marker present: {marker}") placeholder_markers = [ "YYYY-MM-DD", "example", "Do not record", "<", ] for marker in placeholder_markers: if marker in encoded: errors.append(f"template placeholder present: {marker}") if errors: for error in errors: print(f"[FAIL] {error}", file=sys.stderr) sys.exit(1) print(f"[OK] emergency drill evidence is structurally valid: {path}") print(f"[OK] drill_date: {data['drill_date']}") print(f"[OK] source: {data['source_cluster']}/{data['source_namespace']}/{data['source_pod']}") print(f"[OK] availability_window_minutes: {data['availability_window_minutes']}") PY