Add OpenBao emergency drill evidence validator

This commit is contained in:
2026-06-02 00:08:17 +02:00
parent 123b9aafce
commit 606a5f3e1e
5 changed files with 152 additions and 1 deletions

View File

@@ -15,6 +15,7 @@ OPENBAO_RELEASE ?= openbao
OPENBAO_VALUES ?= helm/openbao-values.yaml
OPENBAO_VERIFY_AUTH_ARGS ?=
OPENBAO_RESTORE_EVIDENCE ?= /tmp/netkingdom-openbao-restore-drill/evidence.json
OPENBAO_EMERGENCY_EVIDENCE ?= /tmp/netkingdom-openbao-emergency-drill/evidence.json
##@ CloudNative PG (cnpg) — primary database operator
@@ -131,6 +132,10 @@ openbao-validate-restore-evidence: ## Validate non-secret OpenBao restore-drill
OPENBAO_RESTORE_EVIDENCE='$(OPENBAO_RESTORE_EVIDENCE)' \
scripts/openbao-validate-restore-evidence.sh
openbao-validate-emergency-evidence: ## Validate non-secret OpenBao emergency seal/unseal drill evidence JSON
OPENBAO_EMERGENCY_EVIDENCE='$(OPENBAO_EMERGENCY_EVIDENCE)' \
scripts/openbao-validate-emergency-drill-evidence.sh
##@ Backup
backup: ## Backup platform services (PostgreSQL logical dump) — age-encrypted to Nextcloud
@@ -143,4 +148,4 @@ help: ## Show this help
/^[a-zA-Z_-]+:.*?##/ { printf " \033[36m%-22s\033[0m %s\n", $$1, $$2 } \
/^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) }' $(MAKEFILE_LIST)
.PHONY: db-deploy db-status db-shell db-logs apps-pg-deploy apps-pg-status apps-pg-shell apps-pg-logs pg-deploy pg-status pg-pgpool-check valkey-deploy valkey-status openbao-repo openbao-dry-run openbao-deploy openbao-status openbao-verify openbao-verify-post-unseal openbao-configure-initial openbao-verify-authenticated openbao-validate-restore-evidence backup help
.PHONY: db-deploy db-status db-shell db-logs apps-pg-deploy apps-pg-status apps-pg-shell apps-pg-logs pg-deploy pg-status pg-pgpool-check valkey-deploy valkey-status openbao-repo openbao-dry-run openbao-deploy openbao-status openbao-verify openbao-verify-post-unseal openbao-configure-initial openbao-verify-authenticated openbao-validate-restore-evidence openbao-validate-emergency-evidence backup help

View File

@@ -0,0 +1,21 @@
{
"drill_date": "2026-06-01",
"operator": "platform-root",
"source_cluster": "railiance01",
"source_namespace": "openbao",
"source_pod": "openbao-0",
"seal_started_at": "2026-06-01T22:00:00Z",
"seal_command_issued": true,
"sealed_status_confirmed": true,
"sealed_status_observed_at": "2026-06-01T22:00:30Z",
"unseal_quorum_available": true,
"unseal_started_at": "2026-06-01T22:01:00Z",
"unseal_completed": true,
"unseal_completed_at": "2026-06-01T22:02:30Z",
"post_unseal_status_verified": true,
"post_unseal_readiness_verified": true,
"post_unseal_verification": "bao status reported Sealed false and make openbao-verify-post-unseal passed after the drill",
"availability_window_minutes": 3,
"no_secret_material_recorded": true,
"notes": "Do not record OpenBao tokens, root tokens, unseal shares, private keys, passwords, OTP seeds, or recovery codes."
}

View File

@@ -322,6 +322,16 @@ Audit Core backend that writes JSONL records under
days. Use it only to wire interfaces and setup validation before the durable
Audit Core archive exists.
Emergency seal/unseal drills are disruptive and must only run in an attended
window with threshold unseal shares available. Record non-secret drill evidence
using `docs/openbao-emergency-drill-evidence.example.json` as a template, then
validate it with:
```bash
make openbao-validate-emergency-evidence \
OPENBAO_EMERGENCY_EVIDENCE=/path/to/evidence.json
```
Monitoring baseline:
- pod readiness and liveness from Kubernetes probes

View File

@@ -0,0 +1,107 @@
#!/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}")
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

View File

@@ -301,6 +301,14 @@ restore completion, unseal/status/test-secret verification, isolated
environment destruction, and a `no_secret_material_recorded` assertion. This
keeps `NET-WP-0017-T02` from relying on a bare UI checkbox for restore proof.
**2026-06-01:** Added the matching non-secret emergency seal/unseal drill
evidence template and `make openbao-validate-emergency-evidence`. The validator
requires an attended seal/unseal evidence file with timing, sealed-state proof,
unseal quorum availability, post-unseal verification, availability-window
duration, and `no_secret_material_recorded`. The validator does not run the
disruptive drill; it only checks the evidence captured after the attended
operation.
### T07 - Cross-Repo Transition Tasks
```task