diff --git a/init.sh b/init.sh index 10249b6..853ff5f 100755 --- a/init.sh +++ b/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" </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