Introduction
CrowdSec is an open-source, collaborative intrusion detection system that analyzes logs, detects attacks, and shares threat intelligence with the community. Combined with Traefik, it provides real-time protection against:
- Brute-force attacks (SSH, HTTP authentication, database)
- Web application attacks (SQL injection, XSS, path traversal)
- Vulnerability scanners and automated bots
- Known malicious IPs from the CrowdSec community blocklist
This tutorial covers:
- Deploying CrowdSec as a Docker container alongside Coolify
- Integrating CrowdSec with Traefik to block malicious HTTP requests
- Installing the firewall bouncer to protect SSH and other services
- Creating a custom parser for Supabase Supavisor to detect database brute-force attacks
Prerequisites
- A server running Coolify with Traefik proxy
- SSH access to a server you want to monitor (e.g. a Supabase server)
- Basic familiarity with Docker and YAML
Step 1 - Deploy CrowdSec Container
Step 1.1 - Create directory structure
SSH into your server (e.g. Supabase server) and create the CrowdSec directories:
mkdir -p /opt/crowdsec/data
mkdir -p /opt/crowdsec/configStep 1.2 - Create Docker Compose file
cat > /opt/crowdsec/docker-compose.yml << 'EOF'
services:
crowdsec:
image: crowdsecurity/crowdsec:latest
container_name: crowdsec
restart: unless-stopped
ports:
- "127.0.0.1:8180:8080" # LAPI for bouncers (localhost only)
- "0.0.0.0:6060:6060" # Metrics (restrict via UFW)
security_opt:
- no-new-privileges:true
environment:
- GID=1000
- COLLECTIONS=crowdsecurity/traefik crowdsecurity/http-cve crowdsecurity/whitelist-good-actors crowdsecurity/base-http-scenarios crowdsecurity/sshd crowdsecurity/linux
volumes:
# CrowdSec data and config
- /opt/crowdsec/data:/var/lib/crowdsec/data
- /opt/crowdsec/config:/etc/crowdsec
# Log sources to analyze
- /var/log/traefik:/var/log/traefik:ro
- /var/log/auth.log:/var/log/auth.log:ro
- /var/log/syslog:/var/log/syslog:ro
# Docker socket for container log acquisition
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
- coolify
networks:
coolify:
external: true
EOFStep 1.3 - Start CrowdSec
cd /opt/crowdsec
docker compose up -dWait approximately 30 seconds for CrowdSec to initialize and download collections.
Step 2 - Configure Log Acquisition
CrowdSec needs to know which logs to analyze and how to parse them.
Step 2.1 - Configure log sources
cat > /opt/crowdsec/config/acquis.yaml << 'EOF'
---
filenames:
- /var/log/auth.log
- /var/log/syslog
labels:
type: syslog
---
filenames:
- /var/log/traefik/*.log
labels:
type: traefik
---
# Supavisor logs (Docker socket acquisition)
# Uses regex to match Coolify's dynamic container naming
source: docker
container_name_regexp:
- "supabase-supavisor-.*"
labels:
type: supavisor
EOFStep 2.2 - Restart CrowdSec
docker restart crowdsecStep 3 - Create Custom Supavisor Parser
Supavisor (Supabase's connection pooler) logs authentication failures, but CrowdSec doesn't have a built-in parser for it. Let's create one.
Step 3.1 - Create the parser
cat > /opt/crowdsec/config/parsers/s01-parse/supavisor-logs.yaml << 'EOF'
name: crowdsecurity/supavisor-logs
description: "Parse Supavisor connection pooler logs for authentication failures"
filter: "evt.Parsed.program == 'supavisor'"
onsuccess: next_stage
debug: false
# Supavisor uses Elixir Logger format with metadata
# Example log:
# 18:38:17.778 project=dev_tenant user=postgres region=local mode=transaction type=single app_name=psql peer_ip=123.123.123.123 [error] ClientHandler: Exchange error: "Wrong password" when method :auth_query
pattern_syntax:
SUPAVISOR_TS: '%{TIME:timestamp}\.%{INT:timestamp_ms}'
SUPAVISOR_LEVEL: '\[%{WORD:log_level}\]'
SUPAVISOR_META_FULL: 'project=%{DATA:project}\s+user=%{DATA:db_user}\s+region=%{DATA:region}\s+mode=%{DATA:pool_mode}\s+type=%{DATA:pool_type}\s+app_name=%{DATA:app_name}\s+peer_ip=%{IP:source_ip}'
SUPAVISOR_META_PARTIAL: "region=%{DATA:region}"
nodes:
# Pattern 1: Wrong password authentication failure
- grok:
pattern: '%{SUPAVISOR_TS}\s+%{SUPAVISOR_META_FULL}\s+%{SUPAVISOR_LEVEL}\s+ClientHandler:\s+Exchange error:\s+"Wrong password"%{GREEDYDATA}'
apply_on: Line.Raw
statics:
- meta: log_type
value: supavisor_auth_fail
- meta: service
value: supavisor
- meta: source_ip
expression: evt.Parsed.source_ip
# Pattern 2: SSL required error
- grok:
pattern: '%{SUPAVISOR_TS}\s+%{SUPAVISOR_META_FULL}\s+%{SUPAVISOR_LEVEL}\s+ClientHandler:\s+Tenant is not allowed to connect without SSL%{GREEDYDATA}'
apply_on: Line.Raw
statics:
- meta: log_type
value: supavisor_ssl_required
- meta: service
value: supavisor
- meta: source_ip
expression: evt.Parsed.source_ip
# Pattern 3: Generic exchange error with peer_ip
- grok:
pattern: '%{SUPAVISOR_TS}\s+%{SUPAVISOR_META_FULL}\s+%{SUPAVISOR_LEVEL}\s+ClientHandler:\s+Exchange error:%{GREEDYDATA:error_detail}'
apply_on: Line.Raw
statics:
- meta: log_type
value: supavisor_auth_fail
- meta: service
value: supavisor
- meta: source_ip
expression: evt.Parsed.source_ip
# Pattern 4: Any error with peer_ip (fallback)
- grok:
pattern: '%{SUPAVISOR_TS}\s+%{SUPAVISOR_META_FULL}\s+%{SUPAVISOR_LEVEL}\s+%{GREEDYDATA:error_message}'
apply_on: Line.Raw
filter: "evt.Parsed.log_level == 'error'"
statics:
- meta: log_type
value: supavisor_error_with_ip
- meta: service
value: supavisor
- meta: source_ip
expression: evt.Parsed.source_ip
statics:
- meta: service
value: supavisor
EOFStep 3.2 - Create the brute-force scenario
cat > /opt/crowdsec/config/scenarios/supavisor-bf.yaml << 'EOF'
type: leaky
name: crowdsecurity/supavisor-bf
description: "Detect brute force attacks against PostgreSQL via Supavisor connection pooler"
filter: evt.Meta.log_type == 'supavisor_auth_fail'
groupby: evt.Meta.source_ip
capacity: 5
leakspeed: 30s
blackhole: 5m
labels:
service: supavisor
confidence: 3
spoofable: 0
classification:
- attack.T1110
behavior: "database:bruteforce"
label: "Supavisor bruteforce"
remediation: true
EOFThis scenario triggers a ban when an IP makes 5 failed authentication attempts within 30 seconds.
Step 3.3 - Restart CrowdSec
docker restart crowdsecStep 4 - Integrate CrowdSec with Traefik
The Traefik bouncer plugin checks incoming requests against CrowdSec's decision database and blocks banned IPs.
Step 4.1 - Generate bouncer API key
docker exec crowdsec cscli bouncers add traefik-bouncerSave this API key — you'll need it in the next step.
Example output:
API key for 'traefik-bouncer':
aBcDeFgHiJkLmNoPqRsTuVwXyZ123456
Please keep this key since you will not be able to retrieve it!Step 4.2 - Update Traefik configuration in Coolify
In Coolify → Server → Proxy tab, update the Traefik configuration.
Add the CrowdSec plugin to commands:
command:
# ... existing commands ...
- "--accesslog=true"
- "--accesslog.filepath=/var/log/traefik/access.log"
- "--accesslog.format=json"
- "--accesslog.fields.headers.defaultmode=drop"
- "--accesslog.fields.headers.names.User-Agent=keep"
# CrowdSec Bouncer Plugin
- "--experimental.plugins.crowdsec.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
- "--experimental.plugins.crowdsec.version=v1.4.6"Step 4.3 - Create CrowdSec middleware configuration
On your server (e.g. Supabase server):
Replace
<YOUR-BOUNCER-API-KEY>with your actual key.
cat > /data/coolify/proxy/dynamic/crowdsec.yml << 'EOF'
http:
middlewares:
crowdsec:
plugin:
crowdsec:
enabled: true
crowdsecMode: live
crowdsecLapiKey: "<YOUR-BOUNCER-API-KEY>"
crowdsecLapiHost: "crowdsec:8080"
crowdsecLapiScheme: http
forwardedHeadersTrustedIPs:
- 10.0.0.0/8
- 172.16.0.0/12
- 192.168.0.0/16
clientTrustedIPs:
- 10.0.0.0/8
- 172.16.0.0/12
- 192.168.0.0/16
EOFNote: Traefik automatically watches the
/data/coolify/proxy/dynamic/directory and loads changes without restart.
Step 4.4 - Enable middleware on HTTPS entrypoint
In Coolify → Server → Proxy → labels section:
labels:
# ... existing labels ...
- traefik.http.routers.traefik.middlewares=crowdsec@fileStep 4.5 - Save and restart
Click Save and then Restart Proxy.
Step 5 - Install Firewall Bouncer
The Traefik bouncer only protects HTTP traffic. To protect SSH and database ports, install the firewall bouncer.
Step 5.1 - Add CrowdSec repository
curl -s https://install.crowdsec.net | sudo shStep 5.2 - Install nftables bouncer
apt install crowdsec-firewall-bouncer-nftables -yStep 5.3 - Generate API key
docker exec crowdsec cscli bouncers add firewall-bouncerStep 5.4 - Configure the bouncer
Replace <YOUR-FIREWALL-BOUNCER-API-KEY> with your actual key.
cat > /etc/crowdsec/bouncers/crowdsec-firewall-bouncer.yaml << 'EOF'
mode: nftables
pid_dir: /var/run/
update_frequency: 10s
daemonize: true
log_mode: file
log_dir: /var/log/
log_level: info
log_compression: true
log_max_size: 100
log_max_backups: 3
log_max_age: 30
api_url: http://127.0.0.1:8180/
api_key: <YOUR-FIREWALL-BOUNCER-API-KEY>
insecure_skip_verify: false
disable_ipv6: false
deny_action: DROP
deny_log: false
supported_decisions_types:
- ban
nftables:
ipv4:
enabled: true
set-only: false
table: crowdsec
chain: crowdsec-chain
priority: -10
ipv6:
enabled: true
set-only: false
table: crowdsec6
chain: crowdsec6-chain
priority: -10
nftables_hooks:
- input
- forward
EOFStep 5.5 - Enable and start
systemctl enable crowdsec-firewall-bouncer
systemctl start crowdsec-firewall-bouncerStep 6 - Configure Log Rotation
Prevent Traefik logs from filling the disk:
cat > /etc/logrotate.d/traefik << 'EOF'
/var/log/traefik/*.log {
daily
rotate 14
compress
delaycompress
missingok
notifempty
create 0644 root root
postrotate
docker kill --signal="USR1" coolify-proxy 2>/dev/null || true
endscript
}
EOFStep 7 - Verify CrowdSec Status
Step 7.1 - Check container status
docker ps | grep crowdsecStep 7.2 - View metrics
docker exec crowdsec cscli metricsStep 7.3 - Check registered bouncers
docker exec crowdsec cscli bouncers listExpected output showing both bouncers:
Name IP Address Valid Last API pull Type Version
traefik-bouncer 172.x.x.x :heavy_check_mark: 2024-01-01T12:00:00Z Go-http
firewall-bouncer 127.0.0.1 :heavy_check_mark: 2024-01-01T12:00:00Z Go-httpStep 7.4 - View active decisions (bans)
docker exec crowdsec cscli decisions listStep 7.5 - Check firewall bouncer status
systemctl status crowdsec-firewall-bouncerStep 8 - Test the Setup
Step 8.1 - Manually add a test ban
Ban a test IP temporarily:
docker exec crowdsec cscli decisions add --ip 192.0.2.1 --duration 5m --type banStep 8.2 - Verify the ban
docker exec crowdsec cscli decisions listStep 8.3 - Remove the test ban
docker exec crowdsec cscli decisions delete --ip 192.0.2.1Quick Reference - CrowdSec Commands
| Command | Description |
|---|---|
docker exec crowdsec cscli metrics |
View detection metrics |
docker exec crowdsec cscli decisions list |
List active bans |
docker exec crowdsec cscli decisions delete --ip X.X.X.X |
Unban an IP |
docker exec crowdsec cscli alerts list |
View recent alerts |
docker exec crowdsec cscli bouncers list |
List registered bouncers |
docker exec crowdsec cscli collections list |
List installed collections |
docker exec crowdsec cscli hub update |
Update threat definitions |
docker exec crowdsec cscli explain --log "..." --type traefik |
Debug log parsing |
Conclusion
Your server now has comprehensive intrusion detection and prevention:
- ✅ HTTP traffic protected via Traefik bouncer
- ✅ SSH and direct connections protected via firewall bouncer
- ✅ Automatic detection of brute-force attacks, vulnerability scans, and web exploits
- ✅ Custom detection for Supabase Supavisor database attacks
- ✅ Participation in CrowdSec's community threat intelligence network
Next steps:
- Set up centralized monitoring — see "Centralized Security Monitoring with Prometheus and Grafana"
- Enroll in CrowdSec Console for cloud-based management: https://app.crowdsec.net
- Review and customize scenarios based on your traffic patterns