007946faf4
Claude-Session: https://claude.ai/code/session_01NptTCFtu7dz2Ru89qHRagN
118 lines
5.7 KiB
Python
118 lines
5.7 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""DR-tier-by-default installer gates — mechanical grep-assertions against
|
|
felhom-host-install.sh's known failure mode: a Day-0 that silently regresses one of the
|
|
drill-swept findings (DRILL-day0-vm-2026-07-12 F-1/F-7/F-9/F-10 + the ACL-narrowing 403).
|
|
Run from the repo root: python scripts/hostinstall_gates.py
|
|
|
|
Gates (all must pass; non-zero exit on any failure):
|
|
1. version — exactly ONE version source: SCRIPT_VERSION exists, the header line carries
|
|
no version literal, and the hub Setup-tab const (hub internal/web/configs.go
|
|
hostInstallVersion) equals SCRIPT_VERSION (F-1 structural fix)
|
|
2. age — the `age` package is installed by the agent-install step (F-10)
|
|
3. pbs-apply — configs/felhom-pbs-apply is fetched + installed to
|
|
/usr/local/sbin/felhom-pbs-apply (F-7), and the uninstall removes it
|
|
4. wg — the rendered agent.json defaults wg_tunnel enabled=true (F-9 / decision 5),
|
|
and the byo assert no longer forbids it
|
|
5. acl — the default PVE_STORAGES set still contains felhom-pbs (narrowing it is the
|
|
drill's apply-bridge 403)
|
|
"""
|
|
import io, os, re, sys
|
|
|
|
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
SCRIPT = os.path.join(ROOT, "scripts", "felhom-host-install.sh")
|
|
HUB_CONFIGS = os.path.join(ROOT, "hub", "internal", "web", "configs.go")
|
|
|
|
fails = []
|
|
|
|
|
|
def fail(msg):
|
|
fails.append(msg)
|
|
print("FAIL:", msg)
|
|
|
|
|
|
def ok(msg):
|
|
print(" ok:", msg)
|
|
|
|
|
|
with io.open(SCRIPT, "r", encoding="utf-8") as f:
|
|
src = f.read()
|
|
lines = src.splitlines()
|
|
|
|
# ── 1. version single-source (F-1) ──────────────────────────────────────────────
|
|
m = re.search(r'^SCRIPT_VERSION="(\d+\.\d+\.\d+)"', src, re.M)
|
|
if not m:
|
|
fail("SCRIPT_VERSION=\"x.y.z\" not found — the single version source is gone")
|
|
script_ver = None
|
|
else:
|
|
script_ver = m.group(1)
|
|
ok("SCRIPT_VERSION=%s" % script_ver)
|
|
|
|
# header (first 10 lines) must NOT carry its own version literal — that is the F-1 drift.
|
|
header = "\n".join(lines[:10])
|
|
if re.search(r'felhom-host-install\.sh\s+v\d+\.\d+\.\d+', header):
|
|
fail("header line carries a hardcoded version — SCRIPT_VERSION is the only source (F-1)")
|
|
else:
|
|
ok("header has no version literal")
|
|
|
|
# hub Setup-tab const must equal SCRIPT_VERSION (the copy the drill found at 1.12.0).
|
|
if os.path.exists(HUB_CONFIGS) and script_ver:
|
|
with io.open(HUB_CONFIGS, "r", encoding="utf-8") as f:
|
|
hub_src = f.read()
|
|
hm = re.search(r'hostInstallVersion\s*=\s*"(\d+\.\d+\.\d+)"', hub_src)
|
|
if not hm:
|
|
fail("hub hostInstallVersion const not found in internal/web/configs.go")
|
|
elif hm.group(1) != script_ver:
|
|
fail("hub Setup-tab hostInstallVersion=%s != SCRIPT_VERSION=%s (F-1: bump both together)"
|
|
% (hm.group(1), script_ver))
|
|
else:
|
|
ok("hub Setup-tab hostInstallVersion matches (%s)" % hm.group(1))
|
|
else:
|
|
if not os.path.exists(HUB_CONFIGS):
|
|
fail("hub/internal/web/configs.go not found — cannot cross-check the Setup-tab version")
|
|
|
|
# ── 2. age package (F-10) ───────────────────────────────────────────────────────
|
|
# must match the REAL install invocation, not the log_dry echo (red-proof-hardened twice:
|
|
# a prefix regex matched "agekit", then a loose one matched the dry-run print line).
|
|
if re.search(r'DEBIAN_FRONTEND=noninteractive apt-get install -y -q age\b', src):
|
|
ok("age is in the installed package set")
|
|
else:
|
|
fail("`age` install not found (F-10 — the fresh-box escrow ceremony dies without it)")
|
|
|
|
# ── 3. pbs-apply wrapper shipped + removed (F-7) ────────────────────────────────
|
|
if 'fetch_raw "configs/felhom-pbs-apply"' in src:
|
|
ok("felhom-pbs-apply is fetched from the agent repo")
|
|
else:
|
|
fail("configs/felhom-pbs-apply fetch not found (F-7 — pbsdr capabilities born DEGRADED)")
|
|
if re.search(r'install -m 0755 -o root -g root "\$patmp" /usr/local/sbin/felhom-pbs-apply', src):
|
|
ok("felhom-pbs-apply installed 0755 to /usr/local/sbin")
|
|
else:
|
|
fail("felhom-pbs-apply install line not found (F-7)")
|
|
if re.search(r'rm -f /usr/local/sbin/felhom-pbs-apply', src):
|
|
ok("uninstall removes felhom-pbs-apply")
|
|
else:
|
|
fail("uninstall does not remove /usr/local/sbin/felhom-pbs-apply")
|
|
|
|
# ── 4. wg_tunnel default-on (F-9 / decision 5) ──────────────────────────────────
|
|
if re.search(r"base\.setdefault\('wg_tunnel',\s*\{\"enabled\":\s*True\}\)", src):
|
|
ok("rendered agent.json defaults wg_tunnel.enabled=true")
|
|
else:
|
|
fail("wg_tunnel enabled-by-default missing from the agent.json render (F-9)")
|
|
# the byo assert must NOT forbid wg_tunnel any more (decision 5: WG is base infrastructure).
|
|
byo_assert = re.search(r"byo-forbidden config keys.*?sys\.exit\(1\)", src, re.S)
|
|
if byo_assert and "wg_tunnel" in byo_assert.group(0):
|
|
fail("the byo config assert still forbids wg_tunnel.enabled (decision 5 retired that)")
|
|
else:
|
|
ok("byo assert no longer forbids wg_tunnel")
|
|
|
|
# ── 5. default ACL keeps felhom-pbs (the drill 403) ─────────────────────────────
|
|
if re.search(r'^PVE_STORAGES=\([^)]*felhom-pbs[^)]*\)', src, re.M):
|
|
ok("PVE_STORAGES default contains felhom-pbs")
|
|
else:
|
|
fail("felhom-pbs missing from the default PVE_STORAGES — narrowing it 403s the PBS-DR apply-bridge")
|
|
|
|
print()
|
|
if fails:
|
|
print("hostinstall gates: %d FAILURE(S)" % len(fails))
|
|
sys.exit(1)
|
|
print("hostinstall gates: ALL PASS")
|