feat(uninstall): add 'init.sh uninstall' — full, guarded teardown
A single 'sudo bash init.sh uninstall' that permanently removes the whole LibrePortal footprint, behind a typed 'DELETE LIBREPORTAL' confirmation: - stops + removes the task-processor service - best-effort graceful container removal, then tears down the rootless docker setup + the install user's session (linger/terminate/pkill) - removes the out-of-/docker footprint (/usr/local/lib/libreportal + /usr/local/bin/libreportal, /etc/sudoers.d, the systemd unit, the sysctl drop-ins, restic/kopia/ufw-docker, /root/init.sh) - rm -rf /docker - removes the libreportal + dockerinstall users + subuid/subgid ranges Runs as root (the entrypoint root-check enforces it — and the scoped sudoers can no longer self-remove anyway); self-contained (only init.sh's inline helpers, so it works as it deletes /docker); ordered so containers/ daemon stop before the users are removed. Leaves docker/compose/apt deps and SSH config in place (no lockout). Mirrors FOOTPRINT.md. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> Signed-off-by: librelad <librelad@digitalangels.vip>
This commit is contained in:
parent
65937e8108
commit
93284cdb39
84
init.sh
84
init.sh
@ -176,7 +176,7 @@ fi
|
|||||||
# is EXECUTED directly (install time) — start.sh sources init.sh for its function
|
# is EXECUTED directly (install time) — start.sh sources init.sh for its function
|
||||||
# definitions at runtime (Model A, as the manager), and this block must not run
|
# definitions at runtime (Model A, as the manager), and this block must not run
|
||||||
# then (it would print a spurious "Auto-detected ..." and rewrite the config).
|
# then (it would print a spurious "Auto-detected ..." and rewrite the config).
|
||||||
if [[ "${BASH_SOURCE[0]}" == "$0" && -z "$param7" ]]; then
|
if [[ "${BASH_SOURCE[0]}" == "$0" && "$param1" != "uninstall" && -z "$param7" ]]; then
|
||||||
# A reinstall that doesn't re-pass the git args must not silently
|
# A reinstall that doesn't re-pass the git args must not silently
|
||||||
# downgrade an existing git install to local (that disables the updater
|
# downgrade an existing git install to local (that disables the updater
|
||||||
# and blanks the saved creds). Honor a git URL already saved from a
|
# and blanks the saved creds). Honor a git URL already saved from a
|
||||||
@ -1270,6 +1270,86 @@ completeInitMessage()
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# FULL uninstall: permanently remove everything LibrePortal placed on the host.
|
||||||
|
# Runs as root (the entrypoint root-check below enforces it). Order matters —
|
||||||
|
# containers run AS the docker-install user and the rootless daemon is that user's
|
||||||
|
# systemd --user service, so stop those BEFORE removing the users. Self-contained:
|
||||||
|
# uses only init.sh's inline helpers, so it still works as it deletes /docker.
|
||||||
|
# Keep in sync with FOOTPRINT.md.
|
||||||
|
runFullUninstall()
|
||||||
|
{
|
||||||
|
local mgr="${sudo_user_name:-libreportal}"
|
||||||
|
local iuser
|
||||||
|
iuser=$(grep -h '^CFG_DOCKER_INSTALL_USER=' /docker/configs/general/general_docker_install 2>/dev/null | head -1 | cut -d= -f2 | awk '{print $1}')
|
||||||
|
iuser="${iuser:-dockerinstall}"
|
||||||
|
|
||||||
|
isHeader "LibrePortal — FULL Uninstall"
|
||||||
|
isError "This PERMANENTLY removes EVERYTHING — there is no undo:"
|
||||||
|
echo " - all containers + images + the rootless docker setup"
|
||||||
|
echo " - /docker (ALL app data, configs, database)"
|
||||||
|
echo " - the '$mgr' and '$iuser' users + their home directories"
|
||||||
|
echo " - /usr/local/lib/libreportal/ + the /usr/local/bin/libreportal command"
|
||||||
|
echo " - /etc/sudoers.d/$mgr, the systemd service, the sysctl drop-ins"
|
||||||
|
echo " - the restic / kopia / ufw-docker binaries LibrePortal installed"
|
||||||
|
echo ""
|
||||||
|
isNotice "LEFT IN PLACE: the docker engine, docker-compose, apt-installed deps,"
|
||||||
|
isNotice "and your SSH config (so you can't get locked out)."
|
||||||
|
echo ""
|
||||||
|
isQuestion "Type exactly DELETE LIBREPORTAL to confirm:"
|
||||||
|
local confirm; read -r confirm
|
||||||
|
if [[ "$confirm" != "DELETE LIBREPORTAL" ]]; then
|
||||||
|
isNotice "Aborted — nothing was removed."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
isHeader "Tearing down LibrePortal"
|
||||||
|
|
||||||
|
# 1. Stop + remove the task-processor service.
|
||||||
|
systemctl disable --now libreportal.service >/dev/null 2>&1
|
||||||
|
rm -f /etc/systemd/system/libreportal.service
|
||||||
|
systemctl daemon-reload >/dev/null 2>&1
|
||||||
|
isSuccessful "Stopped + removed the task-processor service"
|
||||||
|
|
||||||
|
# 2. Best-effort graceful container removal, then tear down the rootless
|
||||||
|
# user's session (stops the rootless dockerd + any survivors).
|
||||||
|
local uid; uid=$(id -u "$iuser" 2>/dev/null)
|
||||||
|
if [[ -n "$uid" ]]; then
|
||||||
|
sudo -u "$iuser" env XDG_RUNTIME_DIR="/run/user/$uid" \
|
||||||
|
DOCKER_HOST="unix:///run/user/$uid/docker.sock" \
|
||||||
|
bash -c 'docker ps -aq | xargs -r docker rm -f' >/dev/null 2>&1 || true
|
||||||
|
sudo -u "$iuser" env XDG_RUNTIME_DIR="/run/user/$uid" \
|
||||||
|
dockerd-rootless-setuptool.sh uninstall >/dev/null 2>&1 || true
|
||||||
|
loginctl disable-linger "$iuser" >/dev/null 2>&1 || true
|
||||||
|
loginctl terminate-user "$iuser" >/dev/null 2>&1 || true
|
||||||
|
pkill -9 -u "$iuser" >/dev/null 2>&1 || true
|
||||||
|
isSuccessful "Stopped containers + rootless docker for '$iuser'"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 3. Remove the out-of-/docker footprint (see FOOTPRINT.md).
|
||||||
|
rm -f /usr/local/bin/libreportal
|
||||||
|
rm -rf /usr/local/lib/libreportal
|
||||||
|
rm -f "/etc/sudoers.d/$mgr"
|
||||||
|
rm -f /etc/sysctl.d/99-libreportal-hardening.conf /etc/sysctl.d/99-libreportal-rootless.conf
|
||||||
|
sysctl --system >/dev/null 2>&1
|
||||||
|
rm -f /usr/local/bin/restic /usr/local/bin/kopia /usr/local/bin/ufw-docker
|
||||||
|
rm -f /root/init.sh
|
||||||
|
isSuccessful "Removed the system-integration footprint"
|
||||||
|
|
||||||
|
# 4. Remove all app data.
|
||||||
|
rm -rf /docker
|
||||||
|
isSuccessful "Removed /docker"
|
||||||
|
|
||||||
|
# 5. Remove the LibrePortal users + their subuid/subgid ranges.
|
||||||
|
pkill -9 -u "$mgr" >/dev/null 2>&1 || true
|
||||||
|
userdel -r "$mgr" >/dev/null 2>&1 || true
|
||||||
|
userdel -r "$iuser" >/dev/null 2>&1 || true
|
||||||
|
sed -i "/^${mgr}:/d;/^${iuser}:/d" /etc/subuid /etc/subgid 2>/dev/null || true
|
||||||
|
isSuccessful "Removed users '$mgr' + '$iuser'"
|
||||||
|
|
||||||
|
isHeader "LibrePortal uninstalled"
|
||||||
|
isNotice "Left in place: docker engine, docker-compose, apt deps, SSH config."
|
||||||
|
}
|
||||||
|
|
||||||
# Only run the installer entrypoint (root check + init flow) when init.sh is
|
# Only run the installer entrypoint (root check + init flow) when init.sh is
|
||||||
# EXECUTED directly. When it's SOURCED — start.sh loads init.sh for its function
|
# EXECUTED directly. When it's SOURCED — start.sh loads init.sh for its function
|
||||||
# defs at runtime, and under Model A start.sh runs as the manager, not root — the
|
# defs at runtime, and under Model A start.sh runs as the manager, not root — the
|
||||||
@ -1310,5 +1390,7 @@ else
|
|||||||
initLibrePortalCommand
|
initLibrePortalCommand
|
||||||
initUpdateConfigs
|
initUpdateConfigs
|
||||||
completeInitMessage
|
completeInitMessage
|
||||||
|
elif [[ "$param1" == "uninstall" ]]; then
|
||||||
|
runFullUninstall
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user