Self-Hosted Services with CrowdSec and Traefik
Protect Self-Hosted Services with CrowdSec and Traefik.
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
Basic familiarity with Docker and YAML
Step 1 - Deploy CrowdSec ContainerStep 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 metricsView detection metrics
docker exec crowdsec cscli decisions listList active bans
docker exec crowdsec cscli decisions delete --ip X.X.X.XUnban an IP
docker exec crowdsec cscli alerts listView recent alerts
docker exec crowdsec cscli bouncers listList registered bouncers
docker exec crowdsec cscli collections listList installed collections
docker exec crowdsec cscli hub updateUpdate threat definitions
docker exec crowdsec cscli explain --log "..." --type traefikDebug 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
