# sudoers grant that lets the unprivileged `ops` account install the homelab dashboards # host agent on a monitored host. One line, one script path — deploy/nut-sudoers is the # separate NUT grant on driveripper. # # Install (from the staged copy on the host, as root — the command-scoped `ops` sudo on # these hosts does NOT include visudo, which is exactly why this file exists): # # sudo visudo -cf /home/ops/agent-install/deploy/agent-sudoers # syntax check FIRST # sudo install -m 0440 -o root -g root /home/ops/agent-install/deploy/agent-sudoers \ # /etc/sudoers.d/dashboards-agent # sudo visudo -cf /etc/sudoers.d/dashboards-agent # and check the installed copy # # Scope: an absolute path (no wildcards, `secure_path` is in effect), no shell, no editor, # no `tee`, nothing that can amend this file. Running install-agent.sh without root prints # this exact grant with the script's own resolved path, so a checkout at another path tells # you what to add instead of saying "run as root" to someone who cannot. # # WHY THIS IS NEEDED # The agent is read-only by construction: it answers GET /snapshot on a socket, writes # nothing anywhere, and every request except /health needs the bearer token it reads from a # file only root and the agent's own service user can read (0640 root:dashboards-agent — the # agent opens the path itself, so the service user must be in that group). What needs root is # the *install*, not the agent: creating the # dashboards-agent system user, writing /usr/local/lib/dashboards-agent/host_agent.py, # dropping /etc/systemd/system/dashboards-agent.service, storing the token, and running # systemctl daemon-reload + enable --now (disable --now for --uninstall). # Phase 10 runs each host's agent as that systemd service bound to the address the # dashboard dials directly, so the account that deploys over ssh — ops — has to be able to # run the installer, and that is the only reason it is granted anything. # # WHY THE WHOLE SCRIPT PATH AND NOT A FLAG LIST # The flags carry no secrets: --bind/--port choose the socket, --status is read-only, # --uninstall removes exactly what this install put in place, --rotate-token mints a token # the operator is about to be shown anyway. sudoers can only match flags with a wildcard, # and a wildcard list has to be edited in step with every flag the script gains — an edit # whose failure mode is a broken install, not a smaller privilege. The one flag whose # *argument* would be sensitive is --token, and no documented flow uses it: the installer # generates the token, prints it once, and hands the agent --token-file, because argv is # world-readable through ps while the file is not. # ---- the grant ------------------------------------------------------------------ # # The host has no repo checkout, so deploy/ and agent/ are staged to ~/agent-install/ # (phase 10 task 02 does the copy), which keeps this path identical on every host: # # ssh ops@host 'mkdir -p ~/agent-install' # scp -r deploy agent ops@host:~/agent-install/ # # `ops` is the fleet's monitored/deploy account; change only that field if the agent is # installed by a different account. Nothing follows the path, which in sudoers means the # script may be run with any of its own flags — see the block above. ops ALL=(root) NOPASSWD: /home/ops/agent-install/deploy/install-agent.sh # ---- making the grant mean something (one root command, recommended) ------------ # # A sudo grant on a path is only as strong as who can write that path. ops owns # ~/agent-install, so until the staged copy is root-owned this line is a convenience for a # human, not a boundary. After staging, as root: # # sudo chown -R root:root /home/ops/agent-install # # ops can still wipe its own home directory and re-copy (it is ops' home), so this guards # against drift and a stray edit — not against an operator who wants something else. # ---- deliberately NOT granted --------------------------------------------------- # # visudo, vim, nano, sh, bash, tee — no self-amendment of this grant, no shell, and no # writing any file as root # systemctl (anything else) — the installer already runs the few unit operations # it needs as itself; a bare systemctl * could stop # anything on the host # cat /etc/dashboards-agent.token — the token is readable by root and the agent user # only; install-agent.sh prints it once on first # install and --rotate-token mints a new one # useradd, usermod, groupmod — the installer creates its own user and its disk # group membership; a bare usermod could add ops to # any group on the box # upscmd, upsrw — NUT instant commands stay the dashboard's gated # POST /api/ups/command; see deploy/nut-sudoers