From b1b5c7e7e151770a74ce011d4333f28e3e720f2d Mon Sep 17 00:00:00 2001 From: kisfenyo Date: Sun, 4 Oct 2026 16:16:24 +0200 Subject: [PATCH] installer 1.30.0 (not yet tagged): the crash guard units + config, the root-owned slow-lane trust files (os-trust.json, operator-signers), their removal on uninstall Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS --- scripts/CHANGELOG.md | 14 ++++++ scripts/felhom-host-install.sh | 87 +++++++++++++++++++++++++++++++++- 2 files changed, 100 insertions(+), 1 deletion(-) diff --git a/scripts/CHANGELOG.md b/scripts/CHANGELOG.md index 937d6efe..247e6c1b 100644 --- a/scripts/CHANGELOG.md +++ b/scripts/CHANGELOG.md @@ -1,3 +1,17 @@ +## felhom-host-install.sh 1.30.0 — the crash guard and the slow-lane trust files (2026-10-04) + +Needs agent ≥ 0.142.0 at the pinned tag for the crash guard (an older agent: skipped with a warning, the box keeps +`kernel.panic = 0`). + +- **The crash guard** (`09` decision 88, R-851, `11` §5.9): fetches `configs/felhom-crash-guard`, its two services, the + timer and `crash-guard.conf` from the pinned agent tag; syntax-checks; installs (0755 / 0644 root); enables + `felhom-crash-guard.service` + `felhom-crash-guard-check.timer` now. An existing `/etc/felhom/crash-guard.conf` is kept. +- **The root-owned slow-lane trust files** (`11` §5.8, the wrapper's R3): `/etc/felhom/os-trust.json` (this box's + `host_id`; `ring0_slow_lane` false — an existing file keeps its mark) and `/etc/felhom/operator-signers` (the + operational operator key in ssh `allowed_signers` form). The agent's own config is agent-writable and is not trusted + for a Docker step. +- Uninstall removes all of it (`kernel.panic` returns to the kernel default 0 at the next boot). + ## felhom-host-install.sh 1.29.0 — installs the OS-update wrapper (2026-10-04, `11-os-updates.md` §8 step 2) - Step 5 fetches `configs/felhom-os-apply` from the pinned agent tag and installs it as diff --git a/scripts/felhom-host-install.sh b/scripts/felhom-host-install.sh index 1f0fc7a2..c6da1b96 100644 --- a/scripts/felhom-host-install.sh +++ b/scripts/felhom-host-install.sh @@ -184,7 +184,7 @@ set -euo pipefail -SCRIPT_VERSION="1.29.0" # the SINGLE version source (F-1): -h and the run banners follow it. +SCRIPT_VERSION="1.30.0" # the SINGLE version source (F-1): -h and the run banners follow it. # The hub used to carry a copy for its Setup tab; R-94 DELETED it # (2026-08-02) because the hub cannot know which version a box runs — # the Setup command fetches this script at run time. scripts/ @@ -1158,6 +1158,18 @@ run_uninstall() { if [[ -f /usr/local/sbin/felhom-pbs-apply ]]; then run rm -f /usr/local/sbin/felhom-pbs-apply; else log_skip " felhom-pbs-apply already absent"; fi if [[ -f /usr/local/sbin/felhom-backup-target-apply ]]; then run rm -f /usr/local/sbin/felhom-backup-target-apply; else log_skip " felhom-backup-target-apply already absent"; fi if [[ -f /usr/local/sbin/felhom-os-apply ]]; then run rm -f /usr/local/sbin/felhom-os-apply; else log_skip " felhom-os-apply already absent"; fi + # 1.30.0: the crash guard (kernel.panic goes back to the kernel default 0 at the next boot) and the root-owned + # slow-lane trust files. + if systemctl list-unit-files felhom-crash-guard.service >/dev/null 2>&1; then + run systemctl disable --now felhom-crash-guard-check.timer felhom-crash-guard.service 2>/dev/null || true + fi + local cgf + for cgf in /usr/local/sbin/felhom-crash-guard /etc/systemd/system/felhom-crash-guard.service \ + /etc/systemd/system/felhom-crash-guard-check.service /etc/systemd/system/felhom-crash-guard-check.timer \ + /etc/felhom/crash-guard.conf /etc/felhom/os-trust.json /etc/felhom/operator-signers; do + if [[ -e "$cgf" ]]; then run rm -f "$cgf"; fi + done + if [[ -d /var/lib/felhom-crash-guard ]]; then run rm -rf /var/lib/felhom-crash-guard; fi if [[ -f /var/lib/vz/snippets/felhom-guest-hook.sh ]]; then run rm -f /var/lib/vz/snippets/felhom-guest-hook.sh; fi local dconf _dnsmasq_touched=false for dconf in /etc/dnsmasq.d/felhom-*.conf; do @@ -2395,6 +2407,38 @@ step_agent_install() { rm -f "$ostmp" fi + # The crash guard (agent >= 0.142.0, `09` decision 88, `11` §5.9): a crashed host restarts by itself (kernel.panic = + # 10 s), but the 3rd unclean stop within 60 minutes leaves it off. A root boot unit + an hourly re-arm timer + its + # config. Like the wrapper above, an older pinned agent has none of it — skipped with a warning, never fatal. + if $DRY_RUN; then + log_dry "fetch configs/felhom-crash-guard + its .service/.timer + crash-guard.conf ; install ; enable --now felhom-crash-guard.service felhom-crash-guard-check.timer" + else + local cgtmp; cgtmp=$(mktemp -d -t felhom-cg.XXXXXX) + local -a _cgauth; _git_auth_args _cgauth + local cgok=true cgn + for cgn in felhom-crash-guard felhom-crash-guard.service felhom-crash-guard-check.service felhom-crash-guard-check.timer crash-guard.conf; do + curl -fsS "${_cgauth[@]}" -o "$cgtmp/$cgn" \ + "$GITEA_BASE/$GITEA_OWNER/$AGENT_REPO/raw/tag/v$ART_AGENT_VER/configs/$cgn" 2>/dev/null && [[ -s "$cgtmp/$cgn" ]] || { cgok=false; break; } + done + if $cgok; then + python3 -c 'import ast,sys; ast.parse(open(sys.argv[1]).read())' "$cgtmp/felhom-crash-guard" \ + || { rm -rf "$cgtmp"; die "fetched felhom-crash-guard failed the python3 syntax check — refusing to install"; } + install -m 0755 -o root -g root "$cgtmp/felhom-crash-guard" /usr/local/sbin/felhom-crash-guard + for cgn in felhom-crash-guard.service felhom-crash-guard-check.service felhom-crash-guard-check.timer; do + install -m 0644 -o root -g root "$cgtmp/$cgn" "/etc/systemd/system/$cgn" + done + install -d -m 0755 -o root -g root /etc/felhom + [[ -f /etc/felhom/crash-guard.conf ]] || install -m 0644 -o root -g root "$cgtmp/crash-guard.conf" /etc/felhom/crash-guard.conf + systemctl daemon-reload + systemctl enable --now felhom-crash-guard.service felhom-crash-guard-check.timer \ + || die "could not enable the crash guard units" + log_success " installed the crash guard (kernel.panic=$(cat /proc/sys/kernel/panic) s; 3rd crash in 60 min stays off)" + else + log_warn " agent v$ART_AGENT_VER carries no crash guard (older than 0.142.0) — a crashed host stays off (kernel.panic=0)" + fi + rm -rf "$cgtmp" + fi + # Sudoers — fetch the canonical file, validate with visudo -cf BEFORE installing (0440 root:root). if $DRY_RUN; then log_dry "fetch configs/felhom-agent.sudoers ; visudo -cf ; install 0440 -> $AGENT_SUDOERS" @@ -2845,9 +2889,50 @@ PY else log_warn " no felhom-agent systemd unit — daemon host-report loop not started (provision one-shot still works)" fi + _write_slow_lane_trust _state_mark agent_config } +# _write_slow_lane_trust writes the ROOT-OWNED trust anchors the OS wrapper's slow lane checks itself (1.30.0, `11` §5.8, +# R3): /etc/felhom/os-trust.json (this box's host_id; ring0_slow_lane FALSE — only the demo boxes are marked, by hand) +# and /etc/felhom/operator-signers (the operational key, ssh allowed_signers form). The agent's own config is +# agent-writable and therefore NOT trusted for this. An existing os-trust.json keeps its ring0 mark. +_write_slow_lane_trust() { + if $DRY_RUN; then + log_dry "write /etc/felhom/os-trust.json {host_id=$HOST_ID, ring0_slow_lane=false} + /etc/felhom/operator-signers (${RESOLVED_OP_ID:-}) 0644 root" + return 0 + fi + if [[ -z "$HOST_ID" ]]; then + log_warn " no host_id — the slow-lane trust file is not written (Docker steps stay refused)" + return 0 + fi + install -d -m 0755 -o root -g root /etc/felhom + HOST_ID="$HOST_ID" python3 - <<'PY' +import json, os +p = "/etc/felhom/os-trust.json" +d = {} +try: + d = json.load(open(p)) +except (OSError, ValueError): + pass +d["host_id"] = os.environ["HOST_ID"] +d.setdefault("ring0_slow_lane", False) +tmp = p + ".tmp" +with open(tmp, "w") as f: + json.dump(d, f, indent=2, sort_keys=True) + f.write("\n") +os.chmod(tmp, 0o644) +os.replace(tmp, p) +PY + if [[ -n "$RESOLVED_OP_LINE" ]]; then + printf '%s namespaces="felhom-op-v1" %s\n' "$RESOLVED_OP_ID" "$RESOLVED_OP_LINE" > /etc/felhom/operator-signers.tmp + chmod 0644 /etc/felhom/operator-signers.tmp && mv /etc/felhom/operator-signers.tmp /etc/felhom/operator-signers + log_success " wrote /etc/felhom/os-trust.json + operator-signers ($RESOLVED_OP_ID) — the root-owned slow-lane anchors" + else + log_warn " no operational operator key resolved — signed Docker steps stay refused on this box" + fi +} + #------------------------------------------------------------------------------- # STEP 7 — golden: ensure a restorable golden archive (local else Gitea-fetched + verified) #-------------------------------------------------------------------------------