feat(validation): catch a container running a different secret than the config

Speedtest's config held one password while its container ran another, so the
WebUI credentials card advertised a login that could not work. Validation only
caught it by accident: the rename left a stale tag behind, and the tag-name check
fired on that. Had the rename kept the name, the divergence would have been
invisible — and it is the divergence, not the tag, that actually breaks someone's
login.

So compare them directly: for every app-prefixed tag in the DEPLOYED compose,
check the substituted value against the deployed config's. Live files only —
in the templates one side is a placeholder and the other a RANDOMIZED token, so
they could never agree.

Resolves the slot rather than giving up: a compose written before a key gained
its _<n> suffix still carries the old tag, so fall back to the numbered variant
and compare anyway. That is warned about, not passed over — the compose is due a
re-template — but the warning is separate from the failure, so a stale name with
matching values reports only the warning.

Verified both ways against a fixture of speedtest's real pre-fix state (warning
plus failure), the same fixture with values agreed (warning only), and the live
install across 39 apps (silent).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
librelad 2026-08-19 02:01:46 +01:00
parent 6861358809
commit abcfdc134a

View File

@ -116,6 +116,56 @@ _lpvCheckDeployedSecrets()
| sed 's/^[^=]*=//' | tr -d '"' | sort | uniq -d)
}
# --- deployed: does the container actually run what the config advertises? --
# Only meaningful on a live install, where both sides hold real values: in the
# templates one side is a placeholder and the other a RANDOMIZED token, so they
# would never match.
#
# This is the symptom that matters. Speedtest's config said one password while
# its container ran another, so the WebUI credentials card showed a login that
# could not work — and nothing detected it, because the tag-name check only
# caught it by accident (the rename left a stale tag behind). Had the rename kept
# the name, the divergence would have been invisible.
#
# Slot resolution: a deployed compose written before a key gained its _<n> slot
# still carries the old tag, so fall back to the numbered variant rather than
# giving up — but say so, because that compose is due a re-template.
_lpvCheckDeployedValues()
{
local app="$1" cfg="$2" comp="$3"
[[ -n "$cfg" && -n "$comp" ]] || return 0
local up="${app^^}"; up="${up//-/_}"
local line tag val key slot cfgval
while IFS='|' read -r tag val; do
[[ -n "$tag" && -n "$val" ]] || continue
key="CFG_${tag%_TAG}"
cfgval=$(grep -m1 "^${key}=" "$cfg" 2>/dev/null | cut -d= -f2-)
if [[ -z "$cfgval" ]]; then
# No exact key — try the slot the rename introduced.
slot=$(grep -m1 -oE "^CFG_${tag%_TAG}_[0-9]+=" "$cfg" 2>/dev/null | tr -d '=')
[[ -n "$slot" ]] || continue
_lpvWarn "$app: compose still uses ${tag}, but the config declares ${slot} — re-template ('libreportal app install $app') so the name matches."
key="$slot"
cfgval=$(grep -m1 "^${key}=" "$cfg" 2>/dev/null | cut -d= -f2-)
fi
cfgval="${cfgval%%#*}"
cfgval="${cfgval//\"/}"
cfgval="${cfgval//[[:space:]]/}"
# Not generated yet, or the compose still holds its placeholder: other
# checks own those cases.
[[ -z "$cfgval" || "$cfgval" == RANDOMIZED* ]] && continue
[[ "$val" =~ ^[A-Z][A-Z0-9_]*_DATA(_[0-9]+)?$ ]] && continue
if [[ "$cfgval" != "$val" ]]; then
_lpvFail "$app: $key in the config does not match what the container runs (tag $tag) — anything showing this value, the WebUI card included, is advertising something that will not work."
fi
done < <(grep -oE "#LIBREPORTAL\|${up}_[A-Z0-9_]+_TAG\|[^|[:space:]]+" "$comp" 2>/dev/null \
| sed 's/#LIBREPORTAL|//' | sed 's/_TAG|/_TAG|/' | awk -F'|' '!seen[$1]++ {print $1"|"$2}')
}
# --- compose: annotations substitutable, tags backed -----------------------
_lpvCheckCompose()
{
@ -215,6 +265,7 @@ validateAppConfiguration()
_lpvCheckCompose "$app" "${_lpv_comp_live:-$_lpv_comp_tmpl}" \
"$( [[ -n "$_lpv_comp_live" ]] && echo "deployed compose" || echo "compose template" )" \
"${_lpv_cfg_live:-$_lpv_cfg_tmpl}"
_lpvCheckDeployedValues "$app" "$_lpv_cfg_live" "$_lpv_comp_live"
_lpvCheckAuthAdapter "$app" "${_lpv_cfg_live:-$_lpv_cfg_tmpl}"
# A single-app run that says nothing is indistinguishable from one that did