fix(crowdsec): mirror the bouncer key to a file that exists

crowdsec_install_host.sh wrote CFG_CROWDSEC_TRAEFIK_LAPI_KEY into
${configs_dir}security/security_crowdsec, but no such template ships in
configs/security/ (only security_logins and security_ssh), so
checkConfigFilesMissingFiles never created it, the -f guard always failed, and
the key was never mirrored — every install logged "Live config not present yet"
and the setting stayed empty. The key is declared in crowdsec.config, so point
the write there.

Switched the hand-rolled sed for updateConfigOption, which escapes the value,
routes the write through the user owning the containers tree, and re-sources so
the key is live in the same run. The old sed used | as its delimiter and would
have corrupted the file on a key containing one; verified the new path
round-trips a key with + / and | intact.

updateConfigOption logged "Updated <key> to <value>", and checkSuccess both
prints its message and appends it to the docker log — so mirroring the bouncer
key would have written it to disk in plaintext, as every admin password the auth
adapters persist already was. Credential-looking keys now log the name only;
everything else still logs its value, which is what makes that log useful.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
librelad 2026-08-18 23:48:38 +01:00
parent 84b027feed
commit 5087a88f65
2 changed files with 27 additions and 11 deletions

View File

@ -165,11 +165,14 @@ installCrowdsecHost()
# the Traefik container read-only; the plugin reads it via
# crowdsecLapiKeyFile. /etc/crowdsec/ is outside the framework's
# sourceScanFiles sweep so a bare key file is safe here.
# 2) ${configs_dir}security/security_crowdsec — CFG_CROWDSEC_TRAEFIK_LAPI_KEY
# line, sourced by the framework and visible on the config page.
# Editing the CFG var manually does not re-register the bouncer
# (use the rotate Tools action for that); this is a visibility
# surface, not the auth source of truth.
# 2) the deployed crowdsec.config — CFG_CROWDSEC_TRAEFIK_LAPI_KEY line,
# sourced by the framework and visible on the config page. Editing
# the CFG var manually does not re-register the bouncer; this is a
# visibility surface, not the auth source of truth.
# This used to point at ${configs_dir}security/security_crowdsec,
# which no template ever shipped — so the file never existed, the
# -f guard always failed, and the key was never mirrored. The key
# is declared in crowdsec.config, so that is where it belongs.
# The helper handles cscli + tee + chown + chmod atomically.
local init_result
init_result=$(runCrowdsec bouncer-traefik-init 2>&1)
@ -181,13 +184,15 @@ installCrowdsecHost()
# Mirror the key into the live config file so it's visible /
# editable via the framework's config page like any other CFG_*
# setting. configs/ is manager-owned, so runInstallOp suffices.
local cfg_file="${configs_dir}security/security_crowdsec"
# setting. updateConfigOption rather than a hand-rolled sed: it
# escapes the value, routes the write through the user that owns
# the containers tree, and re-sources so the new key is live in
# this same run.
local cfg_file="${containers_dir}crowdsec/crowdsec.config"
if [[ -f "$cfg_file" ]]; then
runInstallOp sed -i "s|^CFG_CROWDSEC_TRAEFIK_LAPI_KEY=.*|CFG_CROWDSEC_TRAEFIK_LAPI_KEY=${bouncer_key}|" "$cfg_file"
checkSuccess "Key mirrored to CFG_CROWDSEC_TRAEFIK_LAPI_KEY"
updateConfigOption "CFG_CROWDSEC_TRAEFIK_LAPI_KEY" "$bouncer_key" "$cfg_file"
else
isNotice "Live config not present yet — key applied on next install."
isNotice "crowdsec.config not deployed yet — key applied on next install."
fi
else
isNotice "Failed to generate bouncer key: $init_result"

View File

@ -55,7 +55,18 @@ updateConfigOption()
else
$_write_op sed -i "s${DELIM}^${config_option}=.*${DELIM}${config_option}=\"${escaped_value}\"${DELIM}" "$config_file"
fi
checkSuccess "Updated $config_option to $config_value"
# Never echo the value of a credential. checkSuccess prints its message
# and appends it to the docker log, so "Updated CFG_X_ADMIN_PASSWORD to
# hunter2" puts a live password in a file on disk — for every admin
# password the auth adapters persist, every generated token, every key.
# Non-secret settings keep showing their value, which is what makes the
# log useful for tracing a config change.
case "$config_option" in
*PASSWORD*|*PASS|*SECRET*|*TOKEN*|*_KEY|*_KEY_[0-9]*|*APIKEY*)
checkSuccess "Updated $config_option" ;;
*)
checkSuccess "Updated $config_option to $config_value" ;;
esac
source "$config_file"
else
isNotice "Unable to find $config_option with value in $config_file"