1. The principle #
One plugin and two roles.
Two separate integrations #
Traefik and CrowdSec work together in two ways:
- Detection: CrowdSec reads Traefik access logs thanks to the
crowdsecurity/traefikcollection. - Blocking: the community plugin
crowdsec-bouncer-traefik-pluginqueries the LAPI and returns403to banned addresses.
2. Setting it up #
Static and dynamic configuration.
The key #
docker compose exec crowdsec cscli bouncers add traefik
# ou, côté image CrowdSec : BOUNCER_KEY_traefik=une-cle-longue-et-aleatoireStatic configuration #
entryPoints:
web:
address: ":80"
forwardedHeaders:
trustedIPs: # only if a proxy or CDN is in front of Traefik
- 10.0.0.0/8
accessLog:
filePath: /var/log/traefik/access.log
providers:
file:
filename: /etc/traefik/dynamic.yml
experimental:
plugins:
bouncer:
moduleName: github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin
version: v1.8.1Restart Traefik: it downloads the plugin at startup, so it needs Internet access at that point.
Dynamic configuration #
http:
middlewares:
crowdsec:
plugin:
bouncer:
enabled: true
crowdsecMode: stream
updateIntervalSeconds: 15
crowdsecLapiScheme: http
crowdsecLapiHost: crowdsec:8080
crowdsecLapiKey: YOUR_KEY
forwardedHeadersTrustedIPs:
- 10.0.0.0/8
routers:
app:
rule: "Host(`app.example.com`)"
entryPoints: [web]
service: app
middlewares: [crowdsec]
services:
app:
loadBalancer:
servers:
- url: "http://app:8080"With Docker labels:
services:
whoami:
image: traefik/whoami
labels:
- "traefik.enable=true"
- "traefik.http.routers.whoami.rule=Host(`whoami.localhost`)"
- "traefik.http.routers.whoami.middlewares=crowdsec@docker"
- "traefik.http.middlewares.crowdsec.plugin.bouncer.enabled=true"
- "traefik.http.middlewares.crowdsec.plugin.bouncer.crowdsecmode=stream"
- "traefik.http.middlewares.crowdsec.plugin.bouncer.crowdseclapihost=crowdsec:8080"
- "traefik.http.middlewares.crowdsec.plugin.bouncer.crowdseclapikey=YOUR_KEY"The plugin modes #
| Mode | How it works |
|---|---|
none | One LAPI request per incoming request |
live | Same, with a per-IP cache of defaultDecisionSeconds (60 s). Default mode |
stream | Local cache of banned IPs, synced every updateIntervalSeconds (60 s). Recommended |
alone | No local LAPI: list from the CAPI |
appsec | AppSec inspection only |
3. The real IP trap #
Behind a CDN or load balancer.
The symptom #
Everything seems to work, but a banned IP keeps passing. We reproduced it: without entrypoint trust, Traefik overwrites X-Forwarded-For. The plugin sees the proxy's address, never the client's.
| Tested configuration | Banned IP |
|---|---|
Without forwardedHeaders.trustedIPs on the entrypoint | 200: no block |
With it, in stream mode | 403 after the sync |
| With it, decision deleted | 200 again |
The fix #
You must declare the trusted proxy in two places: forwardedHeaders.trustedIPs on the entrypoint, and forwardedHeadersTrustedIPs in the plugin. Limit these ranges to your real proxies: a client must not be able to spoof its address.
To exclude your internal network from checks, use clientTrustedIPs.
4. Going further #
Detection and WAF.
Reading Traefik logs #
filenames:
- /var/log/traefik/access.log
labels:
type: traefikdocker compose exec crowdsec cscli collections install crowdsecurity/traefik
docker compose exec crowdsec cscli metrics show acquisition parsersEnabling AppSec #
crowdsecAppsecEnabled: true
crowdsecAppsecHost: crowdsec:7422
crowdsecAppsecFailureBlock: true
crowdsecAppsecUnreachableBlock: trueMind availability. With crowdsecAppsecUnreachableBlock: true (the default), the site is blocked if AppSec is unreachable. Choose this behavior knowingly.Sources #
Blocking verified on Traefik 3.7.17, plugin 1.8.1 and CrowdSec 1.8.2.
My progress
Sign in to track your progress and take notes. Sign in
Recommended Articles
CrowdSec bouncers: principles and firewall bouncer
Understanding CrowdSec bouncers: where to block, live and stream modes, API keys, decisions API, ...
CrowdSec bouncers: Cloudflare, network devices, Kubernetes and custom bouncer
Block at the CDN with Cloudflare Workers, feed pfSense or MikroTik with Blocklist Mirror, protect...
CrowdSec bouncers for Nginx, Apache and HAProxy
Setting up CrowdSec bouncers for Nginx, OpenResty, Apache and HAProxy SPOA: configuration, block ...
Les plugins Traefik : l'exemple de CrowdSec pour bloquer les attaques
Étendre Traefik avec des plugins (catalogue officiel, interpréteur Yaegi), puis protéger ses appl...
Decisions, profiles, allowlists and AppSec in CrowdSec
Manage decisions with cscli, adapt block duration and type with profiles, avoid false positives w...
CrowdSec: understanding the collaborative firewall
What CrowdSec is, how it detects and blocks attacks: Security Engine, parsers, scenarios, Local A...
Traefik : sécuriser et transformer les requêtes avec les middlewares (BasicAuth, ForwardAuth…)
Les middlewares Traefik modifient les requêtes et réponses entre le routeur et le service : authe...
CrowdSec observability: banned IPs, blocked attacks, Prometheus and Grafana
Monitoring CrowdSec: cscli metrics, banned IPs, detected attacks, volume blocked by bouncers, Pro...
Traefik : les logs (journaux applicatifs et access logs)
Configurer les deux journaux de Traefik : le journal de fonctionnement (niveaux, format) et les a...