feat: integrate CrowdSec with Traefik and add deployment instructions

This commit is contained in:
2026-07-19 21:15:17 +08:00
parent 54d486264b
commit 256cf5de86
7 changed files with 163 additions and 17 deletions
+7
View File
@@ -84,6 +84,13 @@ AUTHENTIK_EMAIL__FROM=authentik@localhost
COMPOSE_PORT_HTTP=80
COMPOSE_PORT_HTTPS=443
# CrowdSec (Swarm)
# Generate a value with: openssl rand -base64 32
CROWDSEC_LAPI_KEY=change-me
# Must match a bouncer key registered in CrowdSec, for example:
# cscli bouncers add traefik-bouncer --key <value>
CROWDSEC_BOUNCER_KEY=change-me
#=====================================================================#
# LibreChat Configuration #
#=====================================================================#
+50
View File
@@ -44,6 +44,56 @@ make deploy-core
docker service logs core_traefik --details
```
## CrowdSec Runbook (Swarm + Traefik)
### 1) Set keys in swarm env
In `swarm/.env`, set:
```env
CROWDSEC_LAPI_KEY=<openssl rand -base64 32>
CROWDSEC_BOUNCER_KEY=<openssl rand -base64 32>
```
### 2) Deploy CrowdSec stack
```bash
make deploy-crowdsec
```
### 3) Register the Traefik bouncer
```bash
make register-crowdsec-bouncer
```
### 4) (Re)deploy Traefik core
This ensures the global HTTPS forwardAuth middleware is active.
```bash
make deploy-core
```
### 5) Verify health and wiring
```bash
docker service ls | grep crowdsec
docker service logs --since 10m crowdsec_traefik-bouncer
docker service logs --since 10m core_traefik
docker ps --filter label=com.docker.swarm.service.name=crowdsec_crowdsec
```
### 6) Verify decisions and blocking
```bash
# Show parser/scenario metrics
docker exec -it $(docker ps --filter label=com.docker.swarm.service.name=crowdsec_crowdsec -q | head -n1) cscli metrics
# Add a temporary test decision for your client IP and verify HTTPS returns 403
docker exec -it $(docker ps --filter label=com.docker.swarm.service.name=crowdsec_crowdsec -q | head -n1) cscli decisions add --ip <YOUR_TEST_IP>
```
Cleanup test decision when done:
```bash
docker exec -it $(docker ps --filter label=com.docker.swarm.service.name=crowdsec_crowdsec -q | head -n1) cscli decisions delete --ip <YOUR_TEST_IP>
```
### Notes:
```yaml
# Declaring the user list
+31
View File
@@ -33,6 +33,37 @@ deploy-core: pull
remove-core:
docker stack rm core
setup-crowdsec:
{ \
echo "Setting up crowdsec config on host..." ;\
mkdir -p /var/data/crowdsec ;\
cmp -s ./swarm/crowdsec/acquis.yaml /var/data/crowdsec/acquis.yaml 2>/dev/null || cp ./swarm/crowdsec/acquis.yaml /var/data/crowdsec/acquis.yaml ;\
}
deploy-crowdsec: pull setup-crowdsec
{ \
echo "Deploying the crowdsec stack..." ;\
set -a ;\
. ./swarm/.env ;\
set +a ;\
docker stack deploy --compose-file ./swarm/crowdsec.yml crowdsec ;\
}
register-crowdsec-bouncer:
{ \
echo "Registering crowdsec traefik bouncer..." ;\
set -a ;\
. ./swarm/.env ;\
set +a ;\
bash ./scripts/register-crowdsec-bouncer.sh ;\
}
deploy-crowdsec-all: deploy-crowdsec register-crowdsec-bouncer deploy-core
@echo "CrowdSec + bouncer registration + core deploy completed."
remove-crowdsec:
docker stack rm crowdsec
deploy-portainer: pull
{ \
echo "Deploying the portainer stack..." ;\
+33
View File
@@ -0,0 +1,33 @@
#!/usr/bin/env bash
set -euo pipefail
BOUNCER_NAME="${CROWDSEC_BOUNCER_NAME:-traefik-bouncer}"
BOUNCER_KEY="${CROWDSEC_BOUNCER_KEY:-}"
SERVICE_NAME="crowdsec_crowdsec"
if [[ -z "$BOUNCER_KEY" ]]; then
echo "CROWDSEC_BOUNCER_KEY is not set. Export it or source swarm/.env first."
exit 1
fi
if ! command -v docker >/dev/null 2>&1; then
echo "docker command not found in PATH."
exit 1
fi
container_id="$(docker ps --filter "label=com.docker.swarm.service.name=${SERVICE_NAME}" --format '{{.ID}}' | head -n1)"
if [[ -z "$container_id" ]]; then
echo "No running CrowdSec LAPI task found for service ${SERVICE_NAME}."
echo "Deploy CrowdSec first: make deploy-crowdsec"
exit 1
fi
if docker exec "$container_id" cscli bouncers list 2>/dev/null | grep -q "$BOUNCER_NAME"; then
echo "Bouncer '${BOUNCER_NAME}' is already registered."
exit 0
fi
echo "Registering bouncer '${BOUNCER_NAME}' on ${SERVICE_NAME}..."
docker exec "$container_id" cscli bouncers add "$BOUNCER_NAME" --key "$BOUNCER_KEY"
echo "Bouncer registration complete."
+7 -2
View File
@@ -91,6 +91,8 @@ services:
- traefik.http.middlewares.xbot.headers.stsIncludeSubdomains=true
- traefik.http.middlewares.xbot.headers.permissionsPolicy="accelerometer=(), ambient-light-sensor=(), autoplay=(), battery=(), camera=(), display-capture=(), document-domain=(), encrypted-media=(), execution-while-not-rendered=(), execution-while-out-of-viewport=(), fullscreen=(), geolocation=(), gyroscope=(), layout-animations=(), legacy-image-formats=(), magnetometer=(), microphone=(), midi=(), oversized-images=(), payment=(), picture-in-picture=(), publickey-credentials-get=(), sync-xhr=(), usb=(), wake-lock=(), xr-spatial-tracking=()"
- traefik.http.middlewares.xbot.headers.referrerPolicy="no-referrer"
- traefik.http.middlewares.crowdsec-bouncer.forwardauth.address=http://traefik-bouncer:8080/api/v1/forwardAuth
- traefik.http.middlewares.crowdsec-bouncer.forwardauth.trustForwardHeader=true
- treafik.http.middlewares.neo4j_strip.stripprefix.prefixes=/neo4j
# traefik-https the actual router using HTTPS
- traefik.http.routers.traefik-public-https.rule=Host(`dashboard.${DOMAIN?Variable not set}`)
@@ -106,8 +108,9 @@ services:
- traefik.http.services.traefik-public.loadbalancer.server.port=8080
# Pass the original Host header to the backend
# - "traefik.http.services.dashboard.loadbalancer.passhostheader=true"
# - crowdsec.enable=true
# - crowdsec.labels.type=nginx
# CrowdSec log acquisition for Traefik is label-based via docker source.
- crowdsec.enable=true
- crowdsec.labels.type=traefik
volumes:
# Add Docker as a mounted volume, so that Traefik can read the labels of other services
- /var/run/docker.sock:/var/run/docker.sock:ro
@@ -132,6 +135,8 @@ services:
- --entrypoints.http.address=:80
# Create an entrypoint "https" listening on port 443
- --entrypoints.https.address=:443
# Apply CrowdSec bouncer globally to all HTTPS routers discovered by Traefik.
- --entrypoints.https.http.middlewares=crowdsec-bouncer@swarm
# Redirect HTTP to HTTPS
- --entrypoints.http.http.redirections.entrypoint.to=https
- --entrypoints.http.http.redirections.entrypoint.scheme=https
+32 -15
View File
@@ -1,27 +1,38 @@
services:
crowdsec:
image: crowdsecurity/crowdsec:slim
restart: always
networks:
crowdsec:
- crowdsec-internal
environment:
DOCKER_HOST: tcp://socket-proxy:2375
COLLECTIONS: "crowdsecurity/nginx"
ports:
- "127.0.0.1:8080:8080"
depends_on:
- 'socket-proxy'
COLLECTIONS: "crowdsecurity/traefik"
CUSTOM_CS_LAPI_KEY: ${CROWDSEC_LAPI_KEY:?Variable not set}
deploy:
restart_policy:
condition: any
volumes:
- ./crowdsec/acquis.yaml:/etc/crowdsec/acquis.yaml
- /var/data/crowdsec/acquis.yaml:/etc/crowdsec/acquis.yaml:ro
- crowdsec-db:/var/lib/crowdsec/data/
- crowdsec-config:/etc/crowdsec/
socket-proxy:
traefik-bouncer:
image: crowdsecurity/traefik-bouncer:latest
environment:
CROWDSEC_LAPI_URL: http://crowdsec:8080
CROWDSEC_LAPI_KEY: ${CROWDSEC_BOUNCER_KEY:?Variable not set}
expose:
- "8080"
networks:
crowdsec:
restart: always
- crowdsec-internal
- traefik-public
deploy:
restart_policy:
condition: any
socket-proxy:
image: lscr.io/linuxserver/socket-proxy:latest
container_name: socket-proxy
networks:
- crowdsec-internal
environment:
INFO: 1
CONTAINERS: 1
@@ -38,10 +49,13 @@ services:
PLUGINS: 0
SERVICES: 0
SESSION: 0
SWARM: 0
SWARM: 1
SYSTEM: 0
TASKS: 0
VOLUMES: 0
deploy:
restart_policy:
condition: any
volumes:
- '/var/run/docker.sock:/var/run/docker.sock:ro'
read_only: true
@@ -53,5 +67,8 @@ volumes:
crowdsec-config:
networks:
crowdsec:
driver: bridge
crowdsec-internal:
driver: overlay
attachable: true
traefik-public:
external: true
+3
View File
@@ -1,3 +1,6 @@
source: docker
use_container_labels: true ## https://docs.crowdsec.net/docs/next/data_sources/docker#use_container_labels
# Traefik service must set these docker labels so CrowdSec can classify logs correctly:
# - crowdsec.enable=true
# - crowdsec.labels.type=traefik
check_interval: 10s ## How often to check for new containers https://docs.crowdsec.net/docs/next/appsec/quickstart/traefik