fix(rootless): slirp4netns default, manager-vs-container helper split, sysctl path

Reinstall test on Debian 12 surfaced three rootless-only breakages (rooted
was byte-identical/fine):

1. pasta blocked by Debian's passt AppArmor profile (DENIED ptrace read ->
   can't open container netns -> rootless dockerd never starts). Default
   CFG_ROOTLESS_NET back to slirp4netns (reliable); pasta stays selectable
   for hosts that relax the profile.
2. de-sudo mis-assigned helpers by owner. /docker management layer (apps DB
   chowned to libreportal by install_sqlite, /docker/logs) is MANAGER-owned,
   not dockerinstall. Add runInstallWrite; move apps-DB sqlite3 -> runInstallOp
   and /docker/logs appends -> runInstallWrite. Revert ownership-SETUP scripts
   (libreportal_folders, app_folder) to runSystem — they must run as root to
   establish ownership during install. Container files (/docker/containers/<app>)
   stay runFileOp.
3. kernel hardening sysctls written to /etc/sysctl/99-custom.conf, which
   'sysctl --system' does not read -> never applied. Write them to
   /etc/sysctl.d/99-libreportal-hardening.conf instead.

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 00:12:06 +01:00
parent 4b17efd1b3
commit 68110d199c
19 changed files with 95 additions and 83 deletions

View File

@ -2,4 +2,4 @@
# Rootless Networking - Network stack and behaviour for rootless Docker **ADVANCED**
# ================================================================================
CFG_ROOTLESS_NET=pasta # Rootless Network Driver - Network stack for rootless Docker; pasta (default): faster and preserves the real client source IP; slirp4netns: legacy fallback if pasta misbehaves. The matching rootlesskit port driver is selected automatically. **ADVANCED** [pasta:Pasta (recommended)|slirp4netns:slirp4netns (fallback)]
CFG_ROOTLESS_NET=slirp4netns # Rootless Network Driver - Network stack for rootless Docker; slirp4netns (default): reliable everywhere; pasta: faster and preserves the real client source IP BUT on Debian the shipped passt AppArmor profile denies the ptrace it needs to open the container netns, so the daemon won't start unless that profile is relaxed. The matching rootlesskit port driver is selected automatically. **ADVANCED** [slirp4netns:slirp4netns (default, reliable)|pasta:Pasta (faster, needs AppArmor override)]

View File

@ -60,10 +60,10 @@ installAuthelia()
dockerComposeSetupFile $app_name;
local result=$(copyResource "$app_name" "configuration.yml" "config" | runFileWrite -a "$logs_dir/$docker_log_file" 2>&1)
local result=$(copyResource "$app_name" "configuration.yml" "config" | runInstallWrite -a "$logs_dir/$docker_log_file" 2>&1)
checkSuccess "Copying configuration.yml to $containers_dir$app_name/config"
local result=$(copyResource "$app_name" "users_database.yml" "config" | runFileWrite -a "$logs_dir/$docker_log_file" 2>&1)
local result=$(copyResource "$app_name" "users_database.yml" "config" | runInstallWrite -a "$logs_dir/$docker_log_file" 2>&1)
checkSuccess "Copying users_database.yml to $containers_dir$app_name/config"
local authelia_config_file="$containers_dir$app_name/config/configuration.yml"

View File

@ -54,7 +54,7 @@ installHeadscale()
local result=$(createFolders "loud" $docker_install_user $containers_dir$app_name/config)
checkSuccess "Create config folder"
local result=$(copyResource "$app_name" "config.yaml" "config" | runFileWrite -a "$logs_dir/$docker_log_file" 2>&1)
local result=$(copyResource "$app_name" "config.yaml" "config" | runInstallWrite -a "$logs_dir/$docker_log_file" 2>&1)
checkSuccess "Copying config.yaml to config folder."
configSetupFileWithData $app_name "config.yaml" "config";

View File

@ -57,7 +57,7 @@ installPrometheus()
local result=$(createTouch "$containers_dir$app_name/$app_name/$app_name.yml" $docker_install_user)
checkSuccess "Created $app_name.yml file for $app_name"
local result=$(copyResource "$app_name" "$app_name.yml" "$app_name" | runFileWrite -a "$logs_dir/$docker_log_file" 2>&1)
local result=$(copyResource "$app_name" "$app_name.yml" "$app_name" | runInstallWrite -a "$logs_dir/$docker_log_file" 2>&1)
checkSuccess "Copying $app_name.yml to containers folder."
((menu_number++))

