Files
local-ci-cd-system/deploy/sudoers.d/ci-web
T
Simone e56eae95c7 feat(web): service management panel over a sudo allow-list (stage D)
Adds operations/services.py: a fixed ManagedService allow-list (act-runner, act-runner-rust, vmware-host, ci-web) with find_service/service_states/control_service. control_service builds an argument list only (never shell=True), validates the action against ALLOWED_ACTIONS (ValueError->400), refuses stop on restart-only services (ci-web, allow_stop=False) without building a command, and defers ci-web's own restart via 'sudo -n systemd-run --on-active=2 systemctl restart ci-web.service' so the HTTP response returns first.

Route POST /services/{service_id}/{action} in web/routes.py mirrors vms_remove: require_user + require_csrf + two-step confirm gate + one audit entry per stage; unknown id / bad action / disallowed-stop short-circuit to 400 before any subprocess or audit; the vmware-host confirm step warns with the active-VM count. New /services read partial with state-aware Start/Stop/Restart buttons, polled every 15s.

deploy/sudoers.d/ci-web grants ci-runner exact-command NOPASSWD for exactly these nine systemctl lines plus the systemd-run deferral — no wildcards, no bare systemctl, no ci-web stop; setup-web-linux.sh installs it mode 0440 and validates with 'visudo -cf', rolling back on failure. Config gains [web].managed_services override; docs/WEB-UI.md documents the panel + security model.

Gates: 1044 tests pass (46 new security/unit tests), coverage 94.20%, ruff + mypy --strict clean. Not yet deployed: sudoers install + prod-venv reinstall + ci-web restart are a human privilege-boundary step.
2026-07-24 12:11:36 +02:00

43 lines
2.7 KiB
Plaintext

# sudoers.d/ci-web — NOPASSWD grant for the ci-web dashboard's service panel (Stage D).
#
# Installed to /etc/sudoers.d/ci-web, mode 0440, owner root:root, by
# setup-web-linux.sh (which validates it with `visudo -cf` before/after
# installing and rolls back on any failure -- see that script's step and
# docs/WEB-UI.md's "Service management" section).
#
# SECURITY MODEL:
# * Exact-command NOPASSWD only -- every line below is a literal, fully
# qualified command with a literal unit name. No wildcards (`*`), no
# bare `systemctl` entry, no `ALL` in the command position. This is the
# entire reason ci-web (running as ci-runner, an unprivileged service
# account) can safely be granted passwordless sudo for exactly these
# nine command lines and nothing else -- ci-runner can never pass an
# arbitrary unit name or subcommand through sudo, even if the web app
# itself were compromised (defense in depth alongside the operations/
# services.py allow-list, which is the primary gate).
# * ci-web.service deliberately has NO `stop` entry here -- matching
# ManagedService("ci-web", ..., allow_stop=False) in
# operations/services.py. Stopping the service that renders this very
# sudoers-governed panel would lock the operator out with no way to
# start it again from the UI.
# * ci-web.service's "restart" is instead a *deferred* restart via
# `systemd-run --on-active=2 systemctl restart ci-web.service` -- a
# direct `systemctl restart ci-web.service` run from inside ci-web's
# own worker process would kill the HTTP response before it could be
# sent. The systemd-run invocation itself returns almost immediately
# (it only *schedules* the restart 2s out), so the confirming request
# completes normally.
#
# Before installing on a new host, verify the absolute paths below still
# match this host's actual binaries:
# command -v systemctl systemd-run
# and edit both this file and Cmnd_Alias below if either is not under
# /usr/bin (e.g. a distro that ships them under /bin or /usr/local/bin).
# Confirmed /usr/bin/systemctl and /usr/bin/systemd-run on this host.
Cmnd_Alias CI_WEB_SVC = \
/usr/bin/systemctl start act-runner.service, /usr/bin/systemctl stop act-runner.service, /usr/bin/systemctl restart act-runner.service, \
/usr/bin/systemctl start act-runner-rust.service, /usr/bin/systemctl stop act-runner-rust.service, /usr/bin/systemctl restart act-runner-rust.service, \
/usr/bin/systemctl start vmware-host.service, /usr/bin/systemctl stop vmware-host.service, /usr/bin/systemctl restart vmware-host.service, \
/usr/bin/systemd-run --on-active=2 systemctl restart ci-web.service
ci-runner ALL=(root) NOPASSWD: CI_WEB_SVC