Record founder acceptance on CCR-2026-0036 and CCR-2026-0037, install the reader roles, and admit only the email-connect node delivery. Treat a missing OpenBao token period as non-periodic. Assistant: grok Assistant-Session: 01a11cdb-f9fe-7011-947c-936e72007e54
134 lines
5.3 KiB
Bash
134 lines
5.3 KiB
Bash
#!/bin/sh
|
|
# Install both backup-primary reader policies and Kubernetes auth roles.
|
|
# Silent: warden access rejects any child output. Receipts stay on absolute paths.
|
|
set -eu
|
|
export BAO_ADDR="${BAO_ADDR:-http://127.0.0.1:18200}"
|
|
export VAULT_ADDR="${VAULT_ADDR:-http://127.0.0.1:18200}"
|
|
receipt_dir=/home/worsch/.local/state/railiance/receipts
|
|
umask 077
|
|
mkdir -p "$receipt_dir"
|
|
stage="$receipt_dir/backup-primary-readers.stage"
|
|
: >"$stage"
|
|
python3 - "$stage" <<'PY'
|
|
import importlib.util
|
|
import json
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
stage = Path(sys.argv[1])
|
|
root = Path("/home/worsch/railiance-platform")
|
|
spec = importlib.util.spec_from_file_location(
|
|
"credential_change", root / "scripts/credential-change.py"
|
|
)
|
|
cc = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(cc)
|
|
|
|
refs = [
|
|
"CCR-2026-0036-coulomb-social-backup-primary.yaml",
|
|
"CCR-2026-0037-email-connect-backup-primary.yaml",
|
|
]
|
|
safe_role_keys = (
|
|
"audience",
|
|
"bound_service_account_names",
|
|
"bound_service_account_namespaces",
|
|
"policies",
|
|
"token_explicit_max_ttl",
|
|
"token_max_ttl",
|
|
"token_no_default_policy",
|
|
"token_num_uses",
|
|
"token_policies",
|
|
"token_type",
|
|
"ttl",
|
|
)
|
|
|
|
def run(argv):
|
|
result = subprocess.run(argv, cwd=root, capture_output=True)
|
|
if result.returncode != 0:
|
|
stage.write_text(f"exit {result.returncode} step {argv[1]}\n", encoding="utf-8")
|
|
raise SystemExit(result.returncode or 1)
|
|
return result.stdout.decode()
|
|
|
|
receipts = []
|
|
for name in refs:
|
|
ccr = json.loads((root / "credential-change-requests" / name).read_text(encoding="utf-8"))
|
|
if ccr.get("status") != "approved" or ccr["openbao"]["auth"].get("bound_claims_confirmed") is not True:
|
|
stage.write_text(f"{ccr.get('id')} not approved for apply\n", encoding="utf-8")
|
|
raise SystemExit(44)
|
|
blockers = cc.applier_readiness_blockers(ccr)
|
|
if blockers:
|
|
stage.write_text(ccr["id"] + " blocked\n", encoding="utf-8")
|
|
raise SystemExit(45)
|
|
openbao = ccr["openbao"]
|
|
auth = openbao["auth"]
|
|
policy_path = root / openbao["policy_file"]
|
|
run(["bao", "policy", "write", openbao["policy_name"], str(policy_path)])
|
|
installed = run(["bao", "policy", "read", openbao["policy_name"]])
|
|
if cc.normalized_policy_body(installed) != cc.normalized_policy_body(policy_path.read_text(encoding="utf-8")):
|
|
stage.write_text(openbao["policy_name"] + " readback differs\n", encoding="utf-8")
|
|
raise SystemExit(46)
|
|
payload = cc.auth_payload(ccr)
|
|
role_file = Path(f"/home/worsch/.local/state/railiance/receipts/{auth['role']}.payload.json")
|
|
role_file.write_text(json.dumps(payload, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
|
role_file.chmod(0o600)
|
|
run(["bao", "write", f"auth/{auth['mount']}/role/{auth['role']}", f"@{role_file}"])
|
|
raw = run(["bao", "read", "-format=json", f"auth/{auth['mount']}/role/{auth['role']}"])
|
|
data = json.loads(raw).get("data", {})
|
|
observed = {key: data.get(key) for key in safe_role_keys if key in data}
|
|
names = data.get("bound_service_account_names")
|
|
namespaces = data.get("bound_service_account_namespaces")
|
|
policies = data.get("token_policies") or data.get("policies")
|
|
audience = data.get("audience") or data.get("audiences")
|
|
problems = []
|
|
if names != payload["bound_service_account_names"]:
|
|
problems.append("names")
|
|
if namespaces != payload["bound_service_account_namespaces"]:
|
|
problems.append("namespaces")
|
|
if policies != payload["policies"].split(","):
|
|
problems.append("policies")
|
|
if audience not in (payload.get("audience"), [payload.get("audience")]):
|
|
problems.append("audience")
|
|
if data.get("token_no_default_policy") is not True:
|
|
problems.append("no_default")
|
|
if data.get("token_num_uses") != 4:
|
|
problems.append("uses")
|
|
if data.get("token_type") != "service":
|
|
problems.append("type")
|
|
if data.get("ttl") not in (300, "5m", "300s"):
|
|
problems.append("ttl")
|
|
if data.get("token_max_ttl") not in (300, "5m", "300s"):
|
|
problems.append("max_ttl")
|
|
if data.get("token_explicit_max_ttl") not in (300, "5m", "300s"):
|
|
problems.append("explicit_max_ttl")
|
|
if problems:
|
|
stage.write_text(
|
|
json.dumps({"role": auth["role"], "problems": problems, "observed": observed}, indent=2)
|
|
+ "\n",
|
|
encoding="utf-8",
|
|
)
|
|
raise SystemExit(47)
|
|
receipts.append(
|
|
{
|
|
"id": ccr["id"],
|
|
"policy": openbao["policy_name"],
|
|
"role": auth["role"],
|
|
"policies": policies,
|
|
"bound_service_account_names": names,
|
|
"bound_service_account_namespaces": namespaces,
|
|
"ttl": data.get("ttl"),
|
|
"token_max_ttl": data.get("token_max_ttl"),
|
|
"token_explicit_max_ttl": data.get("token_explicit_max_ttl"),
|
|
"token_num_uses": data.get("token_num_uses"),
|
|
"token_no_default_policy": data.get("token_no_default_policy"),
|
|
"token_type": data.get("token_type"),
|
|
}
|
|
)
|
|
|
|
out = Path("/home/worsch/.local/state/railiance/receipts/backup-primary-readers.json")
|
|
out.write_text(json.dumps({"status": "installed", "readers": receipts}, indent=2) + "\n", encoding="utf-8")
|
|
out.chmod(0o600)
|
|
PY
|
|
if [ -s "$stage" ]; then
|
|
exit 48
|
|
fi
|
|
exit 0
|