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 listshowedargos-panelregistered ✓ (v1.3.35.3 fix worked).- But
cscli bouncers listshowed only the prod-shapedargos-prod-caddy-bouncerfrom prod's setup history; noargos-demo-bouncerever existed. - Panel container's
CROWDSEC_BOUNCER_API_KEYenv was the placeholder stringdemo-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-bouncerhad 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-shotup -dreplaced with 3-stage flow (crowdsec → bouncer-add → full stack); idempotent bouncer registration via delete-then-add; key-length sanity check before.envwrite.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.go—argosVersion1.3.35.3 → 1.3.35.4.frontend/package.json—version1.3.35.3 → 1.3.35.4.scripts/demo/docker-compose.override.yml— image pinargos-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:
/systemHealth card →recent_errorsas 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.