# SysManage Agent Sudoers Configuration for openSUSE Leap / Tumbleweed / SLES
# This file grants the sysmanage-agent user necessary privileges for system management
# File location: /etc/sudoers.d/sysmanage-agent
# Permissions: 0440 (enforced by package installer)

# Allow non-interactive sudo.
# (``Defaults:sysmanage-agent !requiretty`` was removed -- sudo 1.9.x
# dropped the ``requiretty`` flag entirely; non-interactive sudo is
# the default behavior now.)

# Package Management (Zypper -- the native openSUSE/SLES package manager).
#
# Bare-binary grant: the repository_mirroring_engine's setup_install
# plan runs ``zypper --non-interactive install …`` -- the
# ``--non-interactive`` option sits BEFORE the ``install`` subcommand,
# so a positional rule like ``/usr/bin/zypper install *`` (which
# requires ``install`` to be the first argument) never matches and
# sudo denies the command.  Granting the bare binary matches what the
# engine's docstring assumes and what apt-get/dnf/pkg do here too.
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/bin/zypper *

# Package Management (DNF/YUM) -- available as alternatives on Tumbleweed.
# Bare-binary grant for the same positional-matching reason.
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/bin/dnf *
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/bin/yum *
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/bin/rpm -i *
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/bin/rpm -U *
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/bin/rpm -e *

# Firmware updates via fwupd (apply_fwupd_updates).
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/bin/fwupdmgr update *

# Snap / Flatpak -- supported on openSUSE when installed.
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/bin/snap refresh *
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/bin/flatpak update -y *

# Repository Management
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/bin/dnf config-manager *
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/bin/yum-config-manager *
# sudo 1.9.x rejects wildcards inside command arguments (only
# end-of-argument wildcards are accepted, and not inside path
# components).  Bare commands; the agent's own code constrains paths.
sysmanage-agent ALL=(ALL) NOPASSWD: /bin/sed, /usr/bin/sed
sysmanage-agent ALL=(ALL) NOPASSWD: /bin/rm, /usr/bin/rm

# Generic file deployment via ``sudo install`` (used by repository
# mirror apply, agent config push, and any deploy_files step).
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/bin/install

# Repository mirroring tools (Pro+ repository_mirroring_engine).
# rsync is the snapshot + restore workhorse -- without it,
# ``snapshot`` plans fail at the live-tree -> .snapshots/<id>/ copy
# with sudo's "user not allowed to run" error.
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/bin/createrepo_c, /usr/sbin/createrepo
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/bin/trickle
# Memory containment for mirror syncs (repository_mirroring_engine).
# zypper download / createrepo can parse the full upstream index into
# RAM and OOM a small mirror host, taking the sysmanage-agent down with
# it.  The engine wraps the sync in a transient systemd scope
# (``systemd-run --scope -p MemoryMax=…``) so a runaway sync is
# contained to a cgroup OOM instead of a machine-wide one.  Bare-binary
# grant; the engine decides the argv and skips the wrap at run time
# when this grant is absent, so older hosts simply run uncapped.
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/bin/systemd-run, /bin/systemd-run
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/bin/rsync

# Air-gap collector ISO build (Pro+ airgap_collector_engine): build_iso_plan
# bundles the staging tree with ``sudo xorriso`` and checksums it with
# ``sudo sha256sum``.  Bare-binary grants; without them the run dies at
# BUILDING_ISO with a sudo password prompt.
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/bin/xorriso
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/bin/sha256sum
# Optical-disc burn (build_iso_plan with a burn_device set): ``sudo
# growisofs`` writes the ISO, ``sudo eject`` ejects the disc.  The
# CVE/compliance snapshot fetch uses ``sudo curl``.  Bare-binary grants.
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/bin/growisofs
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/bin/eject
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/bin/curl
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/bin/mkdir, /bin/mkdir
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/bin/find
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/bin/chmod, /bin/chmod
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/bin/chown, /bin/chown

# Air-gap repository ingestion (Pro+ airgap_repository_engine).  The
# repository ingestion orchestrator loop-mounts the transferred ISO
# read-only, rsyncs the verified payload into the local mirror tree,
# then unmounts.  Bare-binary grants, same philosophy as rsync above.
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/bin/mount, /bin/mount
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/bin/umount, /bin/umount

