# driveripper-ops sudo rules for the homelab dashboards UPS work.
#
# Install (from the repo checkout, as a user with sudo):
#
#   sudo visudo -cf deploy/nut-sudoers            # syntax check FIRST
#   sudo install -m 0440 -o root -g root deploy/nut-sudoers /etc/sudoers.d/dashboards-nut
#   sudo visudo -cf /etc/sudoers.d/dashboards-nut # and check the installed copy
#
# Scope: absolute paths only (`secure_path` is in effect), wildcards only where the command
# is read-only or idempotent, and nothing here grants a shell, `sh -c`, an editor, or a
# client that can send instant commands.
#
# WHY THESE ARE NEEDED
# Reads already work with zero privileges — upsd on driveripper serves `LIST UPS` /
# `LIST VAR` unauthenticated (on port 3051, not 3493). What is missing is a upsd.users
# entry with `instcmds`, which is the only thing standing between the dashboard and its
# battery-test buttons. `tee /etc/ups/*` is already granted, but writing upsd.users blind
# would drop the accounts upsmon authenticates as — upsmon must run as a user with
# `shutdown`, so an overwritten file means no orderly power-down. So: read first.

# ---- already present on driveripper (used during setup) ------------------------------ #
# (root) NOPASSWD: /usr/bin/tee /etc/ups/*                  # write a config file
# (root) NOPASSWD: /usr/bin/cp /etc/ups/* /home/ops/*       # copy one out (root-owned)
# (root) NOPASSWD: /usr/bin/systemctl start|restart|reload|enable *
# (root) NOPASSWD: /usr/bin/udevadm *                       # USB device discovery
# (root) NOPASSWD: /usr/bin/firewall-cmd *                  # only if upsd must serve the LAN
# (root) NOPASSWD: /usr/bin/dnf install *                   # nut, nut-client

# ---- read: what is actually configured, before anything is rewritten ----------------- #
Cmnd_Alias NUT_READ = \
    /usr/bin/cat /etc/ups/*, \
    /usr/bin/journalctl -u nut*, \
    /usr/bin/systemctl status nut*, \
    /usr/bin/upsdrvctl status, \
    /usr/bin/upsc *, \
    /usr/sbin/ss -ltnp, \
    /usr/bin/lsusb, \
    /usr/sbin/getenforce, \
    /usr/bin/ausearch -m avc *, /usr/sbin/sealert -l *

# ---- write: add/replace a scoped user, then reload ----------------------------------- #
Cmnd_Alias NUT_WRITE = \
    /usr/bin/tee /etc/ups/upsd.users, \
    /usr/bin/tee /etc/ups/upsd.conf, \
    /usr/bin/systemctl restart nut-server, \
    /usr/bin/systemctl reload nut-server

# ---- deliberately NOT granted -------------------------------------------------------- #
#   /usr/bin/upscmd, /usr/bin/upsrw  — the dashboard's gated POST /api/ups/command is the
#                                      ONLY command path in this project, by design
#   systemctl stop *                 — stopping nut-server blinds the UPS view
#   tee /etc/ups/ups.conf, upsmon.conf — driver instances and shutdown policy are the
#                                      operator's, not the dashboard's
#   sh, bash, vim, tee to anything else      — no escape hatches
