fix(gluetun): wrap rm -f on dockerinstall-owned tempfiles in runFileOp

gluetun_providers.sh writes its working files ($raw, $headers) next to
$output_file, which lives at
  containers_dir/libreportal/frontend/data/apps/generated/gluetun-providers.json
— dockerinstall-owned in rootless. The five rm -f calls on those paths
were unwrapped, so the manager running the script (e.g. from the
task processor) would get Permission denied — same class as the
updateConfigOption sed -i bug that was just fixed.

$tmp comes from mktemp (/tmp), so the rm -f for it stays unwrapped.

Audit context: this was the only remaining raw filesystem op against
container-tree paths in any containers/*/*.sh. The rest of the
container .sh files are clean — every sed -i / chmod / chown / cp / mv
is already routed through runFileOp or runFileWrite, and the
per-app install bodies delegate fs work to high-level helpers
(dockerConfigSetupToContainer, copyResource, dockerComposeSetupFile)
which themselves use the wrappers.

Hooks (<app>_migrate_pre/_post, restoreAppRunHook pre/post) are
present in the framework but unused by any app today — that's by
design (opt-in per-app). If a future app needs federation-key rotation
post-migrate, or a hostname rewrite that the generic URL-rewrite
layer doesn't cover, those slots are ready.

Signed-off-by: librelad <librelad@digitalangels.vip>
This commit is contained in:
librelad 2026-05-27 01:23:55 +01:00
parent cd30d90a81
commit 1e9e65225b

View File

@ -36,18 +36,24 @@ appWebuiRefresh_gluetun() {
-D "$headers" -o "$raw" \
-w '%{http_code}' "$upstream") || http_code=""
# $raw and $headers live next to $output_file (under containers_dir/
# libreportal/frontend/data/, dockerinstall-owned in rootless). The
# manager can't `rm` them directly without a Permission denied — same
# class of bug as the updateConfigOption sed-i issue. runFileOp routes
# the rm through the right user. $tmp is from mktemp (/tmp), so
# `rm -f $tmp` stays unwrapped.
if [[ "$http_code" == "304" ]]; then
rm -f "$raw" "$headers"
runFileOp rm -f "$raw" "$headers"
return 0
fi
if [[ "$http_code" != "200" ]]; then
isNotice "Upstream fetch failed (${http_code:-no response}); keeping existing snapshot."
rm -f "$raw" "$headers"
runFileOp rm -f "$raw" "$headers"
return 0
fi
local new_etag
new_etag=$(awk 'tolower($1)=="etag:"{print $2}' "$headers" | tr -d '\r')
rm -f "$headers"
runFileOp rm -f "$headers"
# servers.json is a top-level object keyed by provider; each provider
# entry has a `servers` array whose items have `vpn` (wireguard|openvpn),
@ -67,11 +73,12 @@ appWebuiRefresh_gluetun() {
| { providers: . }
' "$raw" > "$tmp" 2>/dev/null; then
isNotice "Failed to parse gluetun servers.json; keeping existing provider snapshot."
rm -f "$raw" "$tmp"
runFileOp rm -f "$raw"
rm -f "$tmp"
return 0
fi
rm -f "$raw"
runFileOp rm -f "$raw"
if [ -s "$tmp" ]; then
runFileWrite "$output_file" < "$tmp"; rm -f "$tmp"