View File

@ -53,7 +53,7 @@ installUnbound()
monitoringToggleAppConfig "$app_name" "docker-compose.yml";
local result=$(copyResource "$app_name" "unbound.conf" "etc" | runFileWrite -a "$logs_dir/$docker_log_file" 2>&1)
local result=$(copyResource "$app_name" "unbound.conf" "etc" | runInstallWrite -a "$logs_dir/$docker_log_file" 2>&1)
checkSuccess "Copying unbound.conf to containers folder."
monitoringToggleAppConfig "$app_name" "etc/unbound.conf";

View File

@ -44,7 +44,7 @@ dockerConfigSetupToContainer()
if [ "$silent_flag" == "loud" ]; then
isNotice "Copying config file to '$target_path/$config_file'..."
fi
copyFile "$silent_flag" "$source_file" "$target_path/$config_file" $sudo_user_name | runFileWrite -a "$logs_dir/$docker_log_file" 2>&1
copyFile "$silent_flag" "$source_file" "$target_path/$config_file" $sudo_user_name | runInstallWrite -a "$logs_dir/$docker_log_file" 2>&1
fi
if [[ -n "$config_overrides" ]]; then
@ -124,7 +124,7 @@ dockerConfigSetupToContainer()
case $resetconfigaccept in
[yY])
isNotice "Resetting $app_name config file."
copyFile "loud" "$source_file" "$target_path/$config_file" $docker_install_user | runFileWrite -a "$logs_dir/$docker_log_file" 2>&1
copyFile "loud" "$source_file" "$target_path/$config_file" $docker_install_user | runInstallWrite -a "$logs_dir/$docker_log_file" 2>&1
source $target_path/$config_file
dockerConfigSetupToContainer "loud" $app_name;
while true; do
@ -204,7 +204,7 @@ dockerConfigSetupToContainer()
fi
else
isNotice "Config file for $app_name does not exist. Creating it..."
copyFile "loud" "$source_file" "$target_path/$config_file" $docker_install_user | runFileWrite -a "$logs_dir/$docker_log_file" 2>&1
copyFile "loud" "$source_file" "$target_path/$config_file" $docker_install_user | runInstallWrite -a "$logs_dir/$docker_log_file" 2>&1
isNotice "Config file for $app_name contains no edits."
while true; do
#isQuestion "Would you like to make edits to the config file? (y/n): "

View File

