secubox-deb/packages/secubox-mitmproxy/crowdsec
CyberMind-FR d4e093a4db feat(secubox-mitmproxy): Phase 7.A WAF→CrowdSec bridge — alerts push → nft drop (ref #498)
User : 'plan for later a waf with dropping result of attackants...
threats accumulate looks very high'. Phase 7.A delivers the foundation :
WAF detections become real nft drops within seconds.

## E2E live-verified on gk2

  login    : 200 JWT 160 chars
  alert    : 201 ← decision id 13244
  cscli    : Ip:192.0.2.99 ban origin=secubox-waf 1h
  nft      : 192.0.2.99 in table ip crowdsec (kernel-level drop)

Full chain (mitm LXC → host LAPI → bouncer → nft) round-trip in ~12s.

## Architecture

  [secubox_waf.py in mitm LXC]
       │  detects threat via pattern match
       │  if count >= BAN_THRESHOLD
       │     ↓
       │  ban_ip() → _ban_via_crowdsec()
       │     ↓
       │  HTTP POST /v1/alerts (urllib stdlib, no httpx dep)
       │     ↓
  [socat 10.100.0.1:8080 → 127.0.0.1:8080]   ← Phase 7.A.1 bridge
       │     ↓
  [CrowdSec LAPI on host 127.0.0.1:8080]
       │     ↓
  [crowdsec-firewall-bouncer (existing)]
       │     ↓
  [nft add element ip crowdsec ...]
       │     ↓
  [attacker IP dropped at netdev/forward chain]

## Files

### addons/secubox_waf.py
  New constants CROWDSEC_CFG_FILE, _CS_JWT, _CS_JWT_TTL.
  New _load_crowdsec_cfg() reads TOML, validates 4 required fields.
  New _cs_jwt(cfg) caches JWT for 25 min (LAPI default 30 min expiry).
  New _ban_via_crowdsec() POSTs Alert+Decision to /v1/alerts with
    Bearer JWT. urllib stdlib only (mitm venv lacks httpx). On 401,
    forces JWT refresh next call. Stats : bans_pushed, bans_failed.
  Modified ban_ip() : tries HTTP bridge first, falls back to cscli
    subprocess if bridge disabled. Old subprocess path kept for hosts
    where WAF runs natively (cscli available).

### bin/secubox-waf-cs-bridge-setup
  Installs socat forwarder unit secubox-cs-bridge.service so the mitm
  LXC can reach the host loopback LAPI without changing CrowdSec config.
  Registers secubox-waf as a MACHINE (watcher) via cscli machines add
  with -f - so the local agent's credentials file stays untouched.
  Writes /tmp/crowdsec.toml with machine_id + machine_password.
  Operator copies into LXC at /etc/secubox/waf/crowdsec.toml.

### crowdsec/crowdsec.toml.example
  Updated to document machine_id / machine_password (not the old
  bouncer api_key — bouncers can only PULL decisions, not push them).

## Why machine auth (not bouncer)

CrowdSec LAPI splits routes by auth type :
  - X-Api-Key (bouncer) → GET /v1/decisions/stream (read-only)
  - JWT (watcher)       → POST /v1/alerts (write)
  - JWT (watcher)       → DELETE /v1/decisions

To PUSH a ban, the WAF must auth as a watcher. We register a dedicated
machine 'secubox-waf' so its decisions show up in the dashboard tagged
with origin=secubox-waf (vs origin=crowdsec for scenario-driven bans
and origin=cscli for manual ones).

## Socat bridge rationale

Default CrowdSec install binds LAPI to 127.0.0.1:8080. Direct patching
of listen_uri to 10.100.0.1 breaks the local agent + bouncer (they hard-
code 127.0.0.1 in their credential files). A 30-line socat forwarder
service exposes the same LAPI on the LXC bridge gateway, gated by the
host firewall (only LXCs can reach 10.100.0.1). Zero CrowdSec config
change.

## Acceptance criteria (issue #498 Phase 7.A target)

  1. Attacker dropped at nft within 30s of crossing BAN_THRESHOLD ✓
  2. cscli decisions list shows origin=secubox-waf entries           ✓
  3. nft list table ip crowdsec shows the dropped IPs                ✓
  4. threat_counts dict growth bounded                               ✓ (still
     local memory, but attacker IPs now drop before refilling it)

## Phase 7.A.2+ (followups in TODO.md)

  - Backport into packages/secubox-waf/mitmproxy/secubox_waf.py
  - debian/postinst : invoke setup script + bind-mount config into LXC
  - WAF dashboard tab in admin webui showing bans_pushed counter
  - Tune BAN_THRESHOLD per category (XSS=2, SQLi=1, scanner pattern=5)
  - Phase 7.B : nft rate-limit pre-mitm to catch TCP-only scanners
2026-06-05 17:08:35 +02:00
..
crowdsec.toml.example feat(secubox-mitmproxy): Phase 7.A WAF→CrowdSec bridge — alerts push → nft drop (ref #498) 2026-06-05 17:08:35 +02:00
secubox-waf.yaml feat(/data Phase 1): migrate 5 core packages from /srv to /data with postinst auto-mv (closes #319 Phase 1) (#320) 2026-05-21 16:32:41 +02:00