[Unit] Description=Fleet SSH host-certificate acquisition (TPM-authenticated, fleet SSH CA) Documentation=https://git.bitasylum.org/audiobuzz/diskless-workstation/docs/ssh-host-certificate-design.md # Needs the network up (dials step-ca) and sshd's per-boot host key to exist. # sshd-keygen generates the host key before sshd starts, so ordering After sshd # guarantees the key is present; sshd itself serves the bare key until we install # the cert and reload it. All soft ordering (After, not Requires) — this unit is # FAIL-OPEN: if the CA is unreachable or the host isn't enrolled yet, it exits # non-zero WITHOUT touching sshd, and the timer retries. After=network-online.target sshd.service Wants=network-online.target # Don't run in the throwaway build VM (no TPM, no enrolment, no network CA). ConditionPathExists=/dev/tpmrm0 [Service] Type=oneshot EnvironmentFile=/etc/ssh-hostcert/ssh-hostcert.env ExecStart=/usr/local/bin/fleet-ssh-hostcert # Runs as root — unlike the DynamicUser agent — because it writes root-owned # files under /etc/ssh and reloads sshd. Kept tightly sandboxed otherwise. The # TPM private key never leaves the chip; root only drives signing through it. User=root # STIG-friendly sandbox. /etc/ssh must be writable (the cert + drop-in); the rest # of the filesystem is read-only. ProtectSystem=strict ReadWritePaths=/etc/ssh ProtectHome=yes PrivateTmp=yes DeviceAllow=/dev/tpmrm0 rw NoNewPrivileges=yes ProtectKernelTunables=yes ProtectKernelModules=yes ProtectKernelLogs=yes ProtectControlGroups=yes ProtectClock=yes ProtectHostname=yes RestrictNamespaces=yes RestrictRealtime=yes RestrictSUIDSGID=yes LockPersonality=yes MemoryDenyWriteExecute=yes SystemCallArchitectures=native SystemCallFilter=@system-service SystemCallFilter=~@privileged @resources # INET egress to step-ca; AF_UNIX for the journal + talking to systemd (reload # sshd); AF_NETLINK for Go's interface/source-address selection. RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX AF_NETLINK UMask=0022 # Triggered by ssh-hostcert.timer (boot + periodic renewal), not enabled directly.