The main sweep — ~260 call sites across ~100 files move from string
concatenation on a single root to appDir/storageAppDirs/storageAppConfigs.
On a single-root install the resolved paths are identical, so this is a
no-op until a location is registered.
Enumerators were the interesting half. `for d in "$containers_dir"/*/`
appears in the menus, the registry/artifact scanners and the DNS setup —
and a shell glob cannot list a rootless 751 tree at all, which is the
same bug config_find_file.sh already documents in a comment. Routing them
through storageAppDirs (which enumerates as the owning user) fixes that
alongside the multi-root work.
Three places needed judgement rather than substitution:
db_app_scan.sh deletes database rows and port allocations for apps whose
folder is missing, and reaps "empty" app dirs. With a storage location
unmounted, every app on it looks exactly like that. Each of those
branches now gates on appStorageAvailable first — an app on an unplugged
drive is skipped with a notice, never deleted.
instance_create.sh rewrites cloned hooks so an instance touches its own
directory instead of the base app's. Its sed matched ${containers_dir}<type>,
which this sweep just replaced with $(appDir <type>) — so it would have
silently stopped redirecting, and an instance would have written to the
original's files (the adguard auth adapter case its own comment warns
about). Now matches both appDir forms, verified against bare, quoted,
unrelated-app, legacy and prose cases.
peer_shell/peer_pull streamed and extracted relative to the primary root.
Both now use the app's own root, and peer_shell keeps a single-root
fallback since it runs as a restricted SSH shell with no LibrePortal env.
Also fixes a pre-existing bug found on the way: webui_app_config.sh
tested "$containers_dir/frontend/data/last_update", one level short of the
real tree under the libreportal app dir, so the WebUI refresh trigger
after a config update has never once fired.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
100 lines
4.4 KiB
Bash
100 lines
4.4 KiB
Bash
#!/bin/bash
|
|
|
|
# Traefik install hooks — interactive email prompt (LE notices), static +
|
|
# dynamic config copy, dashboard-access mode wiring (local-only / domain-
|
|
# only / public), monitoring toggle on traefik.yml, whitelist + login.
|
|
|
|
traefik_install_pre()
|
|
{
|
|
local app_name="$1"
|
|
|
|
if [[ -z "$CFG_TRAEFIK_EMAIL" || "$CFG_TRAEFIK_EMAIL" == "changeme" || "$CFG_TRAEFIK_EMAIL" == "Change-Me" ]]; then
|
|
if [[ "$LIBREPORTAL_NONINTERACTIVE" == "1" ]]; then
|
|
isError "CFG_TRAEFIK_EMAIL not set. Run 'libreportal config update CFG_TRAEFIK_EMAIL=you@example.com' first."
|
|
return 1
|
|
fi
|
|
local traefik_email=""
|
|
while true; do
|
|
isQuestion "Administrator email for LetsEncrypt (cert-expiry notices) : "
|
|
read -p "" traefik_email
|
|
emailValidation "$traefik_email"
|
|
[[ $? -eq 0 ]] && break
|
|
isNotice "Please provide a valid email address."
|
|
done
|
|
updateConfigOption "CFG_TRAEFIK_EMAIL" "$traefik_email"
|
|
sourceScanFiles "libreportal_configs"
|
|
fi
|
|
}
|
|
|
|
traefik_install_post_compose()
|
|
{
|
|
local app_name="$1"
|
|
|
|
local result
|
|
result=$(createFolders "loud" $docker_install_user "$(appDir "$app_name")/etc" "$(appDir "$app_name")/etc/certs" "$(appDir "$app_name")/etc/dynamic" "$(appDir "$app_name")/etc/dynamic/middlewears")
|
|
checkSuccess "Created etc and certs & dynamic Directories"
|
|
|
|
result=$(copyResource "$app_name" "traefik.yml" "etc")
|
|
checkSuccess "Copy Traefik configuration file for $app_name"
|
|
|
|
result=$(runFileOp sed -i "s|DEBUGLEVEL|$CFG_TRAEFIK_LOGGING|g" "$(appDir "$app_name")/etc/traefik.yml")
|
|
checkSuccess "Configured Traefik debug level with: $CFG_TRAEFIK_LOGGING for $app_name"
|
|
|
|
configSetupFileWithData $app_name "traefik.yml" "etc"
|
|
|
|
# Apply CFG_TRAEFIK_DASHBOARD_ACCESS: rewrites api.insecure + the
|
|
# `traefik:` entrypoint in traefik.yml, and (for local-only) prefixes
|
|
# the compose port mapping with 127.0.0.1: so :8080 binds to loopback.
|
|
local traefik_yml="$(appDir "$app_name")/etc/traefik.yml"
|
|
local compose_yml="$(appDir "$app_name")/docker-compose.yml"
|
|
local access="${CFG_TRAEFIK_DASHBOARD_ACCESS:-local-only}"
|
|
|
|
case "$access" in
|
|
local-only)
|
|
runFileOp sed -i 's|^\(\s*insecure:\s*\).*$|\1true|' "$traefik_yml"
|
|
runFileOp sed -i '/#LIBREPORTAL|PORTS_TAG_1|/ {
|
|
/127\.0\.0\.1:/! s|"\([0-9]\+:[0-9]\+\)"|"127.0.0.1:\1"|
|
|
}' "$compose_yml"
|
|
checkSuccess "Dashboard access: local-only (loopback :8080 + auth-protected domain)"
|
|
;;
|
|
domain-only)
|
|
runFileOp sed -i 's|^\(\s*insecure:\s*\).*$|\1false|' "$traefik_yml"
|
|
runFileOp sed -i '/^\s*traefik:\s*$/,/^\s*address:\s*:8080\s*$/d' "$traefik_yml"
|
|
checkSuccess "Dashboard access: domain-only (auth-protected via Host route only)"
|
|
;;
|
|
public)
|
|
runFileOp sed -i 's|^\(\s*insecure:\s*\).*$|\1true|' "$traefik_yml"
|
|
runFileOp sed -i '/#LIBREPORTAL|PORTS_TAG_1|/ s|"127\.0\.0\.1:\([0-9]\+:[0-9]\+\)"|"\1"|' "$compose_yml"
|
|
checkSuccess "Dashboard access: public (unauthenticated :8080 on all interfaces — legacy)"
|
|
;;
|
|
*)
|
|
isNotice "Unknown CFG_TRAEFIK_DASHBOARD_ACCESS='$access'; leaving traefik.yml at defaults."
|
|
;;
|
|
esac
|
|
|
|
# Traefik's metrics block lives in traefik.yml; toggle that here. The
|
|
# driver already toggled docker-compose.yml.
|
|
monitoringToggleAppConfig "$app_name" "etc/traefik.yml"
|
|
|
|
result=$(copyResource "$app_name" "config.yml" "etc/dynamic")
|
|
checkSuccess "Copy Traefik Dynamic config.yml configuration file for $app_name"
|
|
|
|
result=$(runFileOp sed -i "s|ERRORWEBSITE|$CFG_TRAEFIK_404_SITE|g" "$(appDir "$app_name")/etc/dynamic/config.yml")
|
|
checkSuccess "Configured Traefik error website with URL: $CFG_TRAEFIK_404_SITE for $app_name"
|
|
|
|
configSetupFileWithData $app_name "config.yml" "etc/dynamic"
|
|
|
|
result=$(copyResource "$app_name" "whitelist.yml" "etc/dynamic")
|
|
checkSuccess "Copy Traefik Dynamic whitelist.yml configuration file for $app_name"
|
|
|
|
result=$(copyResource "$app_name" "protectionauth.yml" "etc/dynamic/middlewears")
|
|
checkSuccess "Copy Traefik Dynamic protectionauth.yml configuration file for $app_name"
|
|
|
|
traefikUpdateWhitelist
|
|
|
|
result=$(copyResource "$app_name" "tls.yml" "etc/dynamic")
|
|
checkSuccess "Copy Traefik Dynamic tls.yml configuration file for $app_name"
|
|
|
|
traefikSetupLoginCredentials
|
|
}
|