# Systemd Service Management
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/bin/systemctl start *, /bin/systemctl start *
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/bin/systemctl stop *, /bin/systemctl stop *
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart *, /bin/systemctl restart *
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/bin/systemctl enable *, /bin/systemctl enable *
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/bin/systemctl disable *, /bin/systemctl disable *
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/bin/systemctl is-active *, /bin/systemctl is-active *
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/bin/systemctl daemon-reload, /bin/systemctl daemon-reload
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/bin/systemctl start *
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/bin/systemctl stop *
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart *
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/bin/systemctl enable *
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/bin/systemctl disable *
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/bin/systemctl is-active *
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/bin/systemctl daemon-reload

# Firewall Management (firewalld)
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/bin/firewall-cmd *
sysmanage-agent ALL=(ALL) NOPASSWD: /bin/firewall-cmd *

# SELinux Management
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/sbin/setenforce *
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/sbin/getenforce
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/sbin/semanage *
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/sbin/setsebool *
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/sbin/getsebool *

# System Power Management
sysmanage-agent ALL=(ALL) NOPASSWD: /sbin/shutdown -r *
sysmanage-agent ALL=(ALL) NOPASSWD: /sbin/shutdown -h *
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/sbin/shutdown -r *
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/sbin/shutdown -h *
sysmanage-agent ALL=(ALL) NOPASSWD: /sbin/reboot
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/sbin/reboot
sysmanage-agent ALL=(ALL) NOPASSWD: /sbin/poweroff
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/sbin/poweroff

# Certificate Management
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/bin/update-ca-trust extract
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/bin/update-ca-trust

# Content Lifecycle Management (Phase 16/19) -- content-view publish, promote
# and serving.  These three were MISSING, and their absence made the whole
# feature non-functional on a real agent: every publish/promote/materialize
# plan opens with ``sudo test -d`` and died on it, so content-view publish had
# never once succeeded outside a harness that strips sudo.  Found by running
# the flow end-to-end against a live agent on 2026-08-25.
#
#   test  -- existence guard at the head of every plan.  A pure predicate: it
#            cannot write, execute or modify anything.  ``find`` is already
#            granted and discloses strictly more.
#   ln    -- the environment promotion ITSELF (``ln -sfn v<N> <env>``), the
#            atomic flip that repoints an environment at a version.
#   nginx -- validating the generated serving config before reload.
#
# NOTE there is deliberately NO ``sh`` here.  ``sudo sh -c`` is arbitrary root
# execution and would defeat the bare-binary policy this file exists to
# enforce; the apt index generation was rewritten to run unprivileged and be
# placed with ``install`` instead.

sysmanage-agent ALL=(ALL) NOPASSWD: /usr/bin/test, /bin/test
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/bin/ln, /bin/ln
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/sbin/nginx, /usr/bin/nginx

