13 KiB
created, updated
| created | updated |
|---|---|
| 2026-03-13 | 2026-03-13 |
CrowdSec Setup with Traefik
Overview
CrowdSec protects all services behind Traefik by analyzing access logs and blocking malicious IPs. Combined with disabling Gitea SSH (port 2222), all public traffic flows through Traefik where CrowdSec can inspect and block it.
Internet (Port 80/443 only)
↓
Router Port Forwarding
↓
┌──────────────────────────────────────────┐
│ Traefik │
│ ┌──────────────────────────────┐ │
│ │ CrowdSec Bouncer Middleware │ ← blocks│
│ └──────────────┬───────────────┘ │
│ │ allowed traffic │
└─────────────────┼────────────────────────┘
traefik_proxy network
┌─────────┼─────────┐
↓ ↓ ↓
Gitea Servarr Future
Services
CrowdSec Engine ← reads Traefik access logs
↓
Community Blocklists (optional)
Prerequisites
- Working Traefik setup (see Traefik Multi-Stack Setup)
- Router port forwarding for 80/443 only (remove 2222)
Step 1: Disable Gitea SSH and Switch to HTTPS Git
1.1 Close Port 2222
Remove the port 2222 forwarding rule from your router. This immediately stops all SSH brute-force attempts.
1.2 Remove SSH Port from Gitea Stack
Edit /mnt/tank/stacks/gitea/docker-compose.yml:
- Remove the
portssection (or just the2222:22mapping) - Optionally add
GITEA__server__DISABLE_SSH=true
services:
gitea:
image: gitea/gitea:latest
container_name: gitea
restart: unless-stopped
environment:
- USER_UID=1000
- USER_GID=1000
- GITEA__database__DB_TYPE=sqlite3
- GITEA__server__DOMAIN=git.vv.nl
- GITEA__server__ROOT_URL=https://git.vv.nl
- GITEA__server__DISABLE_SSH=true
- GITEA__service__DISABLE_REGISTRATION=true
networks:
- traefik_proxy
- gitea_internal
volumes:
- ./data:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
# No ports exposed - Traefik handles HTTPS, SSH disabled
labels:
- "traefik.enable=true"
- "traefik.docker.network=traefik_proxy"
- "traefik.http.routers.gitea.rule=Host(`git.vv.nl`)"
- "traefik.http.routers.gitea.entrypoints=websecure"
- "traefik.http.routers.gitea.tls.certresolver=cloudflare"
- "traefik.http.services.gitea.loadbalancer.server.port=3000"
1.3 Restart Gitea
cd /mnt/tank/stacks/gitea
docker compose down
docker compose up -d
1.4 Set Up HTTPS Git Authentication
On Gitea, create a personal access token:
- Go to
https://git.vv.nl/user/settings/applications - Create a new token with
repositoryscope - Save the token securely
On your local machines, configure git to use HTTPS:
# Update existing remotes from SSH to HTTPS
git remote set-url origin https://git.vv.nl/username/repo.git
# Store credentials so you don't have to enter the token every time
git config --global credential.helper store
# On first push/pull, use your Gitea username and the token as password
git push origin main
# Username: your-gitea-username
# Password: your-personal-access-token
Alternatively, use the token directly in the URL (less secure, stored in git config):
git remote set-url origin https://username:TOKEN@git.vv.nl/username/repo.git
Step 2: Enable Traefik Access Logs
CrowdSec needs Traefik's access logs to detect malicious behavior. Edit /mnt/tank/stacks/traefik/traefik.yml and add:
accessLog:
filePath: "/var/log/traefik/access.log"
bufferingSize: 100
# ... rest of your existing config (api, entryPoints, providers, certificatesResolvers)
Then update /mnt/tank/stacks/traefik/docker-compose.yml to mount the log directory:
services:
traefik:
# ... existing config ...
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./traefik.yml:/traefik.yml:ro
- ./letsencrypt:/letsencrypt
- traefik-logs:/var/log/traefik # Add this line
volumes:
traefik-logs:
name: traefik-logs # Named volume, shared with CrowdSec
Restart Traefik:
cd /mnt/tank/stacks/traefik
docker compose down
docker compose up -d
Step 3: Deploy CrowdSec
Since CrowdSec is tightly coupled to Traefik (reads its logs, provides its middleware), it lives in the Traefik stack.
3.1 Create CrowdSec Acquis Config
Create the log acquisition config:
mkdir -p /mnt/tank/stacks/traefik/crowdsec
Create /mnt/tank/stacks/traefik/crowdsec/acquis.yaml:
filenames:
- /var/log/traefik/*
labels:
type: traefik
3.2 Add CrowdSec to Traefik Stack
Edit /mnt/tank/stacks/traefik/docker-compose.yml to add the CrowdSec engine and bouncer:
version: '3.8'
networks:
traefik_proxy:
name: traefik_proxy
driver: bridge
volumes:
traefik-logs:
name: traefik-logs
crowdsec-db:
name: crowdsec-db
crowdsec-config:
name: crowdsec-config
services:
traefik:
image: traefik:v2.10
container_name: traefik
restart: unless-stopped
depends_on:
- crowdsec-bouncer
security_opt:
- no-new-privileges:true
networks:
- traefik_proxy
ports:
- "80:80"
- "443:443"
environment:
- TZ=Europe/Amsterdam
- CF_DNS_API_TOKEN=your-cloudflare-api-token
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./traefik.yml:/traefik.yml:ro
- ./letsencrypt:/letsencrypt
- traefik-logs:/var/log/traefik
labels:
- "traefik.enable=true"
# Dashboard
- "traefik.http.routers.traefik.rule=Host(`traefik.vv.nl`)"
- "traefik.http.routers.traefik.entrypoints=websecure"
- "traefik.http.routers.traefik.tls.certresolver=cloudflare"
- "traefik.http.routers.traefik.service=api@internal"
- "traefik.http.routers.traefik.middlewares=traefik-auth,crowdsec@docker"
- "traefik.http.middlewares.traefik-auth.basicauth.users=admin:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/"
crowdsec:
image: crowdsecurity/crowdsec:latest
container_name: crowdsec
restart: unless-stopped
networks:
- traefik_proxy
environment:
- TZ=Europe/Amsterdam
# Install the Traefik log parser and HTTP scenarios
- COLLECTIONS=crowdsecurity/traefik crowdsecurity/http-cve crowdsecurity/base-http-scenarios
# Enroll with CrowdSec Console (optional, get key from app.crowdsec.net)
# - ENROLL_KEY=your-enrollment-key
volumes:
- crowdsec-db:/var/lib/crowdsec/data
- crowdsec-config:/etc/crowdsec
- traefik-logs:/var/log/traefik:ro # Read Traefik logs
- ./crowdsec/acquis.yaml:/etc/crowdsec/acquis.yaml:ro
crowdsec-bouncer:
image: fbonalair/traefik-crowdsec-bouncer:latest
container_name: crowdsec-bouncer
restart: unless-stopped
depends_on:
- crowdsec
networks:
- traefik_proxy
environment:
- CROWDSEC_BOUNCER_API_KEY=GENERATE_THIS # See step 3.3
- CROWDSEC_AGENT_HOST=crowdsec:8080
- GIN_MODE=release
labels:
- "traefik.enable=true"
# Define the CrowdSec middleware (forwardAuth to the bouncer)
- "traefik.http.middlewares.crowdsec.forwardauth.address=http://crowdsec-bouncer:8080/api/v1/forwardAuth"
- "traefik.http.middlewares.crowdsec.forwardauth.trustForwardHeader=true"
3.3 Generate the Bouncer API Key
First, bring up CrowdSec alone to generate the API key:
cd /mnt/tank/stacks/traefik
# Start only CrowdSec first
docker compose up -d crowdsec
# Wait a few seconds for it to initialize, then generate a bouncer API key
docker exec crowdsec cscli bouncers add traefik-bouncer
# Copy the generated API key and put it in the docker-compose.yml
# Replace GENERATE_THIS with the actual key
Then start everything:
docker compose up -d
3.4 Verify CrowdSec is Working
# Check CrowdSec is parsing logs
docker exec crowdsec cscli metrics
# Check installed collections
docker exec crowdsec cscli collections list
# Check active decisions (bans)
docker exec crowdsec cscli decisions list
# Check bouncer is registered
docker exec crowdsec cscli bouncers list
Step 4: Apply CrowdSec Middleware to All Services
For each service behind Traefik, add crowdsec@docker to its middleware chain.
Gitea
In /mnt/tank/stacks/gitea/docker-compose.yml, add the middleware label:
labels:
- "traefik.enable=true"
- "traefik.docker.network=traefik_proxy"
- "traefik.http.routers.gitea.rule=Host(`git.vv.nl`)"
- "traefik.http.routers.gitea.entrypoints=websecure"
- "traefik.http.routers.gitea.tls.certresolver=cloudflare"
- "traefik.http.routers.gitea.middlewares=crowdsec@docker" # Add this
- "traefik.http.services.gitea.loadbalancer.server.port=3000"
Any Future Service
Same pattern - add crowdsec@docker to the middlewares label:
- "traefik.http.routers.service-name.middlewares=crowdsec@docker"
Multiple Middlewares
Chain middlewares with commas:
- "traefik.http.routers.traefik.middlewares=traefik-auth,crowdsec@docker"
Step 5: Optional - Enroll in CrowdSec Console
The CrowdSec Console at app.crowdsec.net provides:
- Community blocklists (block known-bad IPs before they hit your server)
- Dashboard to visualize attacks
- Alert notifications
To enroll:
- Create a free account at
https://app.crowdsec.net - Get your enrollment key from the console
- Add to docker-compose.yml:
ENROLL_KEY=your-key - Or enroll manually:
docker exec crowdsec cscli console enroll your-key
Management Commands
# View current bans
docker exec crowdsec cscli decisions list
# Manually ban an IP
docker exec crowdsec cscli decisions add --ip 176.120.22.17 --reason "SSH brute force"
# Unban an IP (if you accidentally ban yourself)
docker exec crowdsec cscli decisions delete --ip YOUR_IP
# View metrics (parsed logs, scenarios triggered)
docker exec crowdsec cscli metrics
# View alerts
docker exec crowdsec cscli alerts list
# Update CrowdSec hub (scenarios, parsers)
docker exec crowdsec cscli hub update
docker exec crowdsec cscli hub upgrade
Troubleshooting
Bouncer Not Blocking
# Verify bouncer is registered
docker exec crowdsec cscli bouncers list
# Check bouncer logs
docker logs crowdsec-bouncer
# Test with a manual ban on a test IP
docker exec crowdsec cscli decisions add --ip 1.2.3.4 --reason "test"
# Then try to access your services from that IP
docker exec crowdsec cscli decisions delete --ip 1.2.3.4
CrowdSec Not Parsing Logs
# Check if acquisition is working
docker exec crowdsec cscli metrics
# Look for "traefik" in the acquisition metrics
# If zero lines parsed, check the log path and acquis.yaml
# Verify Traefik is writing logs
docker exec traefik ls -la /var/log/traefik/
# Check CrowdSec logs for errors
docker logs crowdsec
Locked Out of Your Own Services
If you accidentally ban your own IP:
# SSH into TrueNAS (SSH is on the host, not through Traefik)
ssh user@truenas-local-ip
# Remove the ban
docker exec crowdsec cscli decisions delete --ip YOUR_IP
# Whitelist your IP to prevent future bans
docker exec crowdsec cscli parsers install crowdsecurity/whitelists
# Edit the whitelist config
docker exec -it crowdsec vi /etc/crowdsec/parsers/s02-enrich/whitelists.yaml
To permanently whitelist your IP, create /mnt/tank/stacks/traefik/crowdsec/whitelist.yaml:
name: mywhitelists
description: "My personal whitelist"
whitelist:
reason: "My home/office IP"
ip:
- "YOUR_HOME_IP"
# Add any other trusted IPs
Mount it in the CrowdSec container:
volumes:
- ./crowdsec/whitelist.yaml:/etc/crowdsec/parsers/s02-enrich/mywhitelists.yaml:ro
Port Forwarding (Updated)
After this setup, your router should only forward:
| External Port | Internal Port | Protocol | Service |
|---|---|---|---|
| 80 | 80 | TCP | Traefik HTTP (redirects to HTTPS) |
| 443 | 443 | TCP | Traefik HTTPS (CrowdSec protected) |
Port 2222 is no longer forwarded.
Architecture Summary
- All public traffic enters through ports 80/443 only
- Traefik terminates TLS and routes requests
- CrowdSec bouncer middleware checks every request against the ban list
- CrowdSec engine continuously parses Traefik access logs for malicious patterns
- Community blocklists proactively block known-bad IPs
- No direct port exposure for any backend service
Related
- Traefik Multi-Stack Setup - Base Traefik configuration
- Security TODO - Other security improvements
- Quick Reference - Common management commands