fix(rootless): backup/ssh WebUI generators write as the container owner
The backup + ssh generators created their frontend/data dirs via plain/sudo mkdir and wrote files via sudo tee/mv (root-owned), then called createTouch (dockerinstall) which can't re-own a root file — so every write hit 'touch: Permission denied' in rootless and left root-owned data the dockerinstall container/generators can't rewrite. Convert dir creation to runFileOp mkdir and file writes to runFileWrite (both run as the container owner: dockerinstall in rootless, manager in rooted), dropping the temp/mv/createTouch dance. Also make the createFolders chokepoint mode-aware (containers/ paths created via runFileOp) so it mirrors createTouch. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> Signed-off-by: librelad <librelad@digitalangels.vip>
This commit is contained in:
parent
a7d819799f
commit
75dfb3849b
@ -9,6 +9,21 @@ createFolders()
|
|||||||
local folder_name=$(basename "$dir_path")
|
local folder_name=$(basename "$dir_path")
|
||||||
local clean_dir=$(echo "$dir_path" | sed 's#//*#/#g')
|
local clean_dir=$(echo "$dir_path" | sed 's#//*#/#g')
|
||||||
|
|
||||||
|
# Under /docker/containers/<app>/ the owner is the docker install user
|
||||||
|
# (the rootless container + host generators run as it), so create the dir
|
||||||
|
# AS that user via runFileOp — creating it as the right owner avoids a
|
||||||
|
# chown-to-another-user the unprivileged runtime can't do. Mirrors
|
||||||
|
# createTouch; the $user_name hint is advisory for these paths.
|
||||||
|
if [[ "$clean_dir" == "$containers_dir"* || "$clean_dir" == /docker/containers/* ]]; then
|
||||||
|
if [ ! -d "$dir_path" ]; then
|
||||||
|
local result=$(runFileOp mkdir -p "$dir_path")
|
||||||
|
[ "$silent_flag" == "loud" ] && checkSuccess "Creating $folder_name directory"
|
||||||
|
elif [ "$silent_flag" == "loud" ]; then
|
||||||
|
isNotice "$folder_name directory already exists"
|
||||||
|
fi
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
if [ ! -d "$dir_path" ]; then
|
if [ ! -d "$dir_path" ]; then
|
||||||
local result=$(sudo mkdir -p "$dir_path")
|
local result=$(sudo mkdir -p "$dir_path")
|
||||||
if [ "$silent_flag" == "loud" ]; then
|
if [ "$silent_flag" == "loud" ]; then
|
||||||
|
|||||||
@ -4,7 +4,7 @@ webuiGenerateBackupAppStatus()
|
|||||||
{
|
{
|
||||||
local app_name="$1"
|
local app_name="$1"
|
||||||
local output_dir="$containers_dir/libreportal/frontend/data/backup/generated/apps"
|
local output_dir="$containers_dir/libreportal/frontend/data/backup/generated/apps"
|
||||||
mkdir -p "$output_dir"
|
runFileOp mkdir -p "$output_dir"
|
||||||
|
|
||||||
if [[ -z "$app_name" ]]; then
|
if [[ -z "$app_name" ]]; then
|
||||||
if [[ -f "$docker_dir/$db_file" ]]; then
|
if [[ -f "$docker_dir/$db_file" ]]; then
|
||||||
@ -51,7 +51,5 @@ webuiGenerateBackupAppStatus()
|
|||||||
done < <(resticEnabledLocations)
|
done < <(resticEnabledLocations)
|
||||||
content+="]}"
|
content+="]}"
|
||||||
|
|
||||||
echo "$content" | sudo tee "$temp_file" >/dev/null
|
echo "$content" | runFileWrite "$output_file"
|
||||||
sudo mv "$temp_file" "$output_file"
|
|
||||||
createTouch "$output_file" "$docker_install_user" "silent"
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,7 +6,7 @@ webuiGenerateBackupDashboard()
|
|||||||
local output_file="$output_dir/dashboard.json"
|
local output_file="$output_dir/dashboard.json"
|
||||||
local temp_file="${output_file}.tmp.$$"
|
local temp_file="${output_file}.tmp.$$"
|
||||||
|
|
||||||
mkdir -p "$output_dir"
|
runFileOp mkdir -p "$output_dir"
|
||||||
|
|
||||||
local generated_at
|
local generated_at
|
||||||
generated_at=$(date -Iseconds)
|
generated_at=$(date -Iseconds)
|
||||||
@ -79,8 +79,6 @@ webuiGenerateBackupDashboard()
|
|||||||
content+="\"apps\":$apps_json"
|
content+="\"apps\":$apps_json"
|
||||||
content+="}"
|
content+="}"
|
||||||
|
|
||||||
echo "$content" | sudo tee "$temp_file" >/dev/null
|
echo "$content" | runFileWrite "$output_file"
|
||||||
sudo mv "$temp_file" "$output_file"
|
|
||||||
createTouch "$output_file" "$docker_install_user" "silent"
|
|
||||||
isSuccessful "Backup dashboard JSON regenerated"
|
isSuccessful "Backup dashboard JSON regenerated"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,7 +8,7 @@ webuiGenerateBackupEngines()
|
|||||||
{
|
{
|
||||||
local src_dir="$install_scripts_dir/backup/engines"
|
local src_dir="$install_scripts_dir/backup/engines"
|
||||||
local out_dir="$containers_dir/libreportal/frontend/data/backup/generated/engines"
|
local out_dir="$containers_dir/libreportal/frontend/data/backup/generated/engines"
|
||||||
sudo mkdir -p "$out_dir"
|
runFileOp mkdir -p "$out_dir"
|
||||||
|
|
||||||
if [[ ! -d "$src_dir" ]]; then
|
if [[ ! -d "$src_dir" ]]; then
|
||||||
isNotice "No engines directory at $src_dir — skipping engines regen"
|
isNotice "No engines directory at $src_dir — skipping engines regen"
|
||||||
@ -22,8 +22,7 @@ webuiGenerateBackupEngines()
|
|||||||
[[ -f "$f" ]] || continue
|
[[ -f "$f" ]] || continue
|
||||||
local base
|
local base
|
||||||
base=$(basename "$f")
|
base=$(basename "$f")
|
||||||
sudo cp "$f" "$out_dir/$base"
|
runFileOp cp "$f" "$out_dir/$base"
|
||||||
createTouch "$out_dir/$base" "$docker_install_user" "silent"
|
|
||||||
local id="${base%.json}"
|
local id="${base%.json}"
|
||||||
$first || index+=","
|
$first || index+=","
|
||||||
first=false
|
first=false
|
||||||
@ -32,8 +31,7 @@ webuiGenerateBackupEngines()
|
|||||||
index+="]"
|
index+="]"
|
||||||
|
|
||||||
local idx_file="$out_dir/index.json"
|
local idx_file="$out_dir/index.json"
|
||||||
echo "{\"engines\":$index,\"generated_at\":\"$(date -Iseconds)\"}" | sudo tee "$idx_file" >/dev/null
|
echo "{\"engines\":$index,\"generated_at\":\"$(date -Iseconds)\"}" | runFileWrite "$idx_file"
|
||||||
createTouch "$idx_file" "$docker_install_user" "silent"
|
|
||||||
|
|
||||||
isSuccessful "Engines JSON regenerated"
|
isSuccessful "Engines JSON regenerated"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,7 +5,7 @@ webuiGenerateBackupLocations()
|
|||||||
local output_dir="$containers_dir/libreportal/frontend/data/backup/generated"
|
local output_dir="$containers_dir/libreportal/frontend/data/backup/generated"
|
||||||
local output_file="$output_dir/locations.json"
|
local output_file="$output_dir/locations.json"
|
||||||
local temp_file="${output_file}.tmp.$$"
|
local temp_file="${output_file}.tmp.$$"
|
||||||
mkdir -p "$output_dir"
|
runFileOp mkdir -p "$output_dir"
|
||||||
|
|
||||||
if declare -f backupLocationsMigrate >/dev/null 2>&1; then
|
if declare -f backupLocationsMigrate >/dev/null 2>&1; then
|
||||||
backupLocationsMigrate >/dev/null 2>&1 || true
|
backupLocationsMigrate >/dev/null 2>&1 || true
|
||||||
@ -99,8 +99,6 @@ webuiGenerateBackupLocations()
|
|||||||
done < <(resticAllLocationIndices)
|
done < <(resticAllLocationIndices)
|
||||||
content+="]}"
|
content+="]}"
|
||||||
|
|
||||||
echo "$content" | sudo tee "$temp_file" >/dev/null
|
echo "$content" | runFileWrite "$output_file"
|
||||||
sudo mv "$temp_file" "$output_file"
|
|
||||||
createTouch "$output_file" "$docker_install_user" "silent"
|
|
||||||
isSuccessful "Locations JSON regenerated"
|
isSuccessful "Locations JSON regenerated"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,7 +6,7 @@ webuiGenerateBackupPasswords()
|
|||||||
local output_file="$output_dir/passwords.txt"
|
local output_file="$output_dir/passwords.txt"
|
||||||
local temp_file="${output_file}.tmp.$$"
|
local temp_file="${output_file}.tmp.$$"
|
||||||
|
|
||||||
mkdir -p "$output_dir"
|
runFileOp mkdir -p "$output_dir"
|
||||||
|
|
||||||
local generated_at
|
local generated_at
|
||||||
generated_at=$(date -Iseconds)
|
generated_at=$(date -Iseconds)
|
||||||
@ -33,10 +33,6 @@ webuiGenerateBackupPasswords()
|
|||||||
echo "CFG_BACKUP_LOC_${idx}_PASSWORD=${pass}"
|
echo "CFG_BACKUP_LOC_${idx}_PASSWORD=${pass}"
|
||||||
echo ""
|
echo ""
|
||||||
done < <(resticAllLocationIndices)
|
done < <(resticAllLocationIndices)
|
||||||
} > "$temp_file"
|
} | runFileWrite "$output_file"
|
||||||
|
runFileOp chmod 0600 "$output_file"
|
||||||
chmod 0600 "$temp_file"
|
|
||||||
mv "$temp_file" "$output_file"
|
|
||||||
createTouch "$output_file" "${docker_install_user:-${sudo_user_name:-libreportal}}" "silent"
|
|
||||||
chmod 0600 "$output_file"
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,7 +12,7 @@ webuiGenerateBackupSchema()
|
|||||||
{
|
{
|
||||||
local out_dir="$containers_dir/libreportal/frontend/data/backup/generated"
|
local out_dir="$containers_dir/libreportal/frontend/data/backup/generated"
|
||||||
local out_file="$out_dir/schema.json"
|
local out_file="$out_dir/schema.json"
|
||||||
sudo mkdir -p "$out_dir"
|
runFileOp mkdir -p "$out_dir"
|
||||||
|
|
||||||
# "<type>|FIELD,FIELD,..." — order here is the form's render order.
|
# "<type>|FIELD,FIELD,..." — order here is the form's render order.
|
||||||
local rows=(
|
local rows=(
|
||||||
@ -45,7 +45,6 @@ webuiGenerateBackupSchema()
|
|||||||
done
|
done
|
||||||
json+="}}"
|
json+="}}"
|
||||||
|
|
||||||
echo "$json" | sudo tee "$out_file" >/dev/null
|
echo "$json" | runFileWrite "$out_file"
|
||||||
createTouch "$out_file" "$docker_install_user" "silent"
|
|
||||||
isSuccessful "Backup location schema regenerated"
|
isSuccessful "Backup location schema regenerated"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,7 @@ webuiGenerateBackupSnapshots()
|
|||||||
{
|
{
|
||||||
local scope="${1:-all}"
|
local scope="${1:-all}"
|
||||||
local output_dir="$containers_dir/libreportal/frontend/data/backup/generated"
|
local output_dir="$containers_dir/libreportal/frontend/data/backup/generated"
|
||||||
mkdir -p "$output_dir"
|
runFileOp mkdir -p "$output_dir"
|
||||||
|
|
||||||
local indices=()
|
local indices=()
|
||||||
if [[ "$scope" == "all" ]]; then
|
if [[ "$scope" == "all" ]]; then
|
||||||
@ -32,9 +32,7 @@ webuiGenerateBackupSnapshots()
|
|||||||
content+="\"snapshots\":$raw"
|
content+="\"snapshots\":$raw"
|
||||||
content+="}"
|
content+="}"
|
||||||
|
|
||||||
echo "$content" | sudo tee "$temp_file" >/dev/null
|
echo "$content" | runFileWrite "$output_file"
|
||||||
sudo mv "$temp_file" "$output_file"
|
|
||||||
createTouch "$output_file" "$docker_install_user" "silent"
|
|
||||||
done
|
done
|
||||||
|
|
||||||
isSuccessful "Snapshots JSON regenerated (${#indices[@]} location(s))"
|
isSuccessful "Snapshots JSON regenerated (${#indices[@]} location(s))"
|
||||||
|
|||||||
@ -8,7 +8,7 @@ webuiGenerateSshAccess()
|
|||||||
{
|
{
|
||||||
local out_dir="$containers_dir/libreportal/frontend/data/ssh"
|
local out_dir="$containers_dir/libreportal/frontend/data/ssh"
|
||||||
local out_file="$out_dir/access.json"
|
local out_file="$out_dir/access.json"
|
||||||
sudo mkdir -p "$out_dir"
|
runFileOp mkdir -p "$out_dir"
|
||||||
|
|
||||||
local jsonEscape
|
local jsonEscape
|
||||||
jsonEscape() {
|
jsonEscape() {
|
||||||
@ -45,7 +45,6 @@ webuiGenerateSshAccess()
|
|||||||
|
|
||||||
printf '{"generated_at":"%s","user":"%s","password_auth":%s,"keys":%s}\n' \
|
printf '{"generated_at":"%s","user":"%s","password_auth":%s,"keys":%s}\n' \
|
||||||
"$(date -Iseconds)" "$(jsonEscape "$user")" "$pw_auth" "$keys_json" \
|
"$(date -Iseconds)" "$(jsonEscape "$user")" "$pw_auth" "$keys_json" \
|
||||||
| sudo tee "$out_file" >/dev/null
|
| runFileWrite "$out_file"
|
||||||
createTouch "$out_file" "$docker_install_user" "silent"
|
|
||||||
isSuccessful "SSH access snapshot regenerated"
|
isSuccessful "SSH access snapshot regenerated"
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user