From f1ce5e38228fcb895567bcf845b78f1443f201c8 Mon Sep 17 00:00:00 2001 From: librelad Date: Sat, 23 May 2026 23:51:01 +0100 Subject: [PATCH] harden(desudo): fix docker-cmd helper bug; convert jitsi/authelia/reset_git FIX: dockerCommandRun rooted path is 'sudo $command' (unquoted word-split), so 'docker ps --format "{{.Names}}"' was passing the format with LITERAL quotes -> docker emitted '' and the downstream grep never matched (broken in rooted too). Switch all docker invocations to runFileOp, which preserves args via "$@" in both modes (and runs as dockerinstall against the rootless socket). Fixed monitoring.sh, dashy, tags_processor_network_mode. Convert: jitsimeet (rm/wget/unzip/mv/sed/tee/gen-passwords on /docker -> runFileOp/runFileWrite), authelia (config sed/mkdir/chmod/chown/secrets tee -> runFileOp/runFileWrite; docker exec -> runFileOp docker, preserving --password), reset_git (cp->/root runSystem, install-dir chown runInstallOp; kept sudo -u manager). check_update/update_git_check need no change (all sudo -u manager git, already least-privilege). Co-Authored-By: Claude Opus 4.7 Signed-off-by: librelad --- containers/authelia/authelia.sh | 22 ++++++------ containers/jitsimeet/jitsimeet.sh | 36 +++++++++---------- .../app/containers/dashy/dashy_update_conf.sh | 4 +-- .../processors/tags_processor_network_mode.sh | 4 +-- scripts/network/monitoring/monitoring.sh | 8 ++--- scripts/update/git/reset_git.sh | 4 +-- 6 files changed, 39 insertions(+), 39 deletions(-) diff --git a/containers/authelia/authelia.sh b/containers/authelia/authelia.sh index 8c3ca5c..8ab7146 100755 --- a/containers/authelia/authelia.sh +++ b/containers/authelia/authelia.sh @@ -67,21 +67,21 @@ installAuthelia() checkSuccess "Copying users_database.yml to $containers_dir$app_name/config" local authelia_config_file="$containers_dir$app_name/config/configuration.yml" - sudo sed -i "s|AUTHELIA_THEME_PLACEHOLDER|$CFG_AUTHELIA_THEME|g" "$authelia_config_file" - sudo sed -i "s|AUTHELIA_DOMAIN_PLACEHOLDER|$domain_full|g" "$authelia_config_file" - sudo sed -i "s|AUTHELIA_HOST_PLACEHOLDER|$host_setup|g" "$authelia_config_file" + runFileOp sed -i "s|AUTHELIA_THEME_PLACEHOLDER|$CFG_AUTHELIA_THEME|g" "$authelia_config_file" + runFileOp sed -i "s|AUTHELIA_DOMAIN_PLACEHOLDER|$domain_full|g" "$authelia_config_file" + runFileOp sed -i "s|AUTHELIA_HOST_PLACEHOLDER|$host_setup|g" "$authelia_config_file" checkSuccess "Substituting Authelia configuration values (theme=$CFG_AUTHELIA_THEME domain=$domain_full host=$host_setup)" local authelia_secrets_dir="$containers_dir$app_name/secrets" - sudo mkdir -p "$authelia_secrets_dir" + runFileOp mkdir -p "$authelia_secrets_dir" for secret_name in JWT_SECRET SESSION_SECRET STORAGE_ENCRYPTION_KEY; do local secret_file="$authelia_secrets_dir/$secret_name" if [[ ! -s "$secret_file" ]]; then - openssl rand -hex 64 | sudo tee "$secret_file" >/dev/null - sudo chmod 600 "$secret_file" + openssl rand -hex 64 | runFileWrite "$secret_file" + runFileOp chmod 600 "$secret_file" fi done - sudo chown -R "$docker_install_user":"$docker_install_user" "$authelia_secrets_dir" + runFileOp chown -R "$docker_install_user":"$docker_install_user" "$authelia_secrets_dir" checkSuccess "Generated Authelia secrets at $authelia_secrets_dir" # Enable Authelia's telemetry/metrics endpoint only when @@ -113,7 +113,7 @@ installAuthelia() local authelia_users_file="$containers_dir$app_name/config/users_database.yml" local authelia_attempts=0 while ((authelia_attempts < 30)); do - if sudo docker exec authelia-service authelia --version >/dev/null 2>&1; then + if runFileOp docker exec authelia-service authelia --version >/dev/null 2>&1; then break fi sleep 2 @@ -124,12 +124,12 @@ installAuthelia() isNotice "Authelia container did not become responsive in time — admin left at default (admin / authelia)." else local authelia_hash - authelia_hash=$(sudo docker exec authelia-service authelia crypto hash generate argon2 --password "$authelia_admin_pass" 2>/dev/null \ + authelia_hash=$(runFileOp docker exec authelia-service authelia crypto hash generate argon2 --password "$authelia_admin_pass" 2>/dev/null \ | grep -oE '\$argon2[^[:space:]]+') if [[ -z "$authelia_hash" ]]; then isNotice "Could not generate Authelia password hash — admin left at default (admin / authelia)." else - sudo tee "$authelia_users_file" >/dev/null < $latest_tag.txt) checkSuccess "Create logging txt file" # Download files and unzip - local result=$(sudo wget -O $containers_dir$app_name/$latest_tag.zip $git_url/archive/refs/tags/$latest_tag.zip) + local result=$(runFileOp wget -O $containers_dir$app_name/$latest_tag.zip $git_url/archive/refs/tags/$latest_tag.zip) checkSuccess "Downloading tagged zip file from GitHub" - local result=$(sudo unzip -o $containers_dir$app_name/$latest_tag.zip -d $containers_dir$app_name) + local result=$(runFileOp unzip -o $containers_dir$app_name/$latest_tag.zip -d $containers_dir$app_name) checkSuccess "Unzip downloaded file" - local result=$(sudo mv $containers_dir$app_name/docker-jitsi-meet-$latest_tag/* $containers_dir$app_name) + local result=$(runFileOp mv $containers_dir$app_name/docker-jitsi-meet-$latest_tag/* $containers_dir$app_name) checkSuccess "Moving all files from zip file to install directory" - local result=$(sudo rm -rf $containers_dir$app_name/$latest_tag.zip && sudo rm -rf $containers_dir$app_name/$latest_tag/) + local result=$(runFileOp rm -rf $containers_dir$app_name/$latest_tag.zip && runFileOp rm -rf $containers_dir$app_name/$latest_tag/) checkSuccess "Removing downloaded zip file as no longer needed" ((menu_number++)) @@ -93,39 +93,39 @@ installJitsimeet() dockerSetupEnvFile; # Updating custom .env values - local result=$(sudo sed -i "s|CONFIG=~/.jitsi-meet-cfg|CONFIG=$containers_dir$app_name/.jitsi-meet-cfg|g" $containers_dir$app_name/.env) + local result=$(runFileOp sed -i "s|CONFIG=~/.jitsi-meet-cfg|CONFIG=$containers_dir$app_name/.jitsi-meet-cfg|g" $containers_dir$app_name/.env) checkSuccess "Updating .env file with new install path" - local result=$(sudo sed -i "s|#PUBLIC_URL=https://meet.example.com|PUBLIC_URL=https://$host_setup|g" $containers_dir$app_name/.env) + local result=$(runFileOp sed -i "s|#PUBLIC_URL=https://meet.example.com|PUBLIC_URL=https://$host_setup|g" $containers_dir$app_name/.env) checkSuccess "Updating .env file with Public URL to $host_setup" - local result=$(sudo sed -i "s|HTTP_PORT=8000|HTTP_PORT=$usedport1|g" $containers_dir$app_name/.env) + local result=$(runFileOp sed -i "s|HTTP_PORT=8000|HTTP_PORT=$usedport1|g" $containers_dir$app_name/.env) checkSuccess "Updating .env file with HTTP_PORT to $usedport1" - local result=$(sudo sed -i "s|HTTPS_PORT=8443|HTTPS_PORT=$usedport2|g" $containers_dir$app_name/.env) + local result=$(runFileOp sed -i "s|HTTPS_PORT=8443|HTTPS_PORT=$usedport2|g" $containers_dir$app_name/.env) checkSuccess "Updating .env file with HTTP_PORT to $usedport2" - #local result=$(echo "ENABLE_HTTP_REDIRECT=1" | sudo tee -a "$containers_dir$app_name/.env") + #local result=$(echo "ENABLE_HTTP_REDIRECT=1" | runFileWrite -a "$containers_dir$app_name/.env") #checkSuccess "Updating .env file with option : ENABLE_HTTP_REDIRECT" # Values are missing from the .env by default for some reason # https://github.com/jitsi/docker-jitsi-meet/commit/12051700562d9826f9e024ad649c4dd9b88f94de#diff-b335630551682c19a781afebcf4d07bf978fb1f8ac04c6bf87428ed5106870f5 - local result=$(echo "XMPP_DOMAIN=meet.jitsi" | sudo tee -a "$containers_dir$app_name/.env") + local result=$(echo "XMPP_DOMAIN=meet.jitsi" | runFileWrite -a "$containers_dir$app_name/.env") checkSuccess "Updating .env file with missing option : XMPP_DOMAIN" - local result=$(echo "XMPP_SERVER=xmpp.meet.jitsi" | sudo tee -a "$containers_dir$app_name/.env") + local result=$(echo "XMPP_SERVER=xmpp.meet.jitsi" | runFileWrite -a "$containers_dir$app_name/.env") checkSuccess "Updating .env file with missing option : XMPP_SERVER" - local result=$(echo "JVB_PORT=$usedport4" | sudo tee -a "$containers_dir$app_name/.env") + local result=$(echo "JVB_PORT=$usedport4" | runFileWrite -a "$containers_dir$app_name/.env") checkSuccess "Updating .env file with missing option : JVB_PORT" - local result=$(echo "JVB_TCP_MAPPED_PORT=$usedport5" | sudo tee -a "$containers_dir$app_name/.env") + local result=$(echo "JVB_TCP_MAPPED_PORT=$usedport5" | runFileWrite -a "$containers_dir$app_name/.env") checkSuccess "Updating .env file with missing option : JVB_TCP_MAPPED_PORT" - local result=$(echo "JVB_TCP_PORT=$usedport5" | sudo tee -a "$containers_dir$app_name/.env") + local result=$(echo "JVB_TCP_PORT=$usedport5" | runFileWrite -a "$containers_dir$app_name/.env") checkSuccess "Updating .env file with missing option : JVB_TCP_PORT" - local result=$(cd "$containers_dir$app_name" && sudo ./gen-passwords.sh) + local result=$(cd "$containers_dir$app_name" && runFileOp ./gen-passwords.sh) checkSuccess "Running Jitsi Meet gen-passwords.sh script" ((menu_number++)) @@ -148,10 +148,10 @@ installJitsimeet() #local result=$(sudo sed -i "s|443|$usedport2|g" $containers_dir$app_nameweb/default) #checkSuccess "Updating Docker NGINX default site port 443 to $usedport2" - local result=$(sudo sed -i "s|80|$usedport1|g" $containers_dir$app_name/web/rootfs/defaults/default) + local result=$(runFileOp sed -i "s|80|$usedport1|g" $containers_dir$app_name/web/rootfs/defaults/default) checkSuccess "Updating NGINX default site port 80 to $usedport1" - local result=$(sudo sed -i "s|443|$usedport2|g" $containers_dir$app_name/web/rootfs/defaults/default) + local result=$(runFileOp sed -i "s|443|$usedport2|g" $containers_dir$app_name/web/rootfs/defaults/default) checkSuccess "Updating NGINX default site port 443 to $usedport2" #dockerCommandRun "docker cp '$containers_dir$app_name' '$app_name:/etc/nginx/sites-available/default'" diff --git a/scripts/app/containers/dashy/dashy_update_conf.sh b/scripts/app/containers/dashy/dashy_update_conf.sh index 9db475e..5f26dee 100755 --- a/scripts/app/containers/dashy/dashy_update_conf.sh +++ b/scripts/app/containers/dashy/dashy_update_conf.sh @@ -17,7 +17,7 @@ appDashyUpdateConf() # row, so dockerCheckAppInstalled would say not_installed and bail. # Look at the actual docker container instead — if the container # exists or the install dir is present, generate the conf. - if ! dockerCommandRun "docker ps -a --format '{{.Names}}'" "sudo" 2>/dev/null | grep -qE '^(dashy|dashy-service)$' \ + if ! runFileOp docker ps -a --format '{{.Names}}' 2>/dev/null | grep -qE '^(dashy|dashy-service)$' \ && [[ ! -d "${containers_dir}dashy" ]]; then return 0 fi @@ -278,7 +278,7 @@ EOF local updated_md5=$(runFileOp md5sum "$conf_file" 2>/dev/null | awk '{print $1}') if [[ "$original_md5" != "$updated_md5" ]]; then isNotice "Dashy config changed — restarting container..." - dockerCommandRun "docker restart dashy-service" "sudo" >/dev/null 2>&1 || dockerCommandRun "docker restart dashy" "sudo" >/dev/null 2>&1 || true + runFileOp docker restart dashy-service >/dev/null 2>&1 || runFileOp docker restart dashy >/dev/null 2>&1 || true local _cat_label="categories" [[ ${#_cat_order[@]} -eq 1 ]] && _cat_label="category" isSuccessful "Restarted dashy (${#_cat_order[@]} ${_cat_label}, ${_total_items} URL(s))." diff --git a/scripts/config/tags/processors/tags_processor_network_mode.sh b/scripts/config/tags/processors/tags_processor_network_mode.sh index 66893fb..179787b 100644 --- a/scripts/config/tags/processors/tags_processor_network_mode.sh +++ b/scripts/config/tags/processors/tags_processor_network_mode.sh @@ -104,9 +104,9 @@ tagsProcessorGluetunForwardedPorts() if ! runFileOp cmp -s "$tmp" "$gluetun_compose"; then runFileOp mv "$tmp" "$gluetun_compose" - if dockerCommandRun "docker ps --format '{{.Names}}'" "sudo" 2>/dev/null | grep -q '^gluetun-service$'; then + if runFileOp docker ps --format '{{.Names}}' 2>/dev/null | grep -q '^gluetun-service$'; then isNotice "Gluetun forwarded ports changed; recreating gluetun-service to apply." - (cd "${containers_dir}gluetun" && dockerCommandRun "docker compose up -d --force-recreate gluetun-service" "sudo" >/dev/null 2>&1) || true + (cd "${containers_dir}gluetun" && runFileOp docker compose up -d --force-recreate gluetun-service >/dev/null 2>&1) || true # Recreating gluetun gives it a new container ID, which orphans # every `network_mode: container:gluetun-service` reference. Re- # attach all routed apps so they share the new netns instead of diff --git a/scripts/network/monitoring/monitoring.sh b/scripts/network/monitoring/monitoring.sh index 7a5d8f0..3049317 100644 --- a/scripts/network/monitoring/monitoring.sh +++ b/scripts/network/monitoring/monitoring.sh @@ -139,8 +139,8 @@ monitoringRefreshPrometheus() runFileOp chmod -R a+rX "$scrape_dir" 2>/dev/null - if dockerCommandRun "docker ps --format '{{.Names}}'" "sudo" 2>/dev/null | grep -q '^prometheus-service$'; then - local result=$(dockerCommandRun "docker kill --signal=HUP prometheus-service" "sudo" 2>&1) + if runFileOp docker ps --format '{{.Names}}' 2>/dev/null | grep -q '^prometheus-service$'; then + local result=$(runFileOp docker kill --signal=HUP prometheus-service 2>&1) checkSuccess "Reloaded Prometheus ($count monitored app(s))" else isNotice "Prometheus container not running — scrape.d updated, applied on next start ($count app(s))." @@ -207,8 +207,8 @@ EOF runFileOp chmod -R a+rX "$prov" 2>/dev/null - if dockerCommandRun "docker ps --format '{{.Names}}'" "sudo" 2>/dev/null | grep -q '^grafana-service$'; then - local result=$(dockerCommandRun "docker restart grafana-service" "sudo" 2>&1) + if runFileOp docker ps --format '{{.Names}}' 2>/dev/null | grep -q '^grafana-service$'; then + local result=$(runFileOp docker restart grafana-service 2>&1) checkSuccess "Restarted Grafana ($count dashboard(s) provisioned)" else isNotice "Grafana container not running — provisioning updated, applied on next start ($count dashboard(s))." diff --git a/scripts/update/git/reset_git.sh b/scripts/update/git/reset_git.sh index c49808a..75a59d8 100755 --- a/scripts/update/git/reset_git.sh +++ b/scripts/update/git/reset_git.sh @@ -11,8 +11,8 @@ gitReset() gitMiscUpdate() { - sudo cp -f $script_dir/init.sh /root/ - sudo chown -R $sudo_user_name:$sudo_user_name "$script_dir" + runSystem cp -f $script_dir/init.sh /root/ + runInstallOp chown -R $sudo_user_name:$sudo_user_name "$script_dir" } local result=$(sudo -u $sudo_user_name rm -rf $script_dir)