[Unit] Description=Fleet on-host control-plane agent (telemetry: TPM-mTLS to fleet-manager) Documentation=https://git.bitasylum.org/audiobuzz/diskless-workstation/docs/fleet-management-design.md # Needs the network up to dial the manager; hostapi is a SOFT dep (facts source) — # After, not Requires, so the agent still streams health if hostapi is down. After=network-online.target fleet-hostapi.service Wants=network-online.target [Service] Type=exec EnvironmentFile=/etc/fleet-agent/agent.env ExecStart=/usr/local/bin/fleet-agent # Always retry — a diskless box has nothing better to do than reconnect. The agent # also self-reconnects the stream; Restart covers a hard crash / TPM transient. Restart=always RestartSec=5 # --- sandboxing (STIG-friendly; looser than hostapi — needs the TPM + egress) --- # Runs as a transient unprivileged user. Groups: # tss — /dev/tpmrm0 (the private key stays in the TPM; the agent only signs # the TLS handshake through it). # chrony — TRAVERSE /var/log/chrony to read tracking.log for the clock-health # probe. That dir is mode 0750 chrony:chrony (others have no access), so # DAC needs group membership to even enter it — a bare 0644 on the log # file isn't enough. (SELinux read of the chronyd_var_log_t label is a # SEPARATE layer, granted by the fleet_agent_chronylog module.) DynamicUser=yes SupplementaryGroups=tss chrony # Expose ONLY the TPM resource-manager node (no PrivateDevices — that would hide it). DeviceAllow=/dev/tpmrm0 rw DevicePolicy=closed CapabilityBoundingSet= AmbientCapabilities= NoNewPrivileges=yes ProtectSystem=strict ProtectHome=yes PrivateTmp=yes ProtectKernelTunables=yes ProtectKernelModules=yes ProtectKernelLogs=yes ProtectControlGroups=yes ProtectClock=yes ProtectHostname=yes ProtectProc=default RestrictNamespaces=yes RestrictRealtime=yes RestrictSUIDSGID=yes LockPersonality=yes MemoryDenyWriteExecute=yes SystemCallArchitectures=native SystemCallFilter=@system-service SystemCallFilter=~@privileged @resources # INET egress to the manager (not loopback-only, unlike hostapi); AF_UNIX for the # journal, AF_NETLINK for Go's source-address/interface selection. RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX AF_NETLINK UMask=0077 [Install] WantedBy=multi-user.target