diff --git a/containers/crowdsec/scripts/crowdsec_install_host.sh b/containers/crowdsec/scripts/crowdsec_install_host.sh index ec3e18a..dba68df 100644 --- a/containers/crowdsec/scripts/crowdsec_install_host.sh +++ b/containers/crowdsec/scripts/crowdsec_install_host.sh @@ -174,29 +174,60 @@ installCrowdsecHost() # -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) - if [[ "$init_result" == "EXISTS" ]]; then - isNotice "Bouncer 'traefik-bouncer' already registered — leaving existing key file untouched at /etc/crowdsec/traefik_bouncer.key." - elif [[ "$init_result" == GENERATED:* ]]; then - local bouncer_key="${init_result#GENERATED:}" - checkSuccess "Traefik bouncer API key generated" + local cfg_file="${containers_dir}crowdsec/crowdsec.config" + local key_file="/etc/crowdsec/traefik_bouncer.key" - # Mirror the key into the live config file so it's visible / - # editable via the framework's config page like any other CFG_* - # 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" + # What the config currently holds, if anything. Quotes and whitespace + # stripped: updateConfigOption writes the value quoted. + local recorded_key="" + if [[ -f "$cfg_file" ]]; then + recorded_key=$(grep -m1 '^CFG_CROWDSEC_TRAEFIK_LAPI_KEY=' "$cfg_file" 2>/dev/null | cut -d= -f2-) + recorded_key="${recorded_key%%#*}" + recorded_key="${recorded_key//\"/}" + recorded_key="${recorded_key//[[:space:]]/}" + fi + + local init_result bouncer_key="" + init_result=$(runCrowdsec bouncer-traefik-init 2>&1) + + if [[ "$init_result" == "EXISTS" ]]; then + isNotice "Bouncer 'traefik-bouncer' already registered — leaving the existing key file untouched at $key_file." + # Self-heal. cscli cannot show an existing bouncer's key, so an + # install whose mirror never landed (every install before the write + # target was corrected) had no way back to the value short of + # re-registering the bouncer — which invalidates the key Traefik is + # already using. The helper leaves the key file owned by the manager + # at 0600 precisely so this layer can read it back. + if [[ -z "$recorded_key" ]]; then + if [[ -r "$key_file" ]]; then + bouncer_key=$(tr -d '\r\n' < "$key_file") + [[ -n "$bouncer_key" ]] \ + && isNotice "Config had no bouncer key — recovering it from $key_file." \ + || isNotice "$key_file is empty — cannot recover the bouncer key. Rotate it to get a new one." + else + isNotice "Config has no bouncer key and $key_file is not readable — rotate the bouncer to issue a new one." + fi + fi + elif [[ "$init_result" == GENERATED:* ]]; then + bouncer_key="${init_result#GENERATED:}" + checkSuccess "Traefik bouncer API key generated" + else + isNotice "Failed to generate bouncer key: $init_result" + isNotice "Traefik integration won't authenticate. Re-run installCrowdsecHost to retry." + fi + + # Mirror the key into the live config file so it's visible / editable via + # the framework's config page like any other CFG_* 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. Skipped when the + # config already agrees, so a normal reinstall writes nothing. + if [[ -n "$bouncer_key" && "$bouncer_key" != "$recorded_key" ]]; then if [[ -f "$cfg_file" ]]; then updateConfigOption "CFG_CROWDSEC_TRAEFIK_LAPI_KEY" "$bouncer_key" "$cfg_file" else isNotice "crowdsec.config not deployed yet — key applied on next install." fi - else - isNotice "Failed to generate bouncer key: $init_result" - isNotice "Traefik integration won't authenticate. Re-run installCrowdsecHost to retry." fi ((menu_number++))