feat(configs): D1 Part 1 — self-update host artifacts (guarded wrapper, rollback unit, limits drop-in, sudoers alias)

Design provenance SPIKE-agent-selfupdate-2026-07-05 (SF findings binding):
- felhom-selfupdate-guarded: apply <staged> <sha256> / commit / rollback.
  Ordering [SF-7]: temp-sweep → path confinement (staging dir only, no '..')
  → 64-hex + sha256 RE-verify as root (BEFORE .prev) → same-fs assert →
  .prev snapshot → root-owned staging copy → atomic mv → pending marker →
  reset-failed [SF-4/5] → detached systemd-run restart, verbatim [SF-6].
  rollback is pending-guarded (no pending → exit 0 no-op, [SF-1]) and clears
  pending BEFORE its restart so per-crash OnFailure re-fires no-op. commit
  idempotent, .prev retained (S3d). No env-overridable paths (path-fixedness
  is the security property). shellcheck clean.
- felhom-agent-rollback.service: Type=oneshot OnFailure target; comment block
  documents the systemd-257 per-crash firing reality [SF-1].
- felhom-agent-limits.conf: [Unit]-ONLY drop-in [SF-3] with the spike's tuned
  values verbatim [SF-2]: StartLimitIntervalSec=120, StartLimitBurst=4,
  OnFailure=felhom-agent-rollback.service.
- sudoers: FELHOM_SELFUPDATE alias (coarse apply glob per S4b — the wrapper
  re-verify is the real gate) appended to the grant line. visudo -cf OK.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
This commit is contained in:
2026-07-05 15:20:37 +02:00
parent 72d7f05bf1
commit b7cbded429
4 changed files with 194 additions and 1 deletions
+15 -1
View File
@@ -164,4 +164,18 @@ Cmnd_Alias FELHOM_WG = \
/usr/bin/systemctl disable --now wg-quick@wg-felhom, \
/usr/bin/wg show wg-felhom latest-handshakes
felhom-agent ALL=(root) NOPASSWD: FELHOM_MOUNT, FELHOM_DISK, FELHOM_PROVISION, FELHOM_FORMAT, FELHOM_DNSMASQ, FELHOM_GUESTHOOK, FELHOM_INTERMEDIARY, FELHOM_CONTROLLERSWAP, FELHOM_STALELOCK, FELHOM_NETMOUNT, FELHOM_WG
# Agent self-update (TASK D1, SPIKE-agent-selfupdate-2026-07-05). The agent downloads the
# operator-SIGNED binary (sha256 pinned in the signed op — neither hub nor Gitea compromise can
# substitute it), verifies the sha in-process, then hands off to the guarded wrapper, which
# RE-verifies the sha as root, confines the staged path to /var/lib/felhom-agent/selfupdate/,
# performs the A/B flip (atomic same-fs rename, .prev retained) and schedules a detached restart.
# The apply args are a COARSE glob (spike S4b: sudoers fnmatch makes a [a-f0-9]* sha pattern
# first-char-only anyway) — the wrapper's own sha re-verify + path confinement is the real gate.
# `rollback` is normally run by felhom-agent-rollback.service (root, OnFailure=), not via sudo;
# granting it here keeps the verb probe-able (capability self-check) and operator-invokable.
Cmnd_Alias FELHOM_SELFUPDATE = \
/usr/local/sbin/felhom-selfupdate-guarded apply /var/lib/felhom-agent/selfupdate/* *, \
/usr/local/sbin/felhom-selfupdate-guarded commit, \
/usr/local/sbin/felhom-selfupdate-guarded rollback
felhom-agent ALL=(root) NOPASSWD: FELHOM_MOUNT, FELHOM_DISK, FELHOM_PROVISION, FELHOM_FORMAT, FELHOM_DNSMASQ, FELHOM_GUESTHOOK, FELHOM_INTERMEDIARY, FELHOM_CONTROLLERSWAP, FELHOM_STALELOCK, FELHOM_NETMOUNT, FELHOM_WG, FELHOM_SELFUPDATE