# Hardware health reads (Phase 23.1) -- READ-ONLY, exact argument forms.
# An unprivileged agent cannot read drive health: the NVMe log needs the
# admin ioctl, smartctl cannot open a disk, lvs cannot take LVM's lock, and
# btrfs device stats / ipmitool need root -- every drive read "not judged"
# (2026-10-16).  The agent runs these, and only these, through sudo -n.  Both
# /usr/sbin and /usr/bin forms: distributions differ (and Fedora merged them).
# smartctl: one exact form per disk node (sda-sdz, nvme0-nvme15).  sudoers
# cannot wildcard part of an argument (sudo-rs rejects /dev/*), and a
# whole-argument * would also allow -s / -t, which change drive settings.
Cmnd_Alias SYSMANAGE_SMART_READ = /usr/sbin/smartctl -j -H -A /dev/sda, \
    /usr/sbin/smartctl -j -H -A /dev/sdb, \
    /usr/sbin/smartctl -j -H -A /dev/sdc, \
    /usr/sbin/smartctl -j -H -A /dev/sdd, \
    /usr/sbin/smartctl -j -H -A /dev/sde, \
    /usr/sbin/smartctl -j -H -A /dev/sdf, \
    /usr/sbin/smartctl -j -H -A /dev/sdg, \
    /usr/sbin/smartctl -j -H -A /dev/sdh, \
    /usr/sbin/smartctl -j -H -A /dev/sdi, \
    /usr/sbin/smartctl -j -H -A /dev/sdj, \
    /usr/sbin/smartctl -j -H -A /dev/sdk, \
    /usr/sbin/smartctl -j -H -A /dev/sdl, \
    /usr/sbin/smartctl -j -H -A /dev/sdm, \
    /usr/sbin/smartctl -j -H -A /dev/sdn, \
    /usr/sbin/smartctl -j -H -A /dev/sdo, \
    /usr/sbin/smartctl -j -H -A /dev/sdp, \
    /usr/sbin/smartctl -j -H -A /dev/sdq, \
    /usr/sbin/smartctl -j -H -A /dev/sdr, \
    /usr/sbin/smartctl -j -H -A /dev/sds, \
    /usr/sbin/smartctl -j -H -A /dev/sdt, \
    /usr/sbin/smartctl -j -H -A /dev/sdu, \
    /usr/sbin/smartctl -j -H -A /dev/sdv, \
    /usr/sbin/smartctl -j -H -A /dev/sdw, \
    /usr/sbin/smartctl -j -H -A /dev/sdx, \
    /usr/sbin/smartctl -j -H -A /dev/sdy, \
    /usr/sbin/smartctl -j -H -A /dev/sdz, \
    /usr/sbin/smartctl -j -H -A /dev/nvme0, \
    /usr/sbin/smartctl -j -H -A /dev/nvme1, \
    /usr/sbin/smartctl -j -H -A /dev/nvme2, \
    /usr/sbin/smartctl -j -H -A /dev/nvme3, \
    /usr/sbin/smartctl -j -H -A /dev/nvme4, \
    /usr/sbin/smartctl -j -H -A /dev/nvme5, \
    /usr/sbin/smartctl -j -H -A /dev/nvme6, \
    /usr/sbin/smartctl -j -H -A /dev/nvme7, \
    /usr/sbin/smartctl -j -H -A /dev/nvme8, \
    /usr/sbin/smartctl -j -H -A /dev/nvme9, \
    /usr/sbin/smartctl -j -H -A /dev/nvme10, \
    /usr/sbin/smartctl -j -H -A /dev/nvme11, \
    /usr/sbin/smartctl -j -H -A /dev/nvme12, \
    /usr/sbin/smartctl -j -H -A /dev/nvme13, \
    /usr/sbin/smartctl -j -H -A /dev/nvme14, \
    /usr/sbin/smartctl -j -H -A /dev/nvme15, \
    /usr/bin/smartctl -j -H -A /dev/sda, \
    /usr/bin/smartctl -j -H -A /dev/sdb, \
    /usr/bin/smartctl -j -H -A /dev/sdc, \
    /usr/bin/smartctl -j -H -A /dev/sdd, \
    /usr/bin/smartctl -j -H -A /dev/sde, \
    /usr/bin/smartctl -j -H -A /dev/sdf, \
    /usr/bin/smartctl -j -H -A /dev/sdg, \
    /usr/bin/smartctl -j -H -A /dev/sdh, \
    /usr/bin/smartctl -j -H -A /dev/sdi, \
    /usr/bin/smartctl -j -H -A /dev/sdj, \
    /usr/bin/smartctl -j -H -A /dev/sdk, \
    /usr/bin/smartctl -j -H -A /dev/sdl, \
    /usr/bin/smartctl -j -H -A /dev/sdm, \
    /usr/bin/smartctl -j -H -A /dev/sdn, \
    /usr/bin/smartctl -j -H -A /dev/sdo, \
    /usr/bin/smartctl -j -H -A /dev/sdp, \
    /usr/bin/smartctl -j -H -A /dev/sdq, \
    /usr/bin/smartctl -j -H -A /dev/sdr, \
    /usr/bin/smartctl -j -H -A /dev/sds, \
    /usr/bin/smartctl -j -H -A /dev/sdt, \
    /usr/bin/smartctl -j -H -A /dev/sdu, \
    /usr/bin/smartctl -j -H -A /dev/sdv, \
    /usr/bin/smartctl -j -H -A /dev/sdw, \
    /usr/bin/smartctl -j -H -A /dev/sdx, \
    /usr/bin/smartctl -j -H -A /dev/sdy, \
    /usr/bin/smartctl -j -H -A /dev/sdz, \
    /usr/bin/smartctl -j -H -A /dev/nvme0, \
    /usr/bin/smartctl -j -H -A /dev/nvme1, \
    /usr/bin/smartctl -j -H -A /dev/nvme2, \
    /usr/bin/smartctl -j -H -A /dev/nvme3, \
    /usr/bin/smartctl -j -H -A /dev/nvme4, \
    /usr/bin/smartctl -j -H -A /dev/nvme5, \
    /usr/bin/smartctl -j -H -A /dev/nvme6, \
    /usr/bin/smartctl -j -H -A /dev/nvme7, \
    /usr/bin/smartctl -j -H -A /dev/nvme8, \
    /usr/bin/smartctl -j -H -A /dev/nvme9, \
    /usr/bin/smartctl -j -H -A /dev/nvme10, \
    /usr/bin/smartctl -j -H -A /dev/nvme11, \
    /usr/bin/smartctl -j -H -A /dev/nvme12, \
    /usr/bin/smartctl -j -H -A /dev/nvme13, \
    /usr/bin/smartctl -j -H -A /dev/nvme14, \
    /usr/bin/smartctl -j -H -A /dev/nvme15
sysmanage-agent ALL=(ALL) NOPASSWD: SYSMANAGE_SMART_READ
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/sbin/lvs --reportformat json -o lv_name\,vg_name\,segtype\,lv_health_status\,sync_percent\,raid_mismatch_count\,lv_attr
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/bin/lvs --reportformat json -o lv_name\,vg_name\,segtype\,lv_health_status\,sync_percent\,raid_mismatch_count\,lv_attr
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/sbin/btrfs filesystem show *
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/bin/btrfs filesystem show *
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/sbin/btrfs device stats *
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/bin/btrfs device stats *
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/bin/ipmitool sdr elist
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/bin/ipmitool sel elist
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/bin/ipmitool sel time get

# Antivirus deploy plan (server av_plan_builder): refresh the ClamAV signature
# database once, before clamd first starts -- on these platforms installing
# the package does not start the updater, and clamd will not run without a
# database.  Exactly "freshclam" with NO arguments ("" = none allowed): an
# argument such as --config-file could point it at an attacker's config.
# (2026-10-17: the step was refused, "sudo: I'm sorry sysmanage-agent".)
sysmanage-agent ALL=(ALL) NOPASSWD: /usr/bin/freshclam ""

# Hardware identity (Phase 23.2): the three root-only SMBIOS files under
# /sys/class/dmi/id/ (mode 0400), read with cat -- exact paths, no wildcard,
# so nothing else is readable.  Without these every packaged agent showed
# "No machine identified" (2026-10-17).  Both cat paths: usrmerged and not.
Cmnd_Alias SYSMANAGE_DMI_READ = /usr/bin/cat /sys/class/dmi/id/product_uuid, \
    /usr/bin/cat /sys/class/dmi/id/product_serial, \
    /usr/bin/cat /sys/class/dmi/id/board_serial, \
    /bin/cat /sys/class/dmi/id/product_uuid, \
    /bin/cat /sys/class/dmi/id/product_serial, \
    /bin/cat /sys/class/dmi/id/board_serial
sysmanage-agent ALL=(ALL) NOPASSWD: SYSMANAGE_DMI_READ

# Compliance evidence (server ROADMAP 24.4): the only root-only reads the
# compliance checks need -- the EFFECTIVE sshd settings (sshd -T loads the
# host keys), the loaded audit rules, and two root-only config files.  Exact
# argument forms, no wildcard; both usrmerged and split paths.
Cmnd_Alias SYSMANAGE_COMPLIANCE_READ = /usr/sbin/sshd -T, /sbin/sshd -T, \
    /usr/sbin/auditctl -l, /sbin/auditctl -l, \
    /usr/bin/cat /etc/audit/auditd.conf, /bin/cat /etc/audit/auditd.conf, \
    /usr/bin/cat /etc/aide.conf, /bin/cat /etc/aide.conf
sysmanage-agent ALL=(ALL) NOPASSWD: SYSMANAGE_COMPLIANCE_READ
