From abcfdc134a920a5822010be991b8e617d43f6098 Mon Sep 17 00:00:00 2001 From: librelad Date: Wed, 19 Aug 2026 02:01:46 +0100 Subject: [PATCH] feat(validation): catch a container running a different secret than the config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 _ 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 --- scripts/validation/validate_config.sh | 51 +++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/scripts/validation/validate_config.sh b/scripts/validation/validate_config.sh index 184c793..1b739a0 100644 --- a/scripts/validation/validate_config.sh +++ b/scripts/validation/validate_config.sh @@ -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 _ 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