# 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