@ -62,7 +62,7 @@ tagsProcessorGluetunForwardedPorts()
if [[ ! -f "$docker_dir/$db_file" ]]; then return 0; fi
local installed_apps
installed_apps=$(runFileOp 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)
local routed_lines=""
@ -75,7 +75,7 @@ tagsProcessorGluetunForwardedPorts()
[[ "$net" != "gluetun" ]] && continue
local ports
ports=$(runFileOp sqlite3 "$docker_dir/$db_file" \
ports=$(runInstallOp sqlite3 "$docker_dir/$db_file" \
"SELECT resource_value FROM network_resources WHERE app_name = '$app' AND resource_type = 'port' AND status = 'active';" 2>/dev/null)
while IFS= read -r p; do
[[ -z "$p" ]] && continue

View File

@ -31,7 +31,7 @@ databaseAppScan()
local updated_count=0
# Get the list of all folder names and statuses from the database
local existing_folders=$(runFileOp sqlite3 "$docker_dir/$db_file" "SELECT name, status, uninstall_date FROM apps;")
local existing_folders=$(runInstallOp sqlite3 "$docker_dir/$db_file" "SELECT name, status, uninstall_date FROM apps;")
# Create an array to store existing folder names in the database
local existing_folder_names=()
@ -43,7 +43,7 @@ databaseAppScan()
if (( status == 0 )); then
isNotice "The folder for $folder_name has been found."
# Update the database to set the status to 1 (installed) and unset the uninstall_date
local result=$(runFileOp sqlite3 "$docker_dir/$db_file" "UPDATE apps SET status = 1, uninstall_date = NULL WHERE name = '$folder_name';")
local result=$(runInstallOp sqlite3 "$docker_dir/$db_file" "UPDATE apps SET status = 1, uninstall_date = NULL WHERE name = '$folder_name';")
checkSuccess "Updating apps database for $folder_name to installed status."
((updated_count++)) # Increment updated_count
fi
@ -72,7 +72,7 @@ databaseAppScan()
local folder_time=$(echo "$folder_datetime" | awk '{print $2}')
# Add the new entry to the database with a default status of 1 (installed) and the extracted or current date
local result=$(runFileOp sqlite3 "$docker_dir/$db_file" "INSERT INTO apps (name, status, install_date, install_time) VALUES ('$app_name', 1, '$folder_date', '$folder_time');")
local result=$(runInstallOp sqlite3 "$docker_dir/$db_file" "INSERT INTO apps (name, status, install_date, install_time) VALUES ('$app_name', 1, '$folder_date', '$folder_time');")
checkSuccess "Adding $app_name to the apps database."
((updated_count++)) # Increment updated_count
fi
@ -97,7 +97,7 @@ databaseAppScan()
isNotice "Folder $folder_name no longer exists. Removing from the Database."
# Delete the entry from the apps table
local result=$(runFileOp sqlite3 "$docker_dir/$db_file" "DELETE FROM apps WHERE name = '$app_name';")
local result=$(runInstallOp sqlite3 "$docker_dir/$db_file" "DELETE FROM apps WHERE name = '$app_name';")
checkSuccess "Removing $app_name from the apps database."
portsRemoveFromDatabase $app_name;
@ -196,7 +196,7 @@ databaseAppScan()
else
# If the folder doesn't exist in the directory, uninstall it from the database
isNotice "Folder $folder_name does not exist. Removing from the Database."
local result=$(runFileOp sqlite3 "$docker_dir/$db_file" "DELETE FROM apps WHERE name = '$folder_name';")
local result=$(runInstallOp sqlite3 "$docker_dir/$db_file" "DELETE FROM apps WHERE name = '$folder_name';")
checkSuccess "Removing $folder_name from the apps database."
((updated_count++)) # Increment updated_count
fi

View File

@ -17,16 +17,16 @@ databaseListInstalledApp()
isNotice "Database file not found. Make sure it's installed."
else
# Check if app exists and is installed
local app_check=$(runFileOp sqlite3 "$docker_dir/$db_file" "SELECT name FROM apps WHERE name = '$app_name' AND status = 1;")
local app_check=$(runInstallOp sqlite3 "$docker_dir/$db_file" "SELECT name FROM apps WHERE name = '$app_name' AND status = 1;")
if [ -z "$app_check" ]; then
isNotice "App '$app_name' not found or not installed."
else
local app_details=$(runFileOp sqlite3 "$docker_dir/$db_file" "SELECT install_date, install_time FROM apps WHERE name = '$app_name';")
local app_details=$(runInstallOp sqlite3 "$docker_dir/$db_file" "SELECT install_date, install_time FROM apps WHERE name = '$app_name';")
printf "\e[33m📦 %s\e[0m (Installed: %s)\n" "$app_name" "$app_details"
# Get all Docker services with IPs for this app
local docker_services=$(runFileOp sqlite3 "$docker_dir/$db_file" "SELECT DISTINCT service_name, resource_value FROM network_resources WHERE app_name = '$app_name' AND resource_type = 'ip' AND status = 'active' ORDER BY service_name;" 2>/dev/null)
local docker_services=$(runInstallOp sqlite3 "$docker_dir/$db_file" "SELECT DISTINCT service_name, resource_value FROM network_resources WHERE app_name = '$app_name' AND resource_type = 'ip' AND status = 'active' ORDER BY service_name;" 2>/dev/null)
if [[ -n "$docker_services" ]]; then
local service_count=0
@ -44,7 +44,7 @@ databaseListInstalledApp()
printf "%s \e[94m🔌 Service:\e[0m %s (IP: %s)\n" "$connector" "$docker_service_name" "$docker_service_ip"
# Get all ports for this Docker service
local service_ports=$(runFileOp sqlite3 "$docker_dir/$db_file" "SELECT service_name, resource_value FROM network_resources WHERE app_name = '$app_name' AND resource_type = 'port' AND parent_service = '$docker_service_name' AND status = 'active' ORDER BY service_name;" 2>/dev/null)
local service_ports=$(runInstallOp sqlite3 "$docker_dir/$db_file" "SELECT service_name, resource_value FROM network_resources WHERE app_name = '$app_name' AND resource_type = 'port' AND parent_service = '$docker_service_name' AND status = 'active' ORDER BY service_name;" 2>/dev/null)
if [[ -n "$service_ports" ]]; then
local port_count=0
@ -97,7 +97,7 @@ databaseListInstalledApp()
fi
# Check if Traefik is installed
local traefik_installed=$(runFileOp sqlite3 "$docker_dir/$db_file" "SELECT name FROM apps WHERE name = 'traefik' AND status = 1;" 2>/dev/null)
local traefik_installed=$(runInstallOp sqlite3 "$docker_dir/$db_file" "SELECT name FROM apps WHERE name = 'traefik' AND status = 1;" 2>/dev/null)
# Per-port URL path: 10th column of the matching
# CFG_<APP>_PORT_N row in the deployed config. Find the
@ -137,7 +137,7 @@ databaseListInstalledApp()
local _domain=""
if [[ -n "$traefik_installed" ]]; then
_domain=$(runFileOp sqlite3 "$docker_dir/$db_file" "SELECT resource_value FROM options WHERE option = 'domain_${app_name^^}_DOMAIN';" 2>/dev/null)
_domain=$(runInstallOp sqlite3 "$docker_dir/$db_file" "SELECT resource_value FROM options WHERE option = 'domain_${app_name^^}_DOMAIN';" 2>/dev/null)
fi
local _path_idx _p

View File

@ -51,13 +51,15 @@ runSystem() {
sudo "$@"
}
# Op on the manager install dir / shipped templates — the LibrePortal clone and
# its container templates, owned by the manager user that runs the runtime.
# rooted -> sudo <cmd> (install tree is root-owned; byte-identical)
# rootless -> <cmd> (the manager user already owns it — no privilege)
# For copies that read the install tree and write into /docker (two different
# owners in rootless), don't use this for the whole copy — read here and pipe
# into runFileWrite so each side runs as the correct owner.
# Op on a MANAGER-owned path — the LibrePortal clone + shipped templates AND the
# /docker management layer the runtime owns (apps DB, configs/, logs/, scripts).
# In rootless these are owned by the manager user; ops on them need no privilege.
# rooted -> sudo <cmd> (root-owned; byte-identical)
# rootless -> <cmd> (runs as the current user: root at install-time,
# the manager user at runtime — both can access)
# Container-owned data under /docker/containers/<app>/ is NOT this — use runFileOp.
# For copies that read manager files and write container-owned ones, read here
# and pipe into runFileWrite so each side runs as the correct owner.
runInstallOp() {
if [[ "$CFG_DOCKER_INSTALL_TYPE" == "rootless" ]]; then
"$@"
@ -65,3 +67,22 @@ runInstallOp() {
sudo "$@"
fi
}
# Write stdin to a MANAGER-owned path (apps DB sidecars, configs/, logs/ — e.g.
# the /docker/logs log-append idiom). Mirror of runFileWrite for the manager
# owner. Pass -a/--append as the first arg to append.
# rooted -> sudo tee
# rootless -> tee as the current (manager) user
runInstallWrite() {
local append=""
if [[ "$1" == "-a" || "$1" == "--append" ]]; then
append=" -a"
shift
fi
local dest="$1"
if [[ "$CFG_DOCKER_INSTALL_TYPE" == "rootless" ]]; then
tee$append "$dest" >/dev/null
else
sudo tee$append "$dest" >/dev/null
fi
}

View File

@ -40,7 +40,7 @@ dockerComposeSetupFile()
isError "The source file '$source_file' does not exist."
fi
copyFile "loud" "$source_file" "$target_file" $docker_install_user | runFileWrite -a "$logs_dir/$docker_log_file" 2>&1
copyFile "loud" "$source_file" "$target_file" $docker_install_user | runInstallWrite -a "$logs_dir/$docker_log_file" 2>&1
if [ $? -ne 0 ]; then
isError "Failed to copy the source file to '$target_path'. Check '$docker_log_file' for more details."

View File

@ -212,29 +212,20 @@ EOL"
# Enabling unprivileged user namespaces (needed for rootless) widens the
# kernel attack surface reachable by unprivileged users. Offset that by
# closing the surfaces that local-privilege-escalation chains lean on:
# kptr_restrict hides kernel pointers (defeats info-leak primitives),
# ptrace_scope blocks cross-process ptrace (limits credential theft after
# a compromise), and bpf_jit_harden hardens the JIT against spraying.
# All three are distro-portable and have negligible runtime impact.
if ! grep -qF "# LIBREPORTAL KERNEL HARDENING START" "$sysctl"; then
local result=$(echo '# LIBREPORTAL KERNEL HARDENING START' | sudo tee -a "$sysctl" > /dev/null)
checkSuccess "Adding kernel hardening header to sysctl"
local result=$(echo 'kernel.kptr_restrict=2' | sudo tee -a "$sysctl" > /dev/null)
checkSuccess "Restricting kernel pointer exposure (kptr_restrict)"
local result=$(echo 'kernel.yama.ptrace_scope=1' | sudo tee -a "$sysctl" > /dev/null)
checkSuccess "Restricting cross-process ptrace (yama.ptrace_scope)"
local result=$(echo 'net.core.bpf_jit_harden=2' | sudo tee -a "$sysctl" > /dev/null)
checkSuccess "Hardening the BPF JIT (bpf_jit_harden)"
local result=$(echo '# LIBREPORTAL KERNEL HARDENING END' | sudo tee -a "$sysctl" > /dev/null)
checkSuccess "Adding kernel hardening end to sysctl"
isSuccessful "Applied kernel LPE-surface hardening"
fi
# kptr_restrict hides kernel pointers (info-leak primitives), ptrace_scope
# blocks cross-process ptrace (credential theft post-compromise), and
# bpf_jit_harden hardens the JIT against spraying. Written under
# /etc/sysctl.d/ so `sysctl --system` actually loads it — LibrePortal's
# own $sysctl path (/etc/sysctl/…) is non-standard and is NOT read by
# `sysctl --system`.
local hardening_conf="/etc/sysctl.d/99-libreportal-hardening.conf"
sudo bash -c "cat > '$hardening_conf'" <<'EOL'
# LibrePortal kernel LPE-surface hardening (paired with rootless unprivileged userns)
kernel.kptr_restrict=2
kernel.yama.ptrace_scope=1
net.core.bpf_jit_harden=2
EOL
checkSuccess "Writing kernel LPE-surface hardening to $hardening_conf"
local result=$(sudo sysctl --system)
checkSuccess "Applying changes to sysctl"

View File

@ -5,7 +5,7 @@ function checkSuccess()
if [ $? -eq 0 ]; then
isSuccessful "$1"
if [ -f "$logs_dir/$docker_log_file" ]; then
echo "✓ Success $1" | runFileWrite -a "$logs_dir/$docker_log_file" >/dev/null
echo "✓ Success $1" | runInstallWrite -a "$logs_dir/$docker_log_file" >/dev/null
fi
else
isError "$1"
@ -14,8 +14,8 @@ function checkSuccess()
# blocking on read.
if [[ "$LIBREPORTAL_NONINTERACTIVE" == "1" ]] || [ ! -t 0 ]; then
if [ -f "$logs_dir/$docker_log_file" ]; then
isError " $1" | runFileWrite -a "$logs_dir/$docker_log_file" >/dev/null
echo "===================================" | runFileWrite -a "$logs_dir/$docker_log_file" >/dev/null
isError " $1" | runInstallWrite -a "$logs_dir/$docker_log_file" >/dev/null
echo "===================================" | runInstallWrite -a "$logs_dir/$docker_log_file" >/dev/null
fi
isNotice "Non-interactive mode: aborting on error."
exit 1
@ -36,15 +36,15 @@ function checkSuccess()
if [[ "$error_occurred" == [xX] ]]; then
# Log the error output to the log file
isError " $1" | runFileWrite -a "$logs_dir/$docker_log_file"
echo "===================================" | runFileWrite -a "$logs_dir/$docker_log_file"
isError " $1" | runInstallWrite -a "$logs_dir/$docker_log_file"
echo "===================================" | runInstallWrite -a "$logs_dir/$docker_log_file"
exit 1 # Exit the script with a non-zero status to stop the current action
fi
if [[ "$error_occurred" == [mM] ]]; then
# Log the error output to the log file
isError " $1" | runFileWrite -a "$logs_dir/$docker_log_file"
echo "===================================" | runFileWrite -a "$logs_dir/$docker_log_file"
isError " $1" | runInstallWrite -a "$logs_dir/$docker_log_file"
echo "===================================" | runInstallWrite -a "$logs_dir/$docker_log_file"
if [[ "$initial_command2" == "terminal" ]]; then
resetToMenu;
fi

View File

@ -18,7 +18,7 @@ fixAppFolderPermissions()
# Updating $containers_dir with execute permissions
if [ -d "$containers_dir" ]; then
local result=$(runFileOp chmod +x "$containers_dir" > /dev/null 2>&1)
local result=$(runSystem chmod +x "$containers_dir" > /dev/null 2>&1)
if [ "$silent_flag" == "loud" ]; then
checkSuccess "Updating $containers_dir with execute permissions."
fi
@ -30,7 +30,7 @@ fixAppFolderPermissions()
# Updating $containers_dir$app_name with execute permissions
if [ -d "$containers_dir$app_name" ]; then
local result=$(runFileOp chmod +x "$containers_dir$app_name" > /dev/null 2>&1)
local result=$(runSystem chmod +x "$containers_dir$app_name" > /dev/null 2>&1)
if [ "$silent_flag" == "loud" ]; then
checkSuccess "Updating $containers_dir$app_name with execute permissions."
fi
@ -42,7 +42,7 @@ fixAppFolderPermissions()
# Updating $app_name with read permissions
if [ -d "$containers_dir$app_name" ]; then
local result=$(runFileOp chmod o+r "$containers_dir$app_name")
local result=$(runSystem chmod o+r "$containers_dir$app_name")
if [ "$silent_flag" == "loud" ]; then
checkSuccess "Updating $app_name with read permissions"
fi
@ -54,7 +54,7 @@ fixAppFolderPermissions()
# Updating compose file(s) for LibrePortal access
if [ -d "$containers_dir$app_name" ]; then
local result=$(runFileOp find "$containers_dir$app_name" -type f -name '*docker-compose*' -exec chmod o+r {} \;)
local result=$(runSystem find "$containers_dir$app_name" -type f -name '*docker-compose*' -exec chmod o+r {} \;)
if [ "$silent_flag" == "loud" ]; then
isNotice "Updating compose file(s) for LibrePortal access"
fi
@ -70,7 +70,7 @@ fixAppFolderPermissions()
local file_path="$containers_dir$app_name/$file"
# Check if the file exists
if [ -e "$file_path" ]; then
local result=$(runFileOp chown $docker_install_user:$docker_install_user "$file_path")
local result=$(runSystem chown $docker_install_user:$docker_install_user "$file_path")
if [ "$silent_flag" == "loud" ]; then
checkSuccess "Updating $file with $docker_install_user ownership"
fi

View File

@ -5,23 +5,23 @@ fixFolderPermissions()
local silent_flag="$1"
local app_name="$2"
local result=$(runFileOp chmod +x "$docker_dir" > /dev/null 2>&1)
local result=$(runSystem chmod +x "$docker_dir" > /dev/null 2>&1)
if [ "$silent_flag" == "loud" ]; then
checkSuccess "Updating $docker_dir with execute permissions."
fi
local result=$(runFileOp chmod +x "$containers_dir" > /dev/null 2>&1)
local result=$(runSystem chmod +x "$containers_dir" > /dev/null 2>&1)
if [ "$silent_flag" == "loud" ]; then
checkSuccess "Updating $containers_dir with execute permissions."
fi
local result=$(runFileOp find "$script_dir" "$ssl_dir" "$ssh_dir" "$backup_dir" "$restore_dir" "$migrate_dir" -maxdepth 2 -type d -exec chmod +x {} \;)
local result=$(runSystem find "$script_dir" "$ssl_dir" "$ssh_dir" "$backup_dir" "$restore_dir" "$migrate_dir" -maxdepth 2 -type d -exec chmod +x {} \;)
if [ "$silent_flag" == "loud" ]; then
checkSuccess "Adding execute permissions for $docker_install_user user"
fi
# Install user related
local result=$(runFileOp chown $docker_install_user:$docker_install_user "$containers_dir" > /dev/null 2>&1)
local result=$(runSystem chown $docker_install_user:$docker_install_user "$containers_dir" > /dev/null 2>&1)
if [ "$silent_flag" == "loud" ]; then
checkSuccess "Updating $containers_dir with $docker_install_user ownership"
fi

View File

@ -8,7 +8,7 @@ tailscaleInstallToContainer()
local result=$(createFolders "loud" $docker_install_user $containers_dir$app_name/tailscale)
checkSuccess "Creating Tailscale folder"
copyFile "loud" "${install_scripts_dir}tailscale.sh" "$containers_dir$app_name/tailscale/tailscale.sh" $docker_install_user | runFileWrite -a "$logs_dir/$docker_log_file" 2>&1
copyFile "loud" "${install_scripts_dir}tailscale.sh" "$containers_dir$app_name/tailscale/tailscale.sh" $docker_install_user | runInstallWrite -a "$logs_dir/$docker_log_file" 2>&1
if [[ "$type" != "install" ]]; then
dockerComposeRestart $app_name;

View File

@ -17,7 +17,7 @@
monitoringInstalledApps()
{
[[ -f "$docker_dir/$db_file" ]] || return 0
runFileOp sqlite3 "$docker_dir/$db_file" \
runInstallOp sqlite3 "$docker_dir/$db_file" \
"SELECT name FROM apps WHERE status = 1 ORDER BY name;" 2>/dev/null
}
@ -26,7 +26,7 @@ monitoringIsInstalled()
{
[[ -f "$docker_dir/$db_file" ]] || return 1
local n
n="$(runFileOp sqlite3 "$docker_dir/$db_file" \
n="$(runInstallOp sqlite3 "$docker_dir/$db_file" \
"SELECT COUNT(*) FROM apps WHERE name = '$1' AND status = 1;" 2>/dev/null)"
[[ -n "$n" && "$n" -gt 0 ]]
}

View File

@ -9,23 +9,23 @@ portStoreMapping()
local parent_service="$4" # parent Docker service (optional)
# Check if ports already exist for this app/service
local existing=$(runFileOp sqlite3 "$docker_dir/$db_file" "SELECT resource_value FROM network_resources WHERE app_name='$app_name' AND resource_type='port' AND service_name='$service_name' AND status='active';" 2>/dev/null)
local existing=$(runInstallOp sqlite3 "$docker_dir/$db_file" "SELECT resource_value FROM network_resources WHERE app_name='$app_name' AND resource_type='port' AND service_name='$service_name' AND status='active';" 2>/dev/null)
if [[ -n "$existing" ]]; then
# UPDATE existing ports
if [[ -n "$parent_service" ]]; then
runFileOp sqlite3 "$docker_dir/$db_file" "UPDATE network_resources SET resource_value='$port_mapping', parent_service='$parent_service', created_time=CURRENT_TIME WHERE app_name='$app_name' AND resource_type='port' AND service_name='$service_name';" 2>/dev/null
runInstallOp sqlite3 "$docker_dir/$db_file" "UPDATE network_resources SET resource_value='$port_mapping', parent_service='$parent_service', created_time=CURRENT_TIME WHERE app_name='$app_name' AND resource_type='port' AND service_name='$service_name';" 2>/dev/null
else
runFileOp sqlite3 "$docker_dir/$db_file" "UPDATE network_resources SET resource_value='$port_mapping', created_time=CURRENT_TIME WHERE app_name='$app_name' AND resource_type='port' AND service_name='$service_name';" 2>/dev/null
runInstallOp sqlite3 "$docker_dir/$db_file" "UPDATE network_resources SET resource_value='$port_mapping', created_time=CURRENT_TIME WHERE app_name='$app_name' AND resource_type='port' AND service_name='$service_name';" 2>/dev/null
fi
isNotice "Updated ports for $app_name/$service_name"
#isNotice "Updated ports for $app_name/$service_name: $existing → $port_mapping"
else
# INSERT new ports
if [[ -n "$parent_service" ]]; then
runFileOp sqlite3 "$docker_dir/$db_file" "INSERT INTO network_resources (app_name, resource_type, resource_value, service_name, parent_service, status, created_date, created_time) VALUES ('$app_name', 'port', '$port_mapping', '$service_name', '$parent_service', 'active', CURRENT_DATE, CURRENT_TIME);" 2>/dev/null
runInstallOp sqlite3 "$docker_dir/$db_file" "INSERT INTO network_resources (app_name, resource_type, resource_value, service_name, parent_service, status, created_date, created_time) VALUES ('$app_name', 'port', '$port_mapping', '$service_name', '$parent_service', 'active', CURRENT_DATE, CURRENT_TIME);" 2>/dev/null
else
runFileOp sqlite3 "$docker_dir/$db_file" "INSERT INTO network_resources (app_name, resource_type, resource_value, service_name, status, created_date, created_time) VALUES ('$app_name', 'port', '$port_mapping', '$service_name', 'active', CURRENT_DATE, CURRENT_TIME);" 2>/dev/null
runInstallOp sqlite3 "$docker_dir/$db_file" "INSERT INTO network_resources (app_name, resource_type, resource_value, service_name, status, created_date, created_time) VALUES ('$app_name', 'port', '$port_mapping', '$service_name', 'active', CURRENT_DATE, CURRENT_TIME);" 2>/dev/null
fi
isSuccessful "Allocated port: $port_mapping"
fi

View File

@ -108,14 +108,14 @@ portUpdateComposeTags()
# Only log to database if port is not disabled
if [[ "$access_type_for_tag" != "disabled" ]]; then
# Log to database
runFileOp sqlite3 "$docker_dir/$db_file" "INSERT OR REPLACE INTO network_resources (app_name, resource_type, resource_value, service_name, status, created_date, created_time) VALUES ('$app_name', 'port_tag_combined', '$combined_port', '$db_service_name', 'active', CURRENT_DATE, CURRENT_TIME);" 2>/dev/null
runFileOp sqlite3 "$docker_dir/$db_file" "INSERT OR REPLACE INTO network_resources (app_name, resource_type, resource_value, service_name, status, created_date, created_time) VALUES ('$app_name', 'port_tag_external', '$port_for_tag', '$db_service_name', 'active', CURRENT_DATE, CURRENT_TIME);" 2>/dev/null
runFileOp sqlite3 "$docker_dir/$db_file" "INSERT OR REPLACE INTO network_resources (app_name, resource_type, resource_value, service_name, status, created_date, created_time) VALUES ('$app_name', 'port_tag_internal', '$internal_port_for_tag', '$db_service_name', 'active', CURRENT_DATE, CURRENT_TIME);" 2>/dev/null
runFileOp sqlite3 "$docker_dir/$db_file" "INSERT OR REPLACE INTO network_resources (app_name, resource_type, resource_value, service_name, status, created_date, created_time) VALUES ('$app_name', 'traefik_managed', '$traefik_managed_for_tag', '$db_service_name', 'active', CURRENT_DATE, CURRENT_TIME);" 2>/dev/null
runFileOp sqlite3 "$docker_dir/$db_file" "INSERT OR REPLACE INTO network_resources (app_name, resource_type, resource_value, service_name, status, created_date, created_time) VALUES ('$app_name', 'url_accessible', '$url_accessible_for_tag', '$db_service_name', 'active', CURRENT_DATE, CURRENT_TIME);" 2>/dev/null
runInstallOp sqlite3 "$docker_dir/$db_file" "INSERT OR REPLACE INTO network_resources (app_name, resource_type, resource_value, service_name, status, created_date, created_time) VALUES ('$app_name', 'port_tag_combined', '$combined_port', '$db_service_name', 'active', CURRENT_DATE, CURRENT_TIME);" 2>/dev/null
runInstallOp sqlite3 "$docker_dir/$db_file" "INSERT OR REPLACE INTO network_resources (app_name, resource_type, resource_value, service_name, status, created_date, created_time) VALUES ('$app_name', 'port_tag_external', '$port_for_tag', '$db_service_name', 'active', CURRENT_DATE, CURRENT_TIME);" 2>/dev/null
runInstallOp sqlite3 "$docker_dir/$db_file" "INSERT OR REPLACE INTO network_resources (app_name, resource_type, resource_value, service_name, status, created_date, created_time) VALUES ('$app_name', 'port_tag_internal', '$internal_port_for_tag', '$db_service_name', 'active', CURRENT_DATE, CURRENT_TIME);" 2>/dev/null
runInstallOp sqlite3 "$docker_dir/$db_file" "INSERT OR REPLACE INTO network_resources (app_name, resource_type, resource_value, service_name, status, created_date, created_time) VALUES ('$app_name', 'traefik_managed', '$traefik_managed_for_tag', '$db_service_name', 'active', CURRENT_DATE, CURRENT_TIME);" 2>/dev/null
runInstallOp sqlite3 "$docker_dir/$db_file" "INSERT OR REPLACE INTO network_resources (app_name, resource_type, resource_value, service_name, status, created_date, created_time) VALUES ('$app_name', 'url_accessible', '$url_accessible_for_tag', '$db_service_name', 'active', CURRENT_DATE, CURRENT_TIME);" 2>/dev/null
else
# Log disabled port with 'disabled' status
runFileOp sqlite3 "$docker_dir/$db_file" "INSERT OR REPLACE INTO network_resources (app_name, resource_type, resource_value, service_name, status, created_date, created_time) VALUES ('$app_name', 'port_tag_combined', '$combined_port', '$db_service_name', 'disabled', CURRENT_DATE, CURRENT_TIME);" 2>/dev/null
runInstallOp sqlite3 "$docker_dir/$db_file" "INSERT OR REPLACE INTO network_resources (app_name, resource_type, resource_value, service_name, status, created_date, created_time) VALUES ('$app_name', 'port_tag_combined', '$combined_port', '$db_service_name', 'disabled', CURRENT_DATE, CURRENT_TIME);" 2>/dev/null
fi
done