tests: add local Docker end-to-end suite (Traefik + Crowdsec)

A per-scenario end-to-end suite that boots real Traefik + Crowdsec containers
and exercises the plugin loaded from a local path. Scenarios: stream / live /
none modes, trusted IPs, custom ban page, captcha, and appsec.

This suite is local-only (run with `make e2e`): it boots a real Crowdsec and,
for appsec, downloads the OWASP CRS collections — heavier and less
deterministic than CI needs. CI runs a separate, lighter binary + mock-LAPI
suite instead (see the companion PR).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
mhx
2026-06-06 12:04:08 +02:00
co-authored by Claude Opus 4.8
parent 71d845faae
commit 66b57496f8
20 changed files with 880 additions and 1 deletions
+6
View File
@@ -0,0 +1,6 @@
listen_addr: 0.0.0.0:7422
appsec_config: crowdsecurity/crs-inband
name: e2eAppSec
source: appsec
labels:
type: appsec
@@ -0,0 +1,55 @@
services:
traefik:
image: traefik:v3.7.1
container_name: e2e-appsec-traefik
command:
- "--log.level=INFO"
- "--accesslog"
- "--api.insecure=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entryPoints.web.address=:80"
- "--entryPoints.web.forwardedHeaders.insecure=true"
- "--experimental.localplugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ../../../..:/plugins-local/src/github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin
ports:
- "8000:80"
depends_on:
crowdsec:
condition: service_healthy
whoami:
image: traefik/whoami
container_name: e2e-appsec-whoami
labels:
- "traefik.enable=true"
- "traefik.http.routers.r.rule=PathPrefix(`/foo`)"
- "traefik.http.routers.r.entrypoints=web"
- "traefik.http.routers.r.middlewares=bouncer@docker"
- "traefik.http.services.s.loadbalancer.server.port=80"
- "traefik.http.middlewares.bouncer.plugin.bouncer.enabled=true"
# IP bouncing disabled — we only test AppSec body inspection here.
- "traefik.http.middlewares.bouncer.plugin.bouncer.crowdsecmode=none"
- "traefik.http.middlewares.bouncer.plugin.bouncer.crowdseclapikey=40796d93c2958f9e58345514e67740e5"
- "traefik.http.middlewares.bouncer.plugin.bouncer.crowdsecappsecenabled=true"
- "traefik.http.middlewares.bouncer.plugin.bouncer.crowdsecappsechost=crowdsec:7422"
- "traefik.http.middlewares.bouncer.plugin.bouncer.forwardedheaderstrustedips=172.16.0.0/12"
crowdsec:
image: crowdsecurity/crowdsec:v1.7.8
container_name: crowdsec
environment:
COLLECTIONS: "crowdsecurity/appsec-crs-inband"
BOUNCER_KEY_TRAEFIK: 40796d93c2958f9e58345514e67740e5
CUSTOM_HOSTNAME: crowdsec
CROWDSEC_BYPASS_DB_VOLUME_CHECK: "true"
volumes:
- ./acquis.yaml:/etc/crowdsec/acquis.yaml:ro
healthcheck:
test: ["CMD", "cscli", "lapi", "status"]
interval: 2s
timeout: 3s
retries: 60
start_period: 15s
+40
View File
@@ -0,0 +1,40 @@
#!/usr/bin/env bash
set -euo pipefail
HERE="$(cd "$(dirname "$0")" && pwd)"
# shellcheck source=../../lib/common.sh
source "$HERE/../../lib/common.sh"
SCENARIO=appsec
PROJECT="e2e-${SCENARIO}"
COMPOSE_FILE="$HERE/docker-compose.yml"
LOG_FILE="/tmp/e2e-${SCENARIO}.log"
cleanup() {
local rc=$?
if (( rc != 0 )); then
dump_diagnostics "$PROJECT" "$COMPOSE_FILE" > "$LOG_FILE" 2>&1 || true
echo "Scenario failed. Logs written to $LOG_FILE"
fi
docker compose -p "$PROJECT" -f "$COMPOSE_FILE" down -v --remove-orphans >/dev/null 2>&1 || true
exit $rc
}
trap cleanup EXIT
echo "[$SCENARIO] starting stack (AppSec collections download — may take ~30s on first boot)..."
docker compose -p "$PROJECT" -f "$COMPOSE_FILE" up -d --wait
echo "[$SCENARIO] waiting for crowdsec readiness..."
wait_crowdsec_ready crowdsec 180
echo "[$SCENARIO] waiting for traefik readiness..."
wait_for_status http://localhost:8000/foo 200 30
echo "[$SCENARIO] benign request must pass"
assert_status http://localhost:8000/foo 200
echo "[$SCENARIO] SQL-injection-like query string must be blocked by OWASP CRS (inband)"
# Classic SQLi probe: ?id=1' OR '1'='1 — caught by CRS rule 942100/942130 (paranoia 1).
assert_status "http://localhost:8000/foo?id=1%27%20OR%20%271%27%3D%271" 403
echo "[$SCENARIO] OK"