crowdsec: register the Traefik bouncer with LAPI (fix fail-open)
All checks were successful
ci/woodpecker/push/default Pipeline was successful

The Traefik bouncer plugin's API key was never registered with LAPI — the
crowdsec stack reads many keys from Vault but not ingress_crowdsec_api_key, and
the chart registers no bouncer. So LAPI returned 403 to the plugin, which with
updateMaxFailure=-1 failed open and enforced NOTHING: no community-blocklist
bans, and the (now-Turnstile-wired) captcha never fired. cscli bouncers list was
empty; the registration was likely lost in the MySQL->PostgreSQL DB migration
with no IaC to recreate it.

Seed the bouncer at LAPI startup via BOUNCER_KEY_traefik, valued from the same
Vault key the middleware presents — so they match by construction, and the
bouncer re-registers automatically on every LAPI start (survives DB wipes).

- stacks/crowdsec/main.tf: read ingress_crowdsec_api_key, pass to module.
- module main.tf: new sensitive var + thread into the values templatefile.
- values.yaml: BOUNCER_KEY_traefik on lapi.env.
- docs/architecture/security.md: document registration + fail-open history and
  the proxied-app coverage caveat.

Activates enforcement (community blocklist bans + captcha) on non-proxied apps;
internal IPs stay bypassed (clientTrustedIPs), fail-open-on-LAPI-down preserved.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Viktor Barzin 2026-06-19 17:08:28 +00:00
parent 56dadda453
commit a5bb4db9c5
4 changed files with 28 additions and 1 deletions

View file

@ -83,7 +83,17 @@ CrowdSec operates in a hub-and-agent model:
**Traefik Bouncer Plugin** (`crowdsec-bouncer-traefik-plugin`, `stacks/traefik/modules/traefik/middleware.tf`):
- Integrated as Traefik middleware (in the default ingress chain)
- Queries LAPI for IP reputation on each request
- **Registered with LAPI** via `BOUNCER_KEY_traefik` env on the LAPI container
(`stacks/crowdsec/modules/crowdsec/values.yaml`), seeded from the same Vault key
the middleware presents (`ingress_crowdsec_api_key`). **Before 2026-06-19 the
bouncer was never registered → LAPI returned 403 → the plugin failed open and
enforced nothing (no bans, no captcha).** The seed re-registers automatically on
every LAPI start, so a DB wipe (e.g. the MySQL→PostgreSQL migration that lost the
original registration) can't silently disable enforcement again.
- **Fail-open mode**: If LAPI unreachable, allows traffic (graceful degradation)
- **Only sees non-proxied (direct) apps' real client IPs** (ETP=Local). Proxied
apps arrive from cloudflared's pod IP (in `clientTrustedIPs`) and are bypassed —
extending enforcement to proxied apps needs `forwardedHeadersTrustedIPs` (future).
- Honours two LAPI remediation types (profiles in `stacks/crowdsec/modules/crowdsec/values.yaml`):
- **`ban`** → HTTP 403 (serious attacks: CVE exploits, scanners, brute force)
- **`captcha`** → **Cloudflare Turnstile challenge** so the flagged user can