Skip to content

v1.3.35.4 -- Demo: bouncer key bootstrap

A bugfix on top of v1.3.35.3. The v1.3.35.3 release fixed machine credentials for the demo panel by un-bypassing the crowdsec-init sidecar — but the bouncer auth path (a separate CrowdSec auth surface) was still broken. Threats UI calls + AppSec metrics endpoint still returned 403 because the demo's .env had CROWDSEC_BOUNCER_API_KEY=demo-bouncer-key-not-real hardcoded as a placeholder, and that fake key wasn't registered with LAPI.

argosVersion and frontend/package.json bumped from 1.3.35.3 to 1.3.35.4. Image rebuild required.

Why machine creds + bouncer key are different

CrowdSec exposes two auth surfaces:

Surface Used for Auth
Machine credentials Management calls: POST /v1/alerts, GET /v1/decisions (when acting as a watcher) Username + password
Bouncer API key Filter calls: GET /v1/decisions/stream, GET /v1/decisions (the threats UI's enumeration path), AppSec routing API key in X-Api-Key header

The panel uses both. v1.3.35.3 fixed machine creds via the existing crowdsec-init sidecar's cscli machines add flow. The bouncer key path was orthogonal and still broken.

Symptoms (post-v1.3.35.3)

  • cscli machines list showed argos-panel registered ✓ (v1.3.35.3 fix worked).
  • But cscli bouncers list showed only the prod-shaped argos-prod-caddy-bouncer from prod's setup history; no argos-demo-bouncer ever existed.
  • Panel container's CROWDSEC_BOUNCER_API_KEY env was the placeholder string demo-bouncer-key-not-real (whatever init.sh's .env template wrote).
  • Threats UI returned 403; AppSec metrics returned "unavailable, requires machine credentials" (a misleading error — it's the bouncer key that's wrong, not machine creds).
  • cscli bouncers add argos-demo-bouncer had never been invoked anywhere in the demo bring-up flow.

Root cause

Prod's pattern is operator-mediated: at initial deploy the operator runs cscli bouncers add argos-caddy-bouncer once, copies the printed key into ~/argos-prod/.env, and the key persists across container recreations (volume + file persistence). Demo never had an equivalent step — its .env was generated fresh on every init.sh with a placeholder string in the bouncer-key slot.

The crowdsec-init sidecar cannot generate the bouncer key for caddy: caddy reads {env.CROWDSEC_BOUNCER_API_KEY} at process start (Caddyfile placeholder substitution), not via any reconcile loop. So the env var has to be set on the caddy container at boot, which means the key has to be written to .env before docker compose up brings caddy up.

Fix — staged bring-up in init.sh

scripts/demo/init.sh now does a 3-stage bring-up:

stage 1: docker compose up -d crowdsec   (LAPI alone)
         wait for crowdsec healthcheck (cold start: 30-60s)

stage 2: cscli bouncers delete argos-demo-bouncer (idempotency)
         BOUNCER_KEY=$(cscli bouncers add argos-demo-bouncer -o raw)
         sed -i s/^CROWDSEC_BOUNCER_API_KEY=.*/...$BOUNCER_KEY/ .env

stage 3: docker compose up -d            (full stack with key set)

This mirrors prod's pattern (operator runs cscli bouncers add at initial deploy + pastes key into .env) — the demo just automates that one-time manual step. Throw-away nature of the demo means we don't need to preserve keys across runs; delete-then-add gives a fresh key on every init.

-o raw is the key here: cscli's default output mode prints a human-readable banner; raw prints just the key on stdout so the shell capture is clean. The init script sanity-checks the captured key length (≥16 chars) before writing to .env so a silent cscli failure doesn't poison the .env with an empty value.

Smoke phase 3d

scripts/smoke/demo-environment.sh gains a new phase between 3c and 4 with four assertions:

# Check Failure interpretation
3d-i cscli bouncers list contains argos-demo-bouncer Stage 2 of init.sh didn't run / failed
3d-ii Panel container's CROWDSEC_BOUNCER_API_KEY env is non-placeholder Stage 2 didn't update .env, OR caddy/argos started reading the placeholder version
3d-iii GET /v1/decisions (with X-Api-Key from env) returns HTTP 200 Bouncer key is wrong / not registered
3d-iv Zero lapi 403 lines in last 60s of panel logs Catches any remaining 403 from either auth path

Phase 3c's machine-credentials checks remain in place. The two phases together validate both CrowdSec auth surfaces.

Live evidence (post-fix)

$ docker exec argos-demo-crowdsec cscli bouncers list
... argos-demo-bouncer  172.20.0.x  api-key  ...

$ docker exec argos-demo-panel sh -c 'echo ${#CROWDSEC_BOUNCER_API_KEY}'
43

$ docker exec argos-demo-panel sh -c \
    'wget -q -S -O /dev/null \
       --header="X-Api-Key: ${CROWDSEC_BOUNCER_API_KEY}" \
       http://crowdsec:8081/v1/decisions 2>&1 \
     | grep -oE "HTTP/1\.[01] [0-9]+"'
HTTP/1.1 200

$ docker logs argos-demo-panel --since 60s | grep -c 'lapi 403'
0

Files changed

  • scripts/demo/init.sh — single-shot up -d replaced with 3-stage flow (crowdsec → bouncer-add → full stack); idempotent bouncer registration via delete-then-add; key-length sanity check before .env write.
  • scripts/smoke/demo-environment.sh — new phase 3d with 4 assertions covering bouncer registration + env-var presence + decisions endpoint + log-scan.
  • backend/cmd/argos/main.goargosVersion 1.3.35.3 → 1.3.35.4.
  • frontend/package.jsonversion 1.3.35.3 → 1.3.35.4.
  • scripts/demo/docker-compose.override.yml — image pin argos-prod-argos:1.3.35.4.
  • docs/release-notes/v1.3.35.4.md (this file)
  • CHANGELOG.md, mkdocs.yml

Smoke gate

scripts/smoke/demo-environment.sh --yes PASS end-to-end with new phase 3d green. Self-executed against the live host pre-tag.

Upgrade

cd ~/argos-edge
git pull
make sync-prod && make build-prod-image

scripts/demo/teardown.sh --purge   # if a v1.3.35.3 demo is up
scripts/demo/init.sh                # full bring-up with the
                                    # 3-stage bouncer bootstrap

# Verify both auth paths:
docker exec argos-demo-crowdsec cscli bouncers list    # has argos-demo-bouncer
docker exec argos-demo-crowdsec cscli machines list    # has argos-panel
docker exec argos-demo-panel sh -c 'echo ${#CROWDSEC_BOUNCER_API_KEY}'  # >= 30

After login, the surfaces that were 403'ing should now render:

  • /system Health card → recent_errors as array, NOT null.
  • /security/banned (Threats / Banned IPs) → CrowdSec decision list rendered, NO 403 banner.
  • /security/appsec → metrics counters rendered, NOT "unavailable, requires machine credentials".

Bouncer-key handling

The real bouncer key is written ONLY to ~/argos-demo/.env (operator-managed, sync-excluded, never committed). It's generated fresh on every init.sh run; there's no need to persist it across teardown cycles since the volume reset wipes the LAPI side anyway.

scripts/check-no-personal-data.sh clean: no real key in committed sources.

What this enables

Combined with v1.3.35.3 (machine creds) and v1.3.35.2 (production-density seed), the demo stack now renders every panel surface as a fully-functional CrowdSec deployment. The operator can capture the v1.3.34 deferred screenshots without seeing 403 banners or empty metrics on any surface.