fix(install): ensure ip_tables modules before rootless Docker setup

get.docker.com/rootless aborts when ip_tables/ip6_tables aren't loaded.
Ubuntu 24.04 and 26.04 ship both modules but don't autoload them on a
fresh box, so rootless setup died there — and because the caller captures
its output into $result, the reason never reached the console or the
error report. The install continued, reported success, and printed
credentials for a WebUI that was never running.

initPrerequires now modprobes both modules and persists them to
/etc/modules-load.d/libreportal-rootless.conf for subsequent boots,
failing with an actionable message when the kernel genuinely lacks them
(container/VM kernels without netfilter).

installDockerRootless gets its own guard, since it also runs outside
init.sh via start_docker / rootless_start_setup. It only attempts
modprobe when it can — the de-sudoed manager has no modprobe in the
LP_SYSTEM allowlist, matching how ubuntu.sh handles sudo-apt — and
returns non-zero rather than proceeding into a failure whose message
would be swallowed. Already-loaded modules are a clean no-op, so the
normal post-install re-run path is unaffected.

Uninstall removes the drop-in alongside the sysctl ones, and it's listed
in the footprint summary.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
librelad 2026-08-01 11:08:32 +01:00
parent bdaec0c03b
commit 9b9b2054d3
3 changed files with 76 additions and 0 deletions

51
init.sh
View File

@ -794,11 +794,57 @@ initOS()
isSuccessful "OS Updated"
}
# Rootless Docker needs ip_tables/ip6_tables present before its installer runs.
# Idempotent: safe to re-run, and a no-op once the modules are already live.
# Returns non-zero if the modules genuinely can't be loaded, so the caller can
# stop rather than let rootless setup fail later with a swallowed message.
initIptablesModules()
{
local modules_conf="/etc/modules-load.d/libreportal-rootless.conf"
local mod missing=()
for mod in ip_tables ip6_tables; do
# Already built in (or loaded) — nothing to do for this one.
if lsmod 2>/dev/null | grep -q "^${mod}\b"; then
continue
fi
if ! sudo modprobe "$mod" 2>/dev/null; then
missing+=("$mod")
fi
done
if [[ ${#missing[@]} -gt 0 ]]; then
isError "Could not load required kernel module(s): ${missing[*]}"
isNotice "Rootless Docker cannot be installed without these. On a VPS this usually"
isNotice "means a container/VM kernel without netfilter — check with your provider."
return 1
fi
# Persist across reboots. Written unconditionally so an edited/truncated file
# gets repaired, but only when the content would actually change.
local desired="# LibrePortal: rootless Docker requires the legacy iptables modules
ip_tables
ip6_tables"
if [[ "$(cat "$modules_conf" 2>/dev/null)" != "$desired" ]]; then
echo "$desired" | sudo tee "$modules_conf" >/dev/null
isSuccessful "Persisted iptables modules to $modules_conf"
else
isSuccessful "iptables modules already loaded and persisted."
fi
return 0
}
initPrerequires()
{
isHeader "Installing Prerequired Apps"
# apache2-utils → htpasswd, used by hashPassword for fast local bcrypt.
sudo apt-get install git zip curl sshpass dos2unix dnsutils apt-transport-https ca-certificates software-properties-common uidmap adduser apache2-utils restic -y
# get.docker.com/rootless aborts when the legacy ip_tables modules aren't
# loaded. Ubuntu 24.04/26.04 ship them but don't autoload them on a fresh
# box, so rootless setup dies here with a message that never surfaces (the
# caller captures its output). Load them now and persist for later boots.
initIptablesModules
TARGET_PATH="/usr/local/bin"
CONFIG_FILE="$HOME/.bashrc"
if ! echo "$PATH" | grep -q "$TARGET_PATH"; then
@ -1686,6 +1732,7 @@ runFullUninstall()
printf " %-34s ${DIM}%s${NC}\n" "/etc/sudoers.d/$mgr" "scoped sudo grant"
printf " %-34s ${DIM}%s${NC}\n" "libreportal.service" "systemd task processor"
printf " %-34s ${DIM}%s${NC}\n" "/etc/sysctl.d/99-libreportal*" "rootless sysctl drop-ins"
printf " %-34s ${DIM}%s${NC}\n" "/etc/modules-load.d/libreportal*" "iptables module drop-in"
echo ""
printf " ${BOLD}Containers + binaries${NC}\n"
printf " ${DIM}%s${NC}\n" "all containers + images + the rootless docker daemon"
@ -1748,6 +1795,10 @@ runFullUninstall()
if [[ "$keep_docker" != "true" ]]; then
rm -f /etc/sysctl.d/99-libreportal-hardening.conf /etc/sysctl.d/99-libreportal-rootless.conf
sysctl --system >/dev/null 2>&1
# Drop the modules-load drop-in too. The modules stay loaded until reboot,
# which is fine — we only stop *forcing* them on for a machine that no
# longer runs rootless docker.
rm -f /etc/modules-load.d/libreportal-rootless.conf
fi
rm -f /usr/local/bin/restic /usr/local/bin/kopia /usr/local/bin/ufw-docker
rm -f /root/init.sh

View File

@ -93,6 +93,27 @@ installDockerRootless()
local result; result=$(runSystem loginctl enable-linger $CFG_DOCKER_INSTALL_USER)
checkSuccess "Adding automatic start (linger)"
# get.docker.com/rootless aborts outright when the legacy ip_tables
# modules aren't loaded — and its output is captured into $result below,
# so the real reason never reaches the console or the error report; the
# install just carries on and "succeeds" with no daemon. Ubuntu 24.04 and
# 26.04 ship these modules but don't autoload them on a fresh box.
# init.sh loads + persists them at install time; a later re-run by the
# de-sudoed manager can't modprobe (not in the LP_SYSTEM allowlist), so
# only attempt it when we actually can, and stop loudly when we can't.
local mod missing=()
for mod in ip_tables ip6_tables; do
lsmod 2>/dev/null | grep -q "^${mod}\b" && continue
sudo -n modprobe "$mod" 2>/dev/null || missing+=("$mod")
done
if [[ ${#missing[@]} -gt 0 ]]; then
isError "Rootless Docker needs kernel module(s) that aren't loaded: ${missing[*]}"
isNotice "Load now: sudo modprobe ${missing[*]}"
isNotice "Persist it: add one module per line to $modules_load"
isNotice "Stopping before rootless setup — it would fail with its error swallowed."
return 1
fi
# Install rootless Docker and enable the user service, but do NOT
# start it here. The rootless network override (override.conf,
# written further down) and the daemon-reload that picks it up

View File

@ -45,6 +45,10 @@ swap_file=/swapfile
# the old non-standard /etc/sysctl/ path, so settings written elsewhere never
# persist across reboot.
sysctl="/etc/sysctl.d/99-libreportal-rootless.conf"
# Rootless Docker's installer aborts outright when the legacy ip_tables modules
# aren't loaded. Ubuntu 24.04/26.04 don't autoload them on a fresh box, so we
# both modprobe them now and persist them here for subsequent boots.
modules_load="/etc/modules-load.d/libreportal-rootless.conf"
docker_log_file=libreportal.log
backup_log_file=backup.log
db_file=database.db