29aeaa6bb4
- configs/felhom-agent.service: canonical non-root unit (User=felhom-agent, sudo model); deliberately NO NoNewPrivileges (breaks sudo) and NO mount-namespacing hardening (breaks the intermediary-mount drive propagation into guests) — documented inline. - scripts/publish-agent.sh: build (optional) + PUT binary to Gitea generic + sha256 + GET round-trip. Pinned version, idempotent (delete-then-PUT). - configs/build-golden.sh: after vzdump, compute sha256 + PUT golden.tar.zst to Gitea generic (version = baked controller version). Opt-in; local auto-discovery stays fallback. - cmd/felhom-agent/main.go: version 0.42.0 -> 0.43.0. - README: process model now canonical (non-root + publish/install). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
51 lines
2.7 KiB
Desktop File
51 lines
2.7 KiB
Desktop File
# felhom-agent.service — canonical systemd unit for the Felhom host agent.
|
|
#
|
|
# Install as /etc/systemd/system/felhom-agent.service (the host-bootstrap script installs this from
|
|
# the Gitea-published binary; previously this unit was hand-made on each host). The agent runs as the
|
|
# NON-root `felhom-agent` service user (the documented production model — README "Process model";
|
|
# `privileged.mode: "sudo"`) and shells the few host-root ops out via `sudo -n` against the fixed-arg
|
|
# allowlist in /etc/sudoers.d/felhom-agent (configs/felhom-agent.sudoers). The User= here and the
|
|
# sudoers `felhom-agent ALL=(root) NOPASSWD: …` MUST name the SAME user.
|
|
#
|
|
# Paths MUST match what the sudoers / host-install script expect:
|
|
# binary /usr/local/bin/felhom-agent
|
|
# config /etc/felhom-agent/agent.json (0600 felhom-agent:felhom-agent — secrets live here)
|
|
# state /var/lib/felhom-agent (nonces, local-api cert/key/tokens, staged units, guests)
|
|
#
|
|
# === DELIBERATELY NO SANDBOXING — read before adding any hardening directive ===
|
|
#
|
|
# 1. NoNewPrivileges is NOT set. It is INCOMPATIBLE with the agent's privilege model: it blocks the
|
|
# setuid `sudo` the agent relies on for EVERY host-root op (mount, format, pct, dnsmasq …), so the
|
|
# agent would silently lose all privileged capability. The narrow surface comes from the sudoers
|
|
# fixed-arg allowlist + the agent's in-process fine validation (internal/storage/validate.go), NOT
|
|
# from NoNewPrivileges. Do not add it.
|
|
#
|
|
# 2. NO mount-namespacing hardening (ProtectHome, ProtectSystem, PrivateTmp, ReadOnlyPaths,
|
|
# ProtectControlGroups, …). Any of these give the unit a PRIVATE mount namespace — and the agent's
|
|
# intermediary-mount drive model does `mount --make-shared /mnt/felhom-drives` + `mount --bind` and
|
|
# relies on those propagating into the RUNNING customer guest. In a private namespace the binds
|
|
# would be invisible to the host/guest and every external-drive enrollment would silently break.
|
|
# The agent MUST share the host mount namespace. The security boundary is the sudoers allowlist.
|
|
|
|
[Unit]
|
|
Description=Felhom host agent (Proxmox host tier; hub control loop + PBS verify + storage watchdog)
|
|
Documentation=https://gitea.dooplex.hu/admin/felhom-agent
|
|
After=network-online.target pve-cluster.service pveproxy.service
|
|
Wants=network-online.target
|
|
|
|
[Service]
|
|
Type=simple
|
|
User=felhom-agent
|
|
Group=felhom-agent
|
|
ExecStart=/usr/local/bin/felhom-agent --config /etc/felhom-agent/agent.json
|
|
Restart=on-failure
|
|
RestartSec=5s
|
|
|
|
# State dir: created 0750 felhom-agent:felhom-agent on start if absent (local-api cert/key/tokens,
|
|
# nonces, staged .mount units, per-guest bootstrap dirs all live here).
|
|
StateDirectory=felhom-agent
|
|
StateDirectoryMode=0750
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|