Skip to content

WAF and CrowdSec

Audience

PLANA staff working on edge security or investigating a blocked request.

Two security filters sit on every request that reaches the Envoy Gateway: CrowdSec for IP reputation and Coraza for payload inspection. Both are configured to fail closed — if the filter itself is unreachable, the request is denied.

CrowdSec — IP reputation (ExtAuthz)

CrowdSec is the open-source intrusion-prevention system. It maintains a list of bad-actor IPs (from a community feed plus our own detections) and the Envoy bouncer rejects requests from anyone on the list.

Components

PodRole
crowdsec-lapiLocal API + central decision store
crowdsec-agents (DaemonSet)Watch logs from edge components for attack patterns
crowdsec-bouncerExtAuthz target for Envoy; queries LAPI on every request

All in the crowdsec namespace.

How it decides

For each incoming request:

  1. Envoy ExtAuthz filter calls crowdsec-bouncer:8080/api/v1/decisions?ip=<src>
  2. Bouncer queries LAPI
  3. LAPI returns BAN if the IP is on the list, OK otherwise
  4. Envoy enforces — 403 on BAN

The community feed updates every few hours. Local detections (e.g. "this IP scanned 10 Odoo login attempts in 60s") update in real time.

Configuration

yaml
kind: EnvoyExtensionPolicy
spec:
  extAuthz:
    backendRef: crowdsec-bouncer.crowdsec.svc:8080
    failOpen: false       # fail closed; if bouncer is down, deny

The fail-closed posture is deliberate. After the 2026-05-13 incident where a missing fail-closed flag let CrowdSec outages silently bypass the filter, we audited every filter for this default.

What happens to legitimate users on a banned IP

Rare but real: a corporate NAT, a residential ISP, a shared VPN may end up on the CrowdSec list. Customers who can't reach my.planapulse.ai:

  1. Have them visit https://api.ipify.org to find their public IP
  2. Check Loki for that IP: {namespace="crowdsec-lapi"} |= "<ip>"
  3. If banned, unban via crowdsec CLI: kubectl exec -n crowdsec deploy/crowdsec-lapi -- cscli decisions delete --ip <ip>
  4. Investigate why they were banned (logs at LAPI)

Coraza — WAF (OWASP CRS)

Coraza implements the OWASP ModSecurity Core Rule Set as a Wasm filter inside Envoy. It inspects request bodies, headers, and URLs for known attack patterns:

  • SQL injection
  • Cross-site scripting
  • Path traversal
  • Command injection
  • Suspicious user agents
  • Protocol-level abuses

Configuration

yaml
kind: EnvoyExtensionPolicy
spec:
  wasm:
    image: coraza-waf:owasp-crs
    failOpen: false

CRS-managed rules. Severity 2+ blocks; severity 1 logs only.

Logging

Coraza writes to stdout; promtail ships to Loki. Query blocked requests:

{namespace="envoy-gateway-system"} |= "Coraza" |= "blocked"

Each entry includes the rule ID that triggered, the matched pattern, and the source IP.

False-positive tuning

Some CRS rules over-trigger on legitimate Odoo / pulse-account API calls (Odoo's URL patterns include things that look like SQLi to naive regex). We maintain a small exception list:

yaml
# In coraza-config.yaml
SecRuleRemoveById 942100 942130   # SQLi over-trigger on Odoo views
SecRuleRemoveByTag "OWASP_CRS/WEB_ATTACK/EVASION"

Add exceptions sparingly. Each one widens the attack surface.

What this catches in practice

Sample of blocks in a typical month:

PatternVolume
SQL injection probes (UNION SELECT, 1' OR '1'='1)~100/day
Path traversal (../../etc/passwd)~50/day
Common backdoor probes (/wp-admin/, /admin.php)~200/day
Scanner UAs (Nessus, Nikto, Acunetix)~30/day
Tor exit nodes scanning~20/day

None of these reach the application. The application servers are blissfully unaware.

Where to read more

© PLANA Digital Ltd.