Files
box/bin/exec-watch.sh
T

60 lines
2.5 KiB
Bash
Raw Normal View History

#!/bin/bash
# exec-watch.sh — 15-min watchdog for the bl exec server's signature-auth path.
# Signs a canary op as the exec-canary identity and POSTs it to the
# local exec-constrained server. Records result in /home/super/.exec-watch-status.json
# and appends to logs/exec-watch.log. Failures are pull-based (status file +
# log); wire push alerting here if the fleet wants paging.
#
# Runs via systemd user timer exec-watch.timer (OnUnitActiveSec=15min).
# Server: exec-constrained.py — named ops ONLY, no arbitrary shell;
# signature namespace: exec-constrained, with {op,args,ts,nonce} envelope.
# NOTE: signers file (/home/super/.exec-signers) is synced MANUALLY from the
# VM's /srv/board/allowed_signers on identity renames/adds — bl cannot ssh
# back to the VM, so there is no pull sync. Operator step, documented in
# docs/TOKEN_POLICY.md.
set -uo pipefail
KEY=/home/super/.exec-canary
URL=https://100.123.153.75:8444/exec
STATUS=/home/super/.exec-watch-status.json
LOG=/home/super/Projects/NetVM/logs/exec-watch.log
ts=$(date +%s)
nonce=$(python3 -c "import secrets; print(secrets.token_hex(16))")
payload=$(python3 -c "import json,sys; print(json.dumps({'op':'exec.ping','args':{},'ts':int(sys.argv[1]),'nonce':sys.argv[2]}))" "$ts" "$nonce")
sig=$(printf '%s' "$payload" | ssh-keygen -Y sign -f "$KEY" -n exec-constrained 2>/dev/null)
if [ -z "${sig:-}" ]; then
result="sign-failed"
else
body=$(python3 -c "import json,sys; print(json.dumps({'identity': 'exec-canary', 'payload': sys.argv[1], 'signature': sys.argv[2]}))" "$payload" "$sig")
out=$(curl -sk -m 25 -X POST "$URL" -H 'Content-Type: application/json' -d "$body" 2>/dev/null)
if echo "$out" | grep -q '"rc": 0'; then
result="ok"
else
result="bad-response"
fi
fi
now_iso=$(date -u +%FT%TZ)
{
python3 - "$STATUS" "$now_iso" "$result" <<'PYEOF'
import json, sys
status_path, now_iso, result = sys.argv[1], sys.argv[2], sys.argv[3]
try:
st = json.load(open(status_path))
except Exception:
st = {}
if result == "ok":
st.update({"last_ok": now_iso, "last_fail": None,
"consecutive_failures": 0, "result": "ok"})
else:
st.update({"last_ok": st.get("last_ok"),
"last_fail": now_iso,
"consecutive_failures": st.get("consecutive_failures", 0) + 1,
"result": result})
json.dump(st, open(status_path, "w"))
print(f"[{now_iso}] exec-watch: {result} "
f"(consecutive_failures={st['consecutive_failures']})")
PYEOF
} >> "$LOG" 2>&1
[ "$result" = "ok" ]