From 56d2f8105c5b485dd621d5d7049ed1d03105f2a1 Mon Sep 17 00:00:00 2001 From: librelad Date: Wed, 27 May 2026 01:16:02 +0100 Subject: [PATCH] fix(config): updateConfigOption uses the right de-sudo helper for the file's tree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Symptom (reported on adguard install): sed: couldn't open temporary file /libreportal-containers/adguard/sedZaGX2H: Permission denied ✗ Error Updated CFG_ADGUARD_BACKUP to true ! Notice Non-interactive mode: aborting on error. Root cause: updateConfigOption ran a raw `sed -i` regardless of which tree the config file sits in. Works fine for /libreportal-system/configs/* (manager-owned) but breaks on /libreportal-containers//.config (dockerinstall-owned in rootless mode) — sed -i writes its temp file next to the target, inheriting the directory's perms, and the manager can't write inside dockerinstall dirs. EVERY app installer that mutates a CFG__* value (the autogenerated random password, BACKUP toggle, PORT override, etc.) goes through this function, so this was a latent ticking bomb across all containers. Fix: pick the helper based on path — - under $containers_dir → runFileOp (escalates to dockerinstall in rootless, runs as manager in rooted) - otherwise → runInstallOp (always manager) Read paths (grep / source) stay unwrapped — both dirs are world-readable; only the write needs the privilege swap. Net: no more 'Permission denied' on app installs; the de-sudo pattern is now respected end-to-end for CFG writes. Signed-off-by: librelad --- scripts/config/core/config_update_option.sh | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/scripts/config/core/config_update_option.sh b/scripts/config/core/config_update_option.sh index 1fa71da..4a21973 100755 --- a/scripts/config/core/config_update_option.sh +++ b/scripts/config/core/config_update_option.sh @@ -28,7 +28,22 @@ updateConfigOption() local escaped_value escaped_value=$(printf '%s' "$config_value" | sed -e 's/[\\&"]/\\&/g') - # Check if config option exists in the file + # Pick the right de-sudo helper based on which tree the config lives in. + # Manager (libreportal) owns /libreportal-system/configs/* and runs the + # CLI; dockerinstall owns /libreportal-containers//* in rootless mode. + # Without this, `sed -i` on a containers-owned .config fails with + # sed: couldn't open temporary file /libreportal-containers//sedXXXX: Permission denied + # (sed -i writes its temp next to the target, so it inherits the dir's + # write perms — and the manager can't write inside dockerinstall dirs). + # runFileOp routes the write through the right user. + local _write_op="runInstallOp" + if [[ -n "${containers_dir:-}" && "$config_file" == "${containers_dir%/}/"* ]]; then + _write_op="runFileOp" + fi + + # Check if config option exists in the file. grep + read can use the + # current user — both dirs are world-readable; only the write needs + # escalation. if grep -q "^$config_option=" "$config_file"; then # Extract the comment part first (everything after the first #) local original_line=$(grep "^$config_option=" "$config_file") @@ -36,9 +51,9 @@ updateConfigOption() # Replace the value, preserving comment if it existed. Always quoted. if [[ -n "$comment_part" ]]; then - sed -i "s${DELIM}^${config_option}=.*${DELIM}${config_option}=\"${escaped_value}\" ${comment_part}${DELIM}" "$config_file" + $_write_op sed -i "s${DELIM}^${config_option}=.*${DELIM}${config_option}=\"${escaped_value}\" ${comment_part}${DELIM}" "$config_file" else - sed -i "s${DELIM}^${config_option}=.*${DELIM}${config_option}=\"${escaped_value}\"${DELIM}" "$config_file" + $_write_op sed -i "s${DELIM}^${config_option}=.*${DELIM}${config_option}=\"${escaped_value}\"${DELIM}" "$config_file" fi checkSuccess "Updated $config_option to $config_value" source "$config_file"