Traefik + Fail2Ban: The “Black Hole” Integration
This setup monitors Traefik container logs and injects DROP rules into the DOCKER-USER iptables chain. By using a “Black Hole” approach, we discard malicious packets at the kernel level before they ever reach your application code.
Prerequisites
- Traefik: v3.x+ in Docker with access logs enabled.
- Host Tools: Fail2Ban (v0.11+) and
iptableson the host OS. - Mounts: Traefik log directory must be volume-mounted (e.g.,
./logs:/var/log/traefik).
Step 1: Configure Traefik (JSON Format)
JSON is the gold standard for Fail2Ban. It ensures that a bot with a weird User-Agent (like one containing [ or ]) doesn’t break your regex.
traefik.yml:
accessLog:
filePath: "/var/log/traefik/access.log"
format: json
bufferingSize: 0 # Instant logging for immediate protection
Step 2: Define the “Zero Tolerance” Filter
We will split this into two parts: standard 4xx monitoring and Instant Ban paths.
Filter (/etc/fail2ban/filter.d/traefik-bot.conf):
Definition]
# 1. Standard 4xx errors on sensitive paths
# 2. Direct-to-IP Access (Replace YOUR_VPS_IP with your actual IP)
failregex = ^.*"ClientAddr":"<HOST>:\d+".*?"DownstreamStatus":(401|403|404|429).*?"RequestPath":"/(\.env|\.git|wp-login|setup\.php|config\.php|admin/config\.php|shell).*".*$
^.*"ClientAddr":"<HOST>:\d+".*?"RequestAddr":.*"YOUR_VPS_IP:\d+",.*$
# Critical fix for JSON timestamp parsing
datepattern = ^.*"StartLocal":"<DATE>.*"
ignoreregex = ^.*"request_User-Agent":".*(Googlebot|Bingbot).*",.*$
Step 3: Create the Jail
We target the DOCKER-USER chain to ensure the ban happens before Docker’s internal routing (NAT).
Jail (/etc/fail2ban/jail.d/traefik-bot.local):
[traefik-bot]
enabled = true
port = http,https
filter = traefik-bot
logpath = /var/log/traefik/access.log
maxretry = 3
findtime = 15m
bantime = 24h
# The "Black Hole" Action
action = iptables-allports[chain="DOCKER-USER"]
Step 4: High-Efficiency Log Rotation
Large logs cause Fail2Ban to spike CPU. We rotate at 100MB and signal Traefik to start a fresh file.
/etc/logrotate.d/traefik:
/var/log/traefik/*.log {
daily
rotate 14
maxsize 100M
missingok
compress
delaycompress
postrotate
# Signal Traefik to release the file handle
docker kill --signal=USR1 traefik >/dev/null 2>&1
# Tell Fail2Ban to refresh its tailing of the new file
fail2ban-client reload traefik-bot >/dev/null 2>&1
endscript
}
Step 5: The “Safe-Check” Validation Script
Run this script to see exactly what Fail2Ban would catch before you commit to the bans.
**Create test-traefik-f2b.sh**:
#!/bin/bash
LOG_FILE="/var/log/traefik/access.log"
FILTER_FILE="/etc/fail2ban/filter.d/traefik-bot.conf"
echo "--- Testing Regex Match Count ---"
# This should now show 'Failregex: X total' instead of 0
fail2ban-regex "$LOG_FILE" "$FILTER_FILE"
echo -e "\n--- Direct IP Hits Count ---"
# Check how many people are hitting your IP directly
grep "RequestAddr" "$LOG_FILE" | grep -v "yourdomain.com" | wc -l
Requires
jq(sudo apt install jq).
Verification & Monitoring
- Live Jail Status:
sudo fail2ban-client status traefik-bot - Check the Wall:
sudo iptables -L DOCKER-USER -n -v(Look for theDROPtarget). - Manual Unban:
sudo fail2ban-client set traefik-bot unbanip <IP>
Common Troubleshooting
| Problem | Solution |
|---|---|
| IPs aren’t being dropped | Ensure the action is iptables-allports and the chain is exactly DOCKER-USER. |
| Logs are empty after rotation | Verify your Traefik container is actually named traefik. If not, update the docker kill command. |
| I banned myself! | Add your IP to the ignoreip line in the jail config: ignoreip = 127.0.0.1/8 ::1 123.123.123.123. |