refactor(de-sudo): docker calls via runFileOp/dockerCommandRun, drop sudo

Container-plane docker now routes through the mode-aware helpers instead of
sudo: simple calls (exec/ps/run/build/images/inspect/port/logs across ~15
app/check scripts) -> runFileOp docker (rootless socket as the install user;
rooted via the docker group). The cd && docker compose paths drop the sudo on
the rooted branch (the rootless branch already used dockerCommandRunInstallUser
-- byte-identical now, manager-ready later); gluetun, which had no rootless
branch, now uses dockerCommandRun so force-recreate works in both modes.

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 16:29:22 +01:00
parent 778e6d739d
commit 3ecf213cab
20 changed files with 25 additions and 27 deletions

View File

@ -8,7 +8,7 @@ _bookstackArtisan() {
local container="bookstack" local container="bookstack"
local php_body="$1" local php_body="$1"
shift shift
sudo docker exec -i -w /app/www "$@" "$container" php artisan tinker --execute="$php_body" 2>&1 runFileOp docker exec -i -w /app/www "$@" "$container" php artisan tinker --execute="$php_body" 2>&1
} }
authAdapter_bookstack_setPassword() { authAdapter_bookstack_setPassword() {

View File

@ -1,7 +1,7 @@
#!/bin/bash #!/bin/bash
_focalboardSqlite() { _focalboardSqlite() {
sudo docker exec -i focalboard-service sqlite3 /data/focalboard.db "$1" 2>&1 runFileOp docker exec -i focalboard-service sqlite3 /data/focalboard.db "$1" 2>&1
} }
_focalboardBcrypt() { _focalboardBcrypt() {

View File

@ -1,7 +1,7 @@
#!/bin/bash #!/bin/bash
_giteaCmd() { _giteaCmd() {
sudo docker exec -u git gitea-service gitea admin "$@" 2>&1 runFileOp docker exec -u git gitea-service gitea admin "$@" 2>&1
} }
authAdapter_gitea_setPassword() { authAdapter_gitea_setPassword() {

View File

@ -27,7 +27,7 @@ appGluetunRecreateRouted()
installed_apps=$(runInstallOp sqlite3 "$docker_dir/$db_file" \ installed_apps=$(runInstallOp sqlite3 "$docker_dir/$db_file" \
"SELECT name FROM apps WHERE status = 1 ORDER BY name;" 2>/dev/null) "SELECT name FROM apps WHERE status = 1 ORDER BY name;" 2>/dev/null)
if ! sudo docker ps --format '{{.Names}}' 2>/dev/null | grep -q '^gluetun-service$'; then if ! runFileOp docker ps --format '{{.Names}}' 2>/dev/null | grep -q '^gluetun-service$'; then
# Nothing to re-attach against; gluetun isn't running. # Nothing to re-attach against; gluetun isn't running.
return 0 return 0
fi fi
@ -48,15 +48,13 @@ appGluetunRecreateRouted()
# Skip apps with no running/created container — recreate would # Skip apps with no running/created container — recreate would
# do nothing useful and we'd just emit noise. # do nothing useful and we'd just emit noise.
if ! sudo docker ps -a --format '{{.Names}}' 2>/dev/null \ if ! runFileOp docker ps -a --format '{{.Names}}' 2>/dev/null \
| grep -q "^${app}-service$"; then | grep -q "^${app}-service$"; then
continue continue
fi fi
isNotice "Re-attaching ${app} to gluetun's namespace (force-recreate)..." isNotice "Re-attaching ${app} to gluetun's namespace (force-recreate)..."
(cd "${containers_dir}${app}" \ dockerCommandRun "cd ${containers_dir}${app} && docker compose up -d --force-recreate ${app}-service" >/dev/null 2>&1 || true
&& sudo docker compose up -d --force-recreate "${app}-service" \
>/dev/null 2>&1) || true
((recreated++)) ((recreated++))
done <<< "$installed_apps" done <<< "$installed_apps"

View File

@ -1,7 +1,7 @@
#!/bin/bash #!/bin/bash
_invidiousPsql() { _invidiousPsql() {
sudo docker exec -i invidious-db psql -U kemal -d invidious -At -c "$1" 2>&1 runFileOp docker exec -i invidious-db psql -U kemal -d invidious -At -c "$1" 2>&1
} }
_invidiousBcrypt() { _invidiousBcrypt() {

View File

@ -1,7 +1,7 @@
#!/bin/bash #!/bin/bash
_mattermostMmctl() { _mattermostMmctl() {
sudo docker exec -i mattermost mmctl --local "$@" 2>&1 runFileOp docker exec -i mattermost mmctl --local "$@" 2>&1
} }
authAdapter_mattermost_setPassword() { authAdapter_mattermost_setPassword() {

View File

@ -11,11 +11,11 @@ appNextcloudAddTrustedDomain() {
[[ -z "$domain" ]] && { isError "Domain is required."; return 1; } [[ -z "$domain" ]] && { isError "Domain is required."; return 1; }
local current_count local current_count
current_count=$(sudo docker exec -u www-data nextcloud-service php occ config:system:get trusted_domains 2>/dev/null | grep -c '^\s*[0-9]') current_count=$(runFileOp docker exec -u www-data nextcloud-service php occ config:system:get trusted_domains 2>/dev/null | grep -c '^\s*[0-9]')
[[ -z "$current_count" || "$current_count" -lt 1 ]] && current_count=1 [[ -z "$current_count" || "$current_count" -lt 1 ]] && current_count=1
local out local out
out=$(sudo docker exec -u www-data nextcloud-service php occ config:system:set trusted_domains "$current_count" --value="$domain" 2>&1) out=$(runFileOp docker exec -u www-data nextcloud-service php occ config:system:set trusted_domains "$current_count" --value="$domain" 2>&1)
if echo "$out" | grep -qi "system config value\|set to string"; then if echo "$out" | grep -qi "system config value\|set to string"; then
isSuccessful "Nextcloud trusted_domains[$current_count] = $domain" isSuccessful "Nextcloud trusted_domains[$current_count] = $domain"
return 0 return 0

View File

@ -5,12 +5,12 @@
# pipe passwords via the OC_PASS env var (occ supports --password-from-env). # pipe passwords via the OC_PASS env var (occ supports --password-from-env).
_nextcloudOcc() { _nextcloudOcc() {
sudo docker exec -u www-data nextcloud-service php occ "$@" 2>&1 runFileOp docker exec -u www-data nextcloud-service php occ "$@" 2>&1
} }
_nextcloudOccWithPass() { _nextcloudOccWithPass() {
local pass="$1"; shift local pass="$1"; shift
sudo docker exec -e OC_PASS="$pass" -u www-data nextcloud-service php occ "$@" 2>&1 runFileOp docker exec -e OC_PASS="$pass" -u www-data nextcloud-service php occ "$@" 2>&1
} }
authAdapter_nextcloud_setPassword() { authAdapter_nextcloud_setPassword() {

View File

@ -12,7 +12,7 @@ appNextcloudRescanFiles() {
[[ -n "$username" ]] && target="$username" [[ -n "$username" ]] && target="$username"
local out local out
out=$(sudo docker exec -u www-data nextcloud-service php occ files:scan $target 2>&1) out=$(runFileOp docker exec -u www-data nextcloud-service php occ files:scan $target 2>&1)
if echo "$out" | grep -qi "scanned\|files found"; then if echo "$out" | grep -qi "scanned\|files found"; then
isSuccessful "Nextcloud file rescan completed${username:+ for $username}." isSuccessful "Nextcloud file rescan completed${username:+ for $username}."
return 0 return 0

View File

@ -5,5 +5,5 @@
# modal results pane. # modal results pane.
appNextcloudSystemStatus() { appNextcloudSystemStatus() {
sudo docker exec -u www-data nextcloud-service php occ status 2>&1 runFileOp docker exec -u www-data nextcloud-service php occ status 2>&1
} }

View File

@ -9,5 +9,5 @@ appNextcloudTailLogs() {
lines="$(authToolArg "$args" lines)" lines="$(authToolArg "$args" lines)"
[[ -z "$lines" || ! "$lines" =~ ^[0-9]+$ ]] && lines=100 [[ -z "$lines" || ! "$lines" =~ ^[0-9]+$ ]] && lines=100
sudo docker exec -u www-data nextcloud-service tail -n "$lines" /var/www/html/data/nextcloud.log 2>&1 runFileOp docker exec -u www-data nextcloud-service tail -n "$lines" /var/www/html/data/nextcloud.log 2>&1
} }

View File

@ -12,7 +12,7 @@ appNextcloudToggleMaintenance() {
[[ "$enable" == "true" ]] && target=--on [[ "$enable" == "true" ]] && target=--on
local out local out
out=$(sudo docker exec -u www-data nextcloud-service php occ maintenance:mode $target 2>&1) out=$(runFileOp docker exec -u www-data nextcloud-service php occ maintenance:mode $target 2>&1)
if echo "$out" | grep -qi "maintenance mode (enabled\|already enabled\|disabled\|already disabled)"; then if echo "$out" | grep -qi "maintenance mode (enabled\|already enabled\|disabled\|already disabled)"; then
isSuccessful "Nextcloud maintenance mode $target." isSuccessful "Nextcloud maintenance mode $target."
return 0 return 0

View File

@ -19,7 +19,7 @@ hashPassword()
if command -v docker &>/dev/null; then if command -v docker &>/dev/null; then
local bcrypt_hash local bcrypt_hash
bcrypt_hash=$(sudo docker run --rm ghcr.io/wg-easy/wg-easy wgpw "$password" 2>/dev/null | tr -d '\n') bcrypt_hash=$(runFileOp docker run --rm ghcr.io/wg-easy/wg-easy wgpw "$password" 2>/dev/null | tr -d '\n')
if [[ -n "$bcrypt_hash" ]]; then if [[ -n "$bcrypt_hash" ]]; then
bcrypt_hash=$(echo "$bcrypt_hash" | awk -F= '{print $NF}') bcrypt_hash=$(echo "$bcrypt_hash" | awk -F= '{print $NF}')
local escaped_hash local escaped_hash

View File

@ -44,7 +44,7 @@ dockerComposeDown()
local result=$(dockerCommandRunInstallUser "cd $containers_dir$app_name && docker compose $setup_compose down" >/dev/null 2>&1) local result=$(dockerCommandRunInstallUser "cd $containers_dir$app_name && docker compose $setup_compose down" >/dev/null 2>&1)
checkSuccess "Shutting down container for $app_name" checkSuccess "Shutting down container for $app_name"
elif [[ $mode == "rooted" ]]; then elif [[ $mode == "rooted" ]]; then
local result=$(cd "$containers_dir$app_name" && sudo docker compose $setup_compose down >/dev/null 2>&1) local result=$(cd "$containers_dir$app_name" && docker compose $setup_compose down >/dev/null 2>&1)
checkSuccess "Shutting down container for $app_name" checkSuccess "Shutting down container for $app_name"
else else
isNotice "Unknown Docker install type '$mode' — cannot shut down $app_name." isNotice "Unknown Docker install type '$mode' — cannot shut down $app_name."

View File

@ -119,7 +119,7 @@ dockerComposeUp()
checkSuccess "Started container for $app_name" checkSuccess "Started container for $app_name"
elif [[ $CFG_DOCKER_INSTALL_TYPE == "rooted" ]]; then elif [[ $CFG_DOCKER_INSTALL_TYPE == "rooted" ]]; then
isNotice "Starting container for $app_name, this may take a while..." isNotice "Starting container for $app_name, this may take a while..."
local result=$(cd "$containers_dir$app_name" && sudo COMPOSE_PROGRESS=plain docker compose $setup_compose up $_compose_quiet $_compose_build_flag -d) local result=$(cd "$containers_dir$app_name" && COMPOSE_PROGRESS=plain docker compose $setup_compose up $_compose_quiet $_compose_build_flag -d)
checkSuccess "Started container for $app_name" checkSuccess "Started container for $app_name"
fi fi
# Used for the CLI dockertype switcher. # Used for the CLI dockertype switcher.
@ -128,7 +128,7 @@ dockerComposeUp()
local result=$(dockerCommandRunInstallUser "cd $containers_dir$app_name && docker compose $setup_compose down") local result=$(dockerCommandRunInstallUser "cd $containers_dir$app_name && docker compose $setup_compose down")
checkSuccess "Shutting down container for $app_name" checkSuccess "Shutting down container for $app_name"
elif [[ $type == "rooted" ]]; then elif [[ $type == "rooted" ]]; then
local result=$(cd "$containers_dir$app_name" && sudo docker compose $setup_compose down) local result=$(cd "$containers_dir$app_name" && docker compose $setup_compose down)
checkSuccess "Shutting down container for $app_name" checkSuccess "Shutting down container for $app_name"
fi fi
fi fi

View File

@ -18,7 +18,7 @@ dockerComposeDownRemove()
fi fi
elif [[ $CFG_DOCKER_INSTALL_TYPE == "rooted" ]]; then elif [[ $CFG_DOCKER_INSTALL_TYPE == "rooted" ]]; then
if [[ -d "$containers_dir$app_name" ]]; then if [[ -d "$containers_dir$app_name" ]]; then
local result=$(cd $containers_dir$app_name && sudo docker compose down -v --remove-orphans) local result=$(cd $containers_dir$app_name && docker compose down -v --remove-orphans)
isNotice "Shutting down & Removing all $app_name container data" isNotice "Shutting down & Removing all $app_name container data"
dockerRemoveApp $app_name; dockerRemoveApp $app_name;
else else

View File

@ -19,7 +19,7 @@ dockerRemoveAppImages()
if [[ $CFG_DOCKER_INSTALL_TYPE == "rootless" ]]; then if [[ $CFG_DOCKER_INSTALL_TYPE == "rootless" ]]; then
compose_images=$(dockerCommandRunInstallUser "cd $compose_dir && docker compose config --images" 2>/dev/null) compose_images=$(dockerCommandRunInstallUser "cd $compose_dir && docker compose config --images" 2>/dev/null)
elif [[ $CFG_DOCKER_INSTALL_TYPE == "rooted" ]]; then elif [[ $CFG_DOCKER_INSTALL_TYPE == "rooted" ]]; then
compose_images=$(cd "$compose_dir" && sudo docker compose config --images 2>/dev/null) compose_images=$(cd "$compose_dir" && docker compose config --images 2>/dev/null)
fi fi
fi fi

View File

@ -9,7 +9,7 @@ dockerCheckIsRunningForUser()
local docker_command='docker ps 2>&1' local docker_command='docker ps 2>&1'
local result=$(dockerCommandRunInstallUser "$docker_command") local result=$(dockerCommandRunInstallUser "$docker_command")
elif [[ $type == "rooted" ]]; then elif [[ $type == "rooted" ]]; then
local docker_command='sudo docker ps 2>&1' local docker_command='docker ps 2>&1'
local result=$(eval "$docker_command") local result=$(eval "$docker_command")
else else
echo "Invalid user type specified." echo "Invalid user type specified."

View File

@ -22,7 +22,7 @@ crowdsecToggleLibrePortalLogMounts() {
*) isError "crowdsecToggleLibrePortalLogMounts: bad mode '$mode'"; return 1 ;; *) isError "crowdsecToggleLibrePortalLogMounts: bad mode '$mode'"; return 1 ;;
esac esac
if sudo docker ps --format '{{.Names}}' 2>/dev/null | grep -q '^libreportal-service$'; then if runFileOp docker ps --format '{{.Names}}' 2>/dev/null | grep -q '^libreportal-service$'; then
isNotice "Recreating libreportal so log mount toggle takes effect..." isNotice "Recreating libreportal so log mount toggle takes effect..."
( cd /docker/containers/libreportal && sudo -u libreportal docker compose up -d >/dev/null 2>&1 ) || true ( cd /docker/containers/libreportal && sudo -u libreportal docker compose up -d >/dev/null 2>&1 ) || true
fi fi

View File

@ -12,6 +12,6 @@ installLibrePortalImageWebUI()
reconcileWebuiDirOwnership reconcileWebuiDirOwnership
isNotice "Building libreportal-service image, this may take a while..." isNotice "Building libreportal-service image, this may take a while..."
local result=$(sudo docker build -t libreportal-service -f $containers_dir/libreportal/Dockerfile $containers_dir/libreportal >/dev/null 2>&1) local result=$(runFileOp docker build -t libreportal-service -f $containers_dir/libreportal/Dockerfile $containers_dir/libreportal >/dev/null 2>&1)
checkSuccess "Built LibrePortal WebUI Docker image" checkSuccess "Built LibrePortal WebUI Docker image"
} }