CrowdSec Integration & Security Logging
Connect RouteWarden to CrowdSec to automatically turn blocked reconnaissance scans into immediate firewall bans across your entire infrastructure.
Attacker Probes /.env ──► RouteWarden Blocks & Emits JSON ──► CrowdSec Parses & Bans Attacker IPWhy Pair RouteWarden with CrowdSec?
CrowdSec typically detects attacks by tailing standard web access logs and waiting for multiple suspicious requests to cross a threshold. While effective for broad traffic, this means vulnerability scanners still get multiple chances to probe your services before getting blocked.
Pairing RouteWarden directly with CrowdSec changes this dynamic:
| Feature | Standard Log Analysis | RouteWarden + CrowdSec |
|---|---|---|
| First-Request Neutralization | Requires multiple requests to cross threshold | Intercepted on the very first probe before reaching upstream backends |
| Ban Speed | Typically requires 5–10 requests | Immediate ban triggered on sensitive file reconnaissance |
| Attacker Experience | Standard 403 Forbidden | Choice of deception: fake .env credentials, slow tarpits, or connection drops |
| Parsing Overhead | CrowdSec must parse every web request | CrowdSec only processes discrete routewarden_block security events |
| Anti-Evasion Normalization | Complex URL-encoded paths can slip past naive regex | Evaluates canonical paths cleaned of double encoding, matrix params, and backslashes |
How It Works
- Interception: When a client requests a protected endpoint (such as
/.env,/.git/config, or/dump.sql) or sends a blocked query string, RouteWarden intercepts the request according to your configured response mode (json,html,fakeSuccess,silentDrop, etc.). - Structured Event Emission: Along with the client response, RouteWarden emits a single-line JSON audit event to
stdout:
{ "type": "routewarden_block", "timestamp": "2026-09-19T15:20:00Z", "plugin": "routewarden", "client_ip": "198.51.100.42", "method": "GET", "path": "/.env", "request_uri": "/.env", "pattern": "(?i)(^|/)(\.env.*)$", "action": "fakeSuccess", "reason": "path_blocked", "user_agent": "Mozilla/5.0 (compatible; Nuclei/v3.1.0)"}- CrowdSec Parsing: The custom RouteWarden parser ingests this structured event and extracts the client IP, probed path, HTTP method, and matched pattern.
- Instant Remediation: The scenario flags the probe as high-confidence reconnaissance and immediately instructs your CrowdSec bouncers (firewall, iptables, Cloudflare) to ban the offending IP.
Setup Walkthrough
Step 1: Install the RouteWarden CrowdSec Parser
Create /etc/crowdsec/parsers/s01-parse/routewarden-logs.yaml:
# /etc/crowdsec/parsers/s01-parse/routewarden-logs.yamlonsuccess: next_stagename: routewarden/parserdescription: "Parse RouteWarden security block events from Traefik & Caddy"filter: "evt.Line.Raw contains 'routewarden_block'"nodes: - grok: pattern: '.*(?P<json_raw>\{"type":"routewarden_block".*\})' apply_on: Line.Rawstatics: - meta: log_type value: routewarden_block - meta: source_ip expression: 'JsonExtract(evt.Parsed.json_raw, "client_ip")' - meta: http_path expression: 'JsonExtract(evt.Parsed.json_raw, "path")' - meta: http_method expression: 'JsonExtract(evt.Parsed.json_raw, "method")' - meta: http_user_agent expression: 'JsonExtract(evt.Parsed.json_raw, "user_agent")' - meta: routewarden_pattern expression: 'JsonExtract(evt.Parsed.json_raw, "pattern")' - meta: routewarden_action expression: 'JsonExtract(evt.Parsed.json_raw, "action")'Step 2: Install the RouteWarden Threat Scenario
Create /etc/crowdsec/scenarios/routewarden-threat.yaml:
# /etc/crowdsec/scenarios/routewarden-threat.yamltype: triggername: routewarden/sensitive-endpoint-scandescription: "Ban IPs probing sensitive paths intercepted by RouteWarden"filter: "evt.Meta.log_type == 'routewarden_block'"blackhole: 1hlabels: type: scan remediation: true service: http confidence: 3 spoofable: 0 behavior: "http:probing"scope: type: ip expression: evt.Meta.source_ipStep 3: Configure CrowdSec Log Acquisition (acquis.yaml)
CrowdSec reads logs through an acquisition datasource configured in /etc/crowdsec/acquis.yaml. You can ingest RouteWarden logs via Docker container logs or directly from local log files on disk.
# /etc/crowdsec/acquis.yaml# Ingest directly from Traefik container stdout/stderrsource: dockercontainer_name: - traefiklabels: type: routewardenHow to direct Gateway output to a log file
If you choose file-based acquisition, configure your gateway to write logs to disk:
# Static Traefik Configuration (traefik.yml)log: level: INFO filePath: "/var/log/traefik/traefik.log" format: commonTIP
Docker Volume Sharing for File-Based Acquisition: If CrowdSec runs inside a Docker container while reading a file from the host, ensure the log directory is mounted in both containers:
volumes: - /var/log/traefik:/var/log/traefik:roStep 4: Configure the Gateway (Traefik, Caddy & NGINX)
Security logging is enabled by default (securityLog: true / security_log true).
http: middlewares: routewarden-shield: plugin: routewarden: enabled: true # Emits structured JSON events on stdout for CrowdSec securityLog: true enableDefaultPatterns: true # Deceive attackers with convincing dummy credentials response: mode: fakeSuccess statusCode: 200Complete Docker Compose Example
Here is a practical Docker Compose setup running your preferred gateway with RouteWarden, CrowdSec, and a protected web container:
services: traefik: image: traefik:v3.1 container_name: traefik command: - "--api.insecure=true" - "--providers.docker=true" - "--providers.docker.exposedbydefault=false" - "--entrypoints.web.address=:80" - "--experimental.plugins.routewarden.modulename=github.com/routewarden/traefik-warden" - "--experimental.plugins.routewarden.version=v1.2.1" ports: - "80:80" - "8080:8080" volumes: - /var/run/docker.sock:/var/run/docker.sock:ro restart: unless-stopped crowdsec: image: crowdsecurity/crowdsec:latest container_name: crowdsec environment: COLLECTIONS: "crowdsecurity/traefik crowdsecurity/http-cve" volumes: - ./crowdsec/acquis.yaml:/etc/crowdsec/acquis.yaml:ro - ./crowdsec/parsers:/etc/crowdsec/parsers/s01-parse:ro - ./crowdsec/scenarios:/etc/crowdsec/scenarios:ro - /var/run/docker.sock:/var/run/docker.sock:ro - crowdsec-db:/var/lib/crowdsec/data/ restart: unless-stopped web: image: nginx:alpine container_name: web labels: - "traefik.enable=true" - "traefik.http.routers.web.rule=PathPrefix(`/`)" - "traefik.http.routers.web.middlewares=sec-shield" - "traefik.http.middlewares.sec-shield.plugin.routewarden.enabled=true" - "traefik.http.middlewares.sec-shield.plugin.routewarden.securityLog=true" - "traefik.http.middlewares.sec-shield.plugin.routewarden.response.mode=json"volumes: crowdsec-db:Testing the Integration
1. Send a Test Probe
Simulate a vulnerability scanner probing for exposed configuration files:
curl -i -H "User-Agent: Nuclei/v3.1.0" http://localhost/.env2. Verify Structured Security Log Emission
Check Traefik or Caddy output for the security block record:
docker logs traefik | grep routewarden_blockExample JSON output:
{"action":"json","client_ip":"172.18.0.1","method":"GET","path":"/.env","pattern":"(?i)(^|/)(\.env.*|.*\.(txt|log|bak|backup|sql|conf|config|ini|yaml|yml))$","plugin":"routewarden","reason":"path_blocked","request_uri":"/.env","timestamp":"2026-09-19T15:30:12Z","type":"routewarden_block","user_agent":"Nuclei/v3.1.0"}3. Check Active CrowdSec Decisions
Verify that CrowdSec parsed the event and issued a ban:
# View trigger alertsdocker exec -t crowdsec cscli alerts list# View active firewall remediation decisionsdocker exec -t crowdsec cscli decisions listThe client IP is now banned by CrowdSec across all attached bouncers.
SIEM and Log Shipper Ingestion
Because RouteWarden emits single-line JSON with type: "routewarden_block", you can easily pipe these logs into your existing observability stack without custom grok patterns:
- Elasticsearch / Filebeat: Ingest with standard JSON processors and enrich
client_ipwith GeoIP data. - Grafana Loki / Promtail: Parse with
{type="routewarden_block"} | jsonfor instant dashboard metrics and alerts. - Datadog / Splunk: Automatic JSON facet extraction for
pattern,action,reason, anduser_agent.