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>
51 lines
2.1 KiB
Bash
51 lines
2.1 KiB
Bash
#!/bin/bash
|
|
|
|
# Issue a new API key for the Traefik CrowdSec bouncer.
|
|
#
|
|
# This is the action crowdsec.config and the install script both point at when
|
|
# the key is lost or should be replaced. cscli cannot re-issue a key for an
|
|
# existing bouncer, so rotating means delete + re-add; the privileged helper
|
|
# does both and rewrites /etc/crowdsec/traefik_bouncer.key.
|
|
#
|
|
# The old key stops working the instant the bouncer is deleted, and Traefik
|
|
# holds the key file open — so Traefik is restarted afterwards to pick up the new
|
|
# one. Between those two points requests are authenticated with a dead key, which
|
|
# is why this is a deliberate action and not something the installer does on its
|
|
# own.
|
|
appCrowdsecRotateBouncerKey()
|
|
{
|
|
local app_name="crowdsec"
|
|
|
|
local result
|
|
result=$(runCrowdsec bouncer-traefik-rotate 2>&1)
|
|
|
|
if [[ "$result" != GENERATED:* ]]; then
|
|
isError "Could not rotate the Traefik bouncer key: $result"
|
|
isNotice "The previous key may already have been revoked — check 'cscli bouncers list' before retrying."
|
|
return 1
|
|
fi
|
|
|
|
local bouncer_key="${result#GENERATED:}"
|
|
isSuccessful "New Traefik bouncer API key issued."
|
|
|
|
# Mirror it the same way the installer does, so the config page and the key
|
|
# file agree. updateConfigOption escapes the value, writes as the owner of
|
|
# the containers tree, and re-sources.
|
|
local cfg_file="$(appDir "$app_name")/${app_name}.config"
|
|
if [[ -f "$cfg_file" ]]; then
|
|
updateConfigOption "CFG_CROWDSEC_TRAEFIK_LAPI_KEY" "$bouncer_key" "$cfg_file"
|
|
else
|
|
isNotice "crowdsec.config is not deployed — the key is in /etc/crowdsec/traefik_bouncer.key only."
|
|
fi
|
|
|
|
# Traefik reads the key from the bind-mounted file at startup, so it keeps
|
|
# presenting the revoked key until it restarts. Without this the rotation
|
|
# looks successful while every bouncer check fails with 403.
|
|
if [[ -d "$(appDir traefik)" ]]; then
|
|
dockerComposeRestart traefik
|
|
checkSuccess "Restarting Traefik to load the new bouncer key"
|
|
else
|
|
isNotice "Traefik is not installed here — nothing to restart."
|
|
fi
|
|
}
|