feat(desudo): init.sh installs the SCOPED sudoers by default — kill NOPASSWD:ALL
Replace the NOPASSWD: ALL drop-in with a validated, scoped grant:
- (dockerinstall) NOPASSWD:SETENV: ALL (data plane; rootless-confined)
- (root) NOPASSWD: the 5 root-owned /usr/local/sbin/libreportal-* helpers
+ a fixed system-binary allowlist (systemctl/ufw/ufw-docker/nft/sysctl/
loginctl/service)
No bash/su/tee/cp/chmod/chown/sed/mv/rm/install — none of the
root-equivalent primitives. Also: drop '-G sudo' from the manager useradd
(privileges come from the user-specific drop-in, not group membership),
and defensively remove legacy broad grants on re-run (a NOPASSWD: ALL line
appended to the main /etc/sudoers + sudo-group membership).
Validated live end-to-end as the manager: app lifecycle, webui generate,
ownership reconcile, ssh/dns/socket/svc helpers, task service, data-plane
drop (incl. -E for backups) all denial-free; sudo bash / sudo cat shadow /
arbitrary sudo chown all denied.
Residual (still raw runSystem file-primitives, denied under the scoped
grant until they get helpers / docker-exec rework): owncloud/adguard/
crowdsec app-config edits, wireguard-standalone, restic/kopia binary
self-install. These are opt-in/deferred features.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
This commit is contained in:
parent
12476b507a
commit
c9e6afea79
43
init.sh
43
init.sh
@ -686,24 +686,53 @@ initUsers()
|
||||
if id "$sudo_user_name" &>/dev/null; then
|
||||
isSuccessful "User $sudo_user_name already exists."
|
||||
else
|
||||
sudo useradd -s /bin/bash -d "/home/$sudo_user_name" -m -G sudo "$sudo_user_name" 2>/dev/null
|
||||
# No -G sudo: the manager's privileges come from the scoped /etc/sudoers.d
|
||||
# drop-in below (user-specific), not blanket sudo-group membership.
|
||||
sudo useradd -s /bin/bash -d "/home/$sudo_user_name" -m "$sudo_user_name" 2>/dev/null
|
||||
isNotice "Setting password for $sudo_user_name user."
|
||||
echo "$sudo_user_name:$param2" | sudo chpasswd
|
||||
sudo usermod -aG docker "$sudo_user_name"
|
||||
sudo systemctl restart docker
|
||||
isSuccessful "User $sudo_user_name created successfully."
|
||||
fi
|
||||
# Manager-user sudo lives in a validated /etc/sudoers.d drop-in, not appended
|
||||
# to /etc/sudoers — a malformed line in the main file locks out sudo entirely.
|
||||
# The grant is broad for now; this single drop-in is what gets tightened to a
|
||||
# scoped command allowlist once the runtime no longer needs broad root.
|
||||
# Manager-user sudo: a SCOPED, validated /etc/sudoers.d drop-in (NOT
|
||||
# NOPASSWD: ALL, and never appended to /etc/sudoers — a malformed main file
|
||||
# locks out sudo entirely). Under Model A the runtime runs AS this user and
|
||||
# reaches root ONLY via: the unprivileged docker-install user (data plane,
|
||||
# confined by rootless), the root-owned /usr/local/sbin/libreportal-* helpers
|
||||
# (each a fixed, self-validated op the manager can't modify), and a fixed set
|
||||
# of system binaries. Deliberately excluded: bash/su and tee/cp/chmod/chown/
|
||||
# sed/mv/rm/install (each root-equivalent). See scripts/system/libreportal-*.
|
||||
local sudoers_dropin="/etc/sudoers.d/${sudo_user_name}"
|
||||
local install_user="${CFG_DOCKER_INSTALL_USER:-dockerinstall}"
|
||||
local sudoers_tmp
|
||||
sudoers_tmp=$(mktemp)
|
||||
printf '%s ALL=(ALL) NOPASSWD: ALL\n' "$sudo_user_name" > "$sudoers_tmp"
|
||||
cat > "$sudoers_tmp" <<EOF
|
||||
# Scoped least-privilege grant for the LibrePortal manager. Generated by init.sh.
|
||||
Cmnd_Alias LP_HELPERS = /usr/local/sbin/libreportal-ownership, \\
|
||||
/usr/local/sbin/libreportal-dns, \\
|
||||
/usr/local/sbin/libreportal-ssh-access, \\
|
||||
/usr/local/sbin/libreportal-socket, \\
|
||||
/usr/local/sbin/libreportal-svc
|
||||
Cmnd_Alias LP_SYSTEM = /usr/bin/systemctl, /usr/sbin/ufw, /usr/local/bin/ufw-docker, \\
|
||||
/usr/sbin/nft, /usr/sbin/sysctl, /sbin/sysctl, \\
|
||||
/usr/bin/loginctl, /usr/sbin/service
|
||||
${sudo_user_name} ALL=(${install_user}) NOPASSWD:SETENV: ALL
|
||||
${sudo_user_name} ALL=(root) NOPASSWD: LP_HELPERS, LP_SYSTEM
|
||||
EOF
|
||||
if sudo visudo -cf "$sudoers_tmp" >/dev/null 2>&1; then
|
||||
sudo install -m 0440 -o root -g root "$sudoers_tmp" "$sudoers_dropin"
|
||||
isSuccessful "Configured passwordless sudo for $sudo_user_name (/etc/sudoers.d/${sudo_user_name})."
|
||||
# Defensive cleanup of legacy broad grants from older installs: a
|
||||
# NOPASSWD: ALL line appended to the main /etc/sudoers, plus sudo-group
|
||||
# membership (the scoped drop-in is user-specific; the group isn't needed).
|
||||
if sudo grep -qE "^${sudo_user_name}[[:space:]]+ALL=\(ALL\)[[:space:]]+NOPASSWD:[[:space:]]+ALL" /etc/sudoers 2>/dev/null; then
|
||||
local main_tmp; main_tmp=$(mktemp)
|
||||
sudo grep -vE "^${sudo_user_name}[[:space:]]+ALL=\(ALL\)[[:space:]]+NOPASSWD:[[:space:]]+ALL$" /etc/sudoers > "$main_tmp"
|
||||
sudo visudo -cf "$main_tmp" >/dev/null 2>&1 && sudo cp "$main_tmp" /etc/sudoers
|
||||
rm -f "$main_tmp"
|
||||
fi
|
||||
sudo gpasswd -d "$sudo_user_name" sudo >/dev/null 2>&1 || true
|
||||
isSuccessful "Configured scoped passwordless sudo for $sudo_user_name (/etc/sudoers.d/${sudo_user_name})."
|
||||
else
|
||||
isError "Refusing to install an invalid sudoers drop-in for $sudo_user_name."
|
||||
fi
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user