audit-core/scripts/renew-runtime-lease.sh
tegwick 21e8ce9b72 Align ESO token scripts with the periodic token (RPF-WP-0046)
renew-runtime-lease.sh is recovery-only now: it mints with -period
instead of -ttl=768h, so a recovery no longer undoes the platform's
periodic token, and defaults BAO_ADDR to the tunnel rather than the
retired bao.coulomb.social. The old openbao-eso-token-apply.sh is
retired for the same two reasons.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 151986@bnt-lap001
Assistant-Session: ccd02b6b-80ae-48e5-8cad-9c8f74d21a67
2026-09-24 02:22:49 +02:00

135 lines
4.9 KiB
Bash
Executable file

#!/usr/bin/env bash
# RECOVERY ONLY: contained remint of the ESO token, then force-sync the runtime lease.
#
# Routine renewal is not this script's job. Since 2026-09-23 (railiance-platform
# RPF-WP-0046) external-secrets/openbao-audit-core-eso-token is a periodic 168h
# token renewed daily by the CronJob external-secrets/eso-token-renewer. Run
# this only when that token is lost or revoked and the platform has not
# re-minted it. It mints the same shape (-period, not -ttl), so a recovery
# does not undo the periodic lifecycle.
#
# Must be silent: `warden access --exec` fails closed on any child stdout/stderr
# even when the command succeeds. Persist metadata only. Never print a token.
#
# warden access openbao-platform-admin-login --exec -- \
# env RAILIANCE01_KUBECONFIG="$HOME/.kube/config-railiance01" \
# BAO_ADDR=http://127.0.0.1:18200 \
# /home/worsch/audit-core/scripts/renew-runtime-lease.sh
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
export RAILIANCE01_KUBECONFIG="${RAILIANCE01_KUBECONFIG:-/home/worsch/.kube/config-railiance01}"
export KUBECONFIG="$RAILIANCE01_KUBECONFIG"
# bao.coulomb.social is retired (2026-09-15); the tunnel is the operator path.
export BAO_ADDR="${BAO_ADDR:-http://127.0.0.1:18200}"
# Contained login persists the session in $HOME/.vault-token and unsets
# BAO_TOKEN/VAULT_TOKEN. Do not prompt; do not re-export a token.
unset BAO_TOKEN VAULT_TOKEN OPENBAO_TOKEN || true
SECRET_NAME="${OPENBAO_AUDIT_CORE_ESO_SECRET:-openbao-audit-core-eso-token}"
SECRET_NS="${OPENBAO_AUDIT_CORE_ESO_NAMESPACE:-external-secrets}"
POLICY="${OPENBAO_AUDIT_CORE_POLICIES:-external-secrets-audit-core}"
PERIOD="${OPENBAO_AUDIT_CORE_ESO_PERIOD:-168h}"
EVIDENCE="$ROOT/docs/evidence/$(date -u +%Y-%m-%d)-eso-token-remint.json"
WORKDIR="${HOME:-/tmp}/audit-core-remint"
mkdir -p "$WORKDIR"
chmod 700 "$WORKDIR" 2>/dev/null || true
_write_evidence() {
python3 - "$EVIDENCE" "$@" <<'PY' >/dev/null 2>&1 || true
import json, sys
from datetime import datetime, timezone
from pathlib import Path
path, step = Path(sys.argv[1]), sys.argv[2]
extra = sys.argv[3] if len(sys.argv) > 3 else "{}"
try:
payload = json.loads(extra)
except Exception:
payload = {}
doc = {
"step": step,
"observed_at": datetime.now(timezone.utc).replace(microsecond=0).isoformat(),
"secret": "external-secrets/openbao-audit-core-eso-token",
"lease_path": "database/creds/audit-core-runtime",
}
doc.update(payload)
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(json.dumps(doc, indent=2) + "\n")
PY
}
_write_evidence started '{"kubeconfig_set": true}'
if ! command -v bao >/dev/null 2>&1; then
_write_evidence failed '{"reason": "bao_missing"}'
exit 1
fi
if ! command -v kubectl >/dev/null 2>&1; then
_write_evidence failed '{"reason": "kubectl_missing"}'
exit 1
fi
if [[ ! -f "$RAILIANCE01_KUBECONFIG" ]]; then
_write_evidence failed '{"reason": "kubeconfig_missing"}'
exit 1
fi
if ! kubectl get ns audit-core >/dev/null 2>&1; then
_write_evidence failed '{"reason": "not_railiance01"}'
exit 1
fi
if kubectl get ns core-hub-staging >/dev/null 2>&1; then
_write_evidence failed '{"reason": "coulombcore_kubeconfig"}'
exit 1
fi
health="$(curl -fsS "$BAO_ADDR/v1/sys/health" 2>/dev/null || true)"
if printf '%s' "$health" | grep -q '"sealed":true'; then
_write_evidence failed '{"reason": "openbao_sealed"}'
exit 1
fi
token_json_file="$WORKDIR/token-create.json"
rm -f "$token_json_file"
if ! bao token create -policy="$POLICY" -period="$PERIOD" -orphan -format=json \
>"$token_json_file" 2>/dev/null; then
_write_evidence failed '{"reason": "token_create_failed"}'
rm -f "$token_json_file"
exit 1
fi
child_file="$WORKDIR/eso.token"
python3 - "$token_json_file" "$child_file" <<'PY' >/dev/null 2>&1
import json, sys
from pathlib import Path
raw = json.loads(Path(sys.argv[1]).read_text())
token = (raw.get("auth") or {}).get("client_token") or ""
if len(token) < 8:
raise SystemExit(1)
Path(sys.argv[2]).write_text(token)
Path(sys.argv[2]).chmod(0o600)
PY
create_status=$?
rm -f "$token_json_file"
if [[ $create_status -ne 0 || ! -s "$child_file" ]]; then
_write_evidence failed '{"reason": "token_parse_failed"}'
rm -f "$child_file"
exit 1
fi
_write_evidence token_created "{\"period\": \"$PERIOD\", \"orphan\": true}"
kubectl -n "$SECRET_NS" delete secret "$SECRET_NAME" --ignore-not-found >/dev/null 2>&1 || true
if ! kubectl -n "$SECRET_NS" create secret generic "$SECRET_NAME" \
--from-file=token="$child_file" >/dev/null 2>&1; then
_write_evidence failed '{"reason": "secret_create_failed"}'
rm -f "$child_file"
exit 1
fi
rm -f "$child_file"
_write_evidence secret_replaced '{"namespace": "external-secrets"}'
if ! kubectl -n audit-core annotate externalsecret audit-core-database \
force-sync="$(date -u +%s)" --overwrite >/dev/null 2>&1; then
_write_evidence failed '{"reason": "annotate_failed"}'
exit 1
fi
_write_evidence annotated '{"externalsecret": "audit-core/audit-core-database"}'
exit 0