fix(install): broad sudo during install, tighten to scoped only after

The install hands the heavy setup to the manager (completeInitMessage:
sudo -u libreportal 'libreportal run install') — creating the
docker-install user, rootless setup, apt, sysctl — which needs broad root.
initUsers was installing the SCOPED sudoers up front, so that handoff died
with 'sudo: a password is required' on useradd. Fix: initUsers installs a
temporary NOPASSWD: ALL for the install phase; completeInitMessage calls
the new initScopedSudoers to tighten to the runtime allowlist only after
the install succeeds (on failure, broad sudo is left so the manual
'libreportal run install' retry works). This restores the documented
'kill NOPASSWD:ALL AFTER the runtime is set up' ordering.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
This commit is contained in:
librelad 2026-05-24 20:01:52 +01:00
parent d7aae3f47e
commit c63cb4a2a7

58
init.sh
View File

@ -698,16 +698,42 @@ initUsers()
sudo systemctl restart docker sudo systemctl restart docker
isSuccessful "User $sudo_user_name created successfully." isSuccessful "User $sudo_user_name created successfully."
fi fi
# Manager-user sudo: a SCOPED, validated /etc/sudoers.d drop-in (NOT # Install-phase sudo: the heavy install runs AS this user (see the handoff in
# NOPASSWD: ALL, and never appended to /etc/sudoers — a malformed main file # completeInitMessage) and needs BROAD root — useradd for the docker-install
# locks out sudo entirely). Under Model A the runtime runs AS this user and # user, rootless setup, apt, sysctl, etc. So grant a temporary validated
# reaches root ONLY via: the unprivileged docker-install user (data plane, # NOPASSWD: ALL drop-in now (never appended to /etc/sudoers — a malformed main
# confined by rootless), the root-owned /usr/local/lib/libreportal/ helpers # file locks out sudo entirely); completeInitMessage calls initScopedSudoers
# (each a fixed, self-validated op the manager can't modify), and a fixed set # to tighten it to the scoped RUNTIME allowlist once the install succeeds.
# 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 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"
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 install-phase sudo for $sudo_user_name (tightened after install)."
else
isError "Refusing to install an invalid sudoers drop-in for $sudo_user_name."
fi
rm -f "$sudoers_tmp"
initRootHelpers
}
# Tighten the manager's sudo from the install-phase NOPASSWD: ALL down to the
# scoped RUNTIME allowlist. Called AFTER the (manager-run) install phase, which
# needs the broad root this deliberately withholds. The runtime then reaches root
# ONLY via: the unprivileged docker-install user (data plane, rootless-confined),
# the root-owned /usr/local/lib/libreportal/ helpers (each a fixed, self-validated
# op the manager can't modify), and a fixed system-binary set. Excluded:
# bash/su + tee/cp/chmod/chown/sed/mv/rm/install (each root-equivalent). Also
# clears legacy broad grants (a NOPASSWD: ALL in the main /etc/sudoers, sudo-group
# membership). See FOOTPRINT.md.
initScopedSudoers()
{
local sudoers_dropin="/etc/sudoers.d/${sudo_user_name}"
local install_user
install_user=$(grep -h '^CFG_DOCKER_INSTALL_USER=' "$configs_dir/general/general_docker_install" 2>/dev/null | head -1 | cut -d= -f2 | awk '{print $1}')
install_user="${install_user:-${CFG_DOCKER_INSTALL_USER:-dockerinstall}}"
local sudoers_tmp local sudoers_tmp
sudoers_tmp=$(mktemp) sudoers_tmp=$(mktemp)
cat > "$sudoers_tmp" <<EOF cat > "$sudoers_tmp" <<EOF
@ -727,9 +753,7 @@ ${sudo_user_name} ALL=(root) NOPASSWD: LP_HELPERS, LP_SYSTEM
EOF EOF
if sudo visudo -cf "$sudoers_tmp" >/dev/null 2>&1; then if sudo visudo -cf "$sudoers_tmp" >/dev/null 2>&1; then
sudo install -m 0440 -o root -g root "$sudoers_tmp" "$sudoers_dropin" sudo install -m 0440 -o root -g root "$sudoers_tmp" "$sudoers_dropin"
# Defensive cleanup of legacy broad grants from older installs: a # Clear legacy broad grants from older installs.
# 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 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) local main_tmp; main_tmp=$(mktemp)
sudo grep -vE "^${sudo_user_name}[[:space:]]+ALL=\(ALL\)[[:space:]]+NOPASSWD:[[:space:]]+ALL$" /etc/sudoers > "$main_tmp" sudo grep -vE "^${sudo_user_name}[[:space:]]+ALL=\(ALL\)[[:space:]]+NOPASSWD:[[:space:]]+ALL$" /etc/sudoers > "$main_tmp"
@ -737,13 +761,11 @@ EOF
rm -f "$main_tmp" rm -f "$main_tmp"
fi fi
sudo gpasswd -d "$sudo_user_name" sudo >/dev/null 2>&1 || true 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})." isSuccessful "Tightened $sudo_user_name sudo to the scoped runtime allowlist."
else else
isError "Refusing to install an invalid sudoers drop-in for $sudo_user_name." isError "Invalid scoped sudoers — left install-phase sudo in place for $sudo_user_name."
fi fi
rm -f "$sudoers_tmp" rm -f "$sudoers_tmp"
initRootHelpers
} }
# Install the root-owned privilege helpers. Under Model A the runtime runs AS the # Install the root-owned privilege helpers. Under Model A the runtime runs AS the
@ -1260,7 +1282,9 @@ completeInitMessage()
# Switch to libreportal user and run the install command # Switch to libreportal user and run the install command
if sudo -u "$sudo_user_name" LIBREPORTAL_SKIP_LOGO=1 bash -c "libreportal run install"; then if sudo -u "$sudo_user_name" LIBREPORTAL_SKIP_LOGO=1 bash -c "libreportal run install"; then
: # Install done — tighten the manager's broad install-phase sudo down to
# the scoped runtime allowlist.
initScopedSudoers
else else
echo "" echo ""
echo "⚠️ LibrePortal installation encountered issues." echo "⚠️ LibrePortal installation encountered issues."