
📚 Documentation ðŸ’
Source
💬 Discourse
CrowdSec Remediation QuickStart for Traefik
Objectives​
This quickstart shows how to deploy the CrowdSec Traefik bouncer in Kubernetes and protect workloads exposed through Traefik using a middleware plugin.
At the end, you will have:
- CrowdSec LAPI running in-cluster and reachable from Traefik
- A Traefik middleware enforcing CrowdSec remediation decisions
- A Kubernetes secret storing the shared bouncer key
- An operational pattern that avoids committing the LAPI key in plaintext
Prerequisites​
- It is assumed that you already have:
- A working CrowdSec Security Engine installation. For a Kubernetes install quickstart, refer to /u/getting_started/installation/kubernetes.
- A working Traefik installation in Kubernetes.
- Existing
IngressRoute,Ingress, or other Traefik-managed routes exposing your applications.
Source IPs​
To ensure remediation works correctly, Traefik must receive the real client IP for each request. When Traefik is deployed behind a load balancer, CDN, or another reverse proxy, the source IP may otherwise be replaced with the upstream address.
Traefik must first trust the upstream IP ranges. This is done with:
The CrowdSec middleware must then trust the same ranges:
spec:
plugin:
bouncer:
forwardedHeadersTrustedIps: <trusted-cidr>
If the client IP is forwarded through a header other than X-Forwarded-For,
set it explicitly:
spec:
plugin:
bouncer:
forwardedHeadersCustomName: X-Real-Ip
Correctly forwarding and trusting these headers ensures that both Traefik and CrowdSec operate on the same client IP, which is required for IP-based remediation.
Side note about source IP with CrowdSec and Kubernetes
Source IP addresses are essential in a CrowdSec deployment for two reasons. First, the log processor must know which IPs are responsible for triggering scenarios. Second, the remediation component needs to identify the originating IP of incoming requests in order to apply the appropriate action.
In a Kubernetes environment, this requires disabling source NAT on nodes so
that the CrowdSec-monitored service pods receive the real client IP. As a
consequence, the Service's externalTrafficPolicy must be set to Local, and
the workload must run either as a DaemonSet or as a Deployment ensuring one
pod per node. This guarantees that no traffic, and therefore no security
events, is missed.
Traefik Custom Resource Definitions​
Traefik's CRDs provide the custom resource types, such as Middleware, that
are required to configure Traefik through the Kubernetes CRD provider. CrowdSec
remediation relies on one of these resources to declare the middleware. Without
the CRDs, this middleware cannot be created or used.
- Helm
- Kubectl
Install the Traefik CRDs via Helm:
helm repo add traefik https://traefik.github.io/charts
helm repo update
helm upgrade --install traefik-crds traefik/traefik-crds \
-n traefik \
--create-namespace
You can also deploy the Traefik CRDs without Helm:
kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.6/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml
Enable experimental plugin loading​
The CrowdSec Traefik plugin cannot be enabled only through CLI flags. You must also enable experimental plugin loading in the Traefik chart values:
experimental:
plugins:
crowdsec-bouncer-traefik-plugin:
moduleName: github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin
version: v1.4.5
Apply or upgrade your Traefik release:
helm upgrade --install traefik traefik/traefik \
-n traefik \
--create-namespace \
-f traefik-values.yaml
Store the Traefik bouncer key in a Kubernetes secret​
As with the Envoy guide, the practical approach is to choose a fixed key, store
it in a Kubernetes secret, and force BOUNCER_KEY_traefik from lapi.env with
valueFrom.secretKeyRef.
Create or update the secret used by CrowdSec LAPI:
apiVersion: v1
kind: Secret
metadata:
name: crowdsec-keys
namespace: crowdsec
type: Opaque
stringData:
ENROLL_KEY: "<your-existing-enroll-key>"
BOUNCER_KEY_traefik: "<choose-a-long-random-key>"
Apply it:
kubectl apply -f crowdsec-keys.yaml
Then reference BOUNCER_KEY_traefik from the CrowdSec Helm values:
lapi:
env:
- name: BOUNCER_KEY_traefik
valueFrom:
secretKeyRef:
name: crowdsec-keys
key: BOUNCER_KEY_traefik
Apply the CrowdSec release again:
helm upgrade --install crowdsec crowdsec/crowdsec \
--namespace crowdsec \
--create-namespace \
-f crowdsec-values.yaml
Verify CrowdSec LAPI access​
The Traefik middleware only needs access to CrowdSec LAPI. Make sure the
CrowdSec release exposes the LAPI service and that the bouncer key is available
through lapi.env.
Verify the CrowdSec pods and services:
kubectl -n crowdsec get pods
kubectl -n crowdsec get svc crowdsec-service
You should see:
crowdsec-lapiinRunningcrowdsec-serviceexposing port8080
Deploy the Traefik middleware​
To achieve remediation in a Traefik environment, create a Middleware
resource.
- Templated Manifest
- Inline Manifest
Keep the real key in the crowdsec-keys secret and inject it when you apply
the middleware.
Create a template file:
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: bouncer
namespace: traefik
spec:
plugin:
bouncer:
enabled: true
crowdsecMode: stream
crowdsecLapiScheme: http
crowdsecLapiHost: crowdsec-service.crowdsec.svc.cluster.local:8080
crowdsecLapiPath: /
crowdsecLapiKey: ${BOUNCER_KEY_TRAEFIK}
Then render and apply it from the Kubernetes secret:
export BOUNCER_KEY_TRAEFIK="$(
kubectl -n crowdsec get secret crowdsec-keys \
-o jsonpath='{.data.BOUNCER_KEY_traefik}' | base64 -d
)"
envsubst < bouncer-middleware.tmpl.yaml | kubectl apply -f -
This keeps the source-of-truth key in the Kubernetes secret and avoids storing the literal key in the manifest file.
If you only need a quick functional test, you can apply a middleware manifest with the key inline:
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: bouncer
namespace: traefik
spec:
plugin:
bouncer:
enabled: true
crowdsecMode: stream
crowdsecLapiScheme: http
crowdsecLapiHost: crowdsec-service.crowdsec.svc.cluster.local:8080
crowdsecLapiPath: /
crowdsecLapiKey: <same-value-as-BOUNCER_KEY_traefik>
Apply it:
kubectl apply -f bouncer-middleware.yaml
This is useful for quick validation, but the templated or GitOps-rendered approach is the recommended one for production.
Once the middleware exists, attach it to your IngressRoute or other Traefik
route resources.
Traefik with WAF (AppSec) on Kubernetes​
If you want remediation and WAF protection together, first enable AppSec in the CrowdSec chart while still sourcing the bouncer key from the Kubernetes secret:
config:
config.yaml.local: |
api:
server:
auto_registration:
enabled: true
token: "${REGISTRATION_TOKEN}"
allowed_ranges:
- "127.0.0.1/32"
- "192.168.0.0/16"
- "10.0.0.0/8"
- "172.16.0.0/12"
appsec:
enabled: true
acquisitions:
- source: appsec
listen_addr: "0.0.0.0:7422"
path: /
appsec_configs:
- crowdsecurity/appsec-default
- crowdsecurity/crs
labels:
type: appsec
env:
- name: COLLECTIONS
value: "crowdsecurity/appsec-virtual-patching crowdsecurity/appsec-crs crowdsecurity/appsec-generic-rules"
lapi:
env:
- name: BOUNCER_KEY_traefik
valueFrom:
secretKeyRef:
name: crowdsec-keys
key: BOUNCER_KEY_traefik
If you add this config to the CrowdSec values, don't forget to upgrade the release:
helm upgrade --install crowdsec crowdsec/crowdsec \
--namespace crowdsec \
--create-namespace \
-f crowdsec-values.yaml
Then use an AppSec-enabled middleware template:
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: bouncer
namespace: traefik
spec:
plugin:
bouncer:
enabled: true
crowdsecMode: stream
crowdsecLapiScheme: http
crowdsecLapiHost: crowdsec-service.crowdsec.svc.cluster.local:8080
crowdsecLapiPath: /
crowdsecLapiKey: ${BOUNCER_KEY_TRAEFIK}
crowdsecAppsecEnabled: true
crowdsecAppsecHost: crowdsec-appsec-service.crowdsec.svc.cluster.local:7422
crowdsecAppsecPath: /
crowdsecAppsecFailureBlock: true
crowdsecAppsecUnreachableBlock: true
crowdsecAppsecBodyLimit: 10485760
Render and apply it the same way:
export BOUNCER_KEY_TRAEFIK="$(
kubectl -n crowdsec get secret crowdsec-keys \
-o jsonpath='{.data.BOUNCER_KEY_traefik}' | base64 -d
)"
envsubst < bouncer-middleware.tmpl.yaml | kubectl apply -f -
Validate AppSec​
Test that malicious requests are blocked:
curl -I http://<your-ingress-ip>/.env
# Expected: HTTP/1.1 403 Forbidden