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
gates / gates (push) Successful in 28s

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
2026-10-04 16:16:24 +02:00
parent 175ecfcdd2
commit b1b5c7e7e1
2 changed files with 100 additions and 1 deletions
+14
View File
@@ -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
+86 -1
View File
@@ -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:-<none>}) 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)
#-------------------------------------------------------------------------------