Fail2Ban Docker nghe đơn giản nhưng hay cấu hình sai ở production: ban IP trong INPUT chain nhưng traffic vào container lại đi qua FORWARD/NAT, khiến bot vẫn chạm được Nginx hoặc service publish port. Cách ổn hơn là để Fail2Ban chạy trên host, đọc log thật của Nginx/SSH, rồi ban IP qua chain DOCKER-USER trước khi Docker forward packet vào container.

Khi nào cần Fail2Ban cho Docker?
Nếu server đang publish Nginx reverse proxy, app panel, SSH hoặc API public ra Internet, bạn sẽ thấy log 404 scan, brute-force, request quá tốc độ hoặc thử path nhạy cảm. Nginx limit_req giúp giảm request rate ở layer HTTP, còn Fail2Ban giúp chặn IP ở firewall khi hành vi xấu lặp lại.
Bài này dùng bối cảnh Ubuntu/Debian host chạy Docker Engine, Nginx container hoặc Nginx Proxy Manager có log mount ra host. Nếu bạn đang dùng Nginx native, phần jail vẫn áp dụng, chỉ cần đổi logpath.
Vấn đề chính: Docker bỏ qua một phần firewall thông thường
Docker tạo rule firewall để NAT và forward traffic cho container. Tài liệu Docker lưu ý không nên sửa rule Docker tự tạo, và Docker/ufw có thể không tương thích như nhiều người nghĩ: traffic vào port publish có thể bị route trước khi chạm rule ufw INPUT. Vì vậy nếu chỉ ban ở INPUT chain, bạn có thể tưởng đã chặn nhưng container vẫn nhận traffic.
DOCKER-USER là chain được Docker dành cho rule do admin thêm vào trước khi packet đi tiếp vào chain Docker quản lý. Đây là nơi phù hợp để Fail2Ban chèn rule drop/reject cho IP xấu.
Bước 1: cài Fail2Ban trên Docker host
Với Ubuntu/Debian:
sudo apt update
sudo apt install -y fail2ban
sudo systemctl enable --now fail2ban
fail2ban-client version
Không nên chạy Fail2Ban “mỗi container một bản” nếu mục tiêu là bảo vệ toàn host. Chạy trên host giúp quản lý firewall nhất quán, đặc biệt cho SSH và các port publish.
Bước 2: đảm bảo Nginx log nằm trên host
Fail2Ban chỉ ban được khi đọc được log. Với Docker Compose, mount log Nginx ra host:
services:
nginx:
image: nginx:1.27-alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d:ro
- /var/log/docker-nginx:/var/log/nginx
Nếu dùng Nginx Proxy Manager, log thường nằm trong volume/data của NPM. Hãy xác định đúng file access/error log trước khi viết jail.
Bước 3: cấu hình banaction dùng DOCKER-USER
Tạo action riêng để chèn rule vào DOCKER-USER. Ví dụ với iptables:
sudo tee /etc/fail2ban/action.d/docker-user.conf >/dev/null <<'EOF'
[Definition]
actionstart = iptables -N f2b-<name> 2>/dev/null || true
iptables -C DOCKER-USER -j f2b-<name> 2>/dev/null || iptables -I DOCKER-USER -j f2b-<name>
iptables -C f2b-<name> -j RETURN 2>/dev/null || iptables -A f2b-<name> -j RETURN
actionstop = iptables -D DOCKER-USER -j f2b-<name> 2>/dev/null || true
iptables -F f2b-<name> 2>/dev/null || true
iptables -X f2b-<name> 2>/dev/null || true
actioncheck = iptables -n -L DOCKER-USER >/dev/null
actionban = iptables -I f2b-<name> 1 -s <ip> -j DROP
actionunban = iptables -D f2b-<name> -s <ip> -j DROP
EOF
Cảnh báo: nếu host dùng nftables backend hoặc distro custom firewall, hãy kiểm tra backend trước. Đừng flush toàn bộ iptables/nftables trên server production vì có thể làm rớt kết nối và phá rule Docker.
Bước 4: jail cho SSH
SSH thường nên bảo vệ trước. Với systemd journal:
sudo tee /etc/fail2ban/jail.d/sshd-local.conf >/dev/null <<'EOF'
[sshd]
enabled = true
backend = systemd
port = ssh
maxretry = 5
findtime = 10m
bantime = 1h
EOF
sudo fail2ban-client reload
sudo fail2ban-client status sshd
SSH không nhất thiết phải dùng DOCKER-USER nếu SSH chạy native trên host; jail mặc định thường đủ. DOCKER-USER quan trọng hơn với service publish qua Docker.
Bước 5: Nginx rate limit + Fail2Ban
Nginx có module ngx_http_limit_req_module dùng thuật toán leaky bucket để giới hạn request theo key như IP. Ví dụ:
# trong http {}
limit_req_zone $binary_remote_addr zone=login_per_ip:10m rate=5r/m;
limit_req_status 429;
limit_req_log_level warn;
server {
location /wp-login.php {
limit_req zone=login_per_ip burst=10 nodelay;
proxy_pass http://wordpress:80;
}
}
Sau đó để Fail2Ban đọc error log Nginx và ban IP nào vượt rate nhiều lần:
sudo tee /etc/fail2ban/filter.d/nginx-limit-req-local.conf >/dev/null <<'EOF'
[Definition]
failregex = limiting requests, excess: .* by zone "[^\"]+", client: <HOST>,
ignoreregex =
EOF
sudo tee /etc/fail2ban/jail.d/nginx-limit-req-local.conf >/dev/null <<'EOF'
[nginx-limit-req-local]
enabled = true
filter = nginx-limit-req-local
logpath = /var/log/docker-nginx/error.log
maxretry = 10
findtime = 10m
bantime = 1h
action = docker-user[name=nginx-limit-req-local]
EOF
sudo fail2ban-regex /var/log/docker-nginx/error.log /etc/fail2ban/filter.d/nginx-limit-req-local.conf
sudo fail2ban-client reload
sudo fail2ban-client status nginx-limit-req-local
Nếu bạn đang dựng reverse proxy cho container, có thể xem thêm bài Caddy reverse proxy cho Docker hoặc Linux logrotate cho Nginx, Docker và systemd để quản lý log bền hơn.
Kiểm tra ban có thật sự vào DOCKER-USER không
sudo fail2ban-client set nginx-limit-req-local banip 203.0.113.10
sudo iptables -S DOCKER-USER
sudo iptables -S f2b-nginx-limit-req-local
sudo fail2ban-client set nginx-limit-req-local unbanip 203.0.113.10
Nếu rule không xuất hiện, kiểm tra tên action, quyền Fail2Ban, backend firewall và việc chain DOCKER-USER tồn tại. Với Docker mới hoặc cấu hình nftables, cần điều chỉnh action tương ứng.
Troubleshooting nhanh
- Không match log: chạy
fail2ban-regexvới một dòng log thật, đừng đoán regex. - Ban rồi nhưng bot vẫn vào: rule có thể nằm sai chain; kiểm tra
DOCKER-USER. - Không thấy IP thật: reverse proxy/CDN có thể làm log chỉ thấy IP proxy; cần cấu hình real IP đúng.
- ufw đã deny nhưng container vẫn mở: đây là hành vi thường gặp do Docker NAT trước ufw INPUT.
- Log quá lớn: cấu hình rotate, tham khảo bài logrotate đã link ở trên.
FAQ về Fail2Ban Docker
Có nên chạy Fail2Ban trong container không?
Có thể, nhưng với production đơn giản hơn là chạy Fail2Ban trên host để quản lý SSH, Nginx log và firewall tập trung. Container Fail2Ban cần mount log, capability mạng và cấu hình cẩn thận.
DOCKER-USER khác INPUT chain thế nào?
INPUT xử lý traffic vào chính host. Traffic đến port publish của container thường đi qua forward/NAT, nên rule trong DOCKER-USER phù hợp hơn để chặn trước khi packet vào Docker chain.
Fail2Ban có thay thế Nginx rate limit không?
Không. Nginx rate limit xử lý tốc độ request ở HTTP layer; Fail2Ban dùng log để ban IP lặp lại hành vi xấu ở firewall layer. Hai lớp này bổ sung cho nhau.
Dùng Cloudflare thì có cần Fail2Ban không?
Vẫn cần nếu origin còn public hoặc có SSH/API không qua Cloudflare. Nếu traffic web chỉ qua Cloudflare, hãy cấu hình allowlist Cloudflare IP và real IP header trước khi viết rule ban.
Kết luận
Fail2Ban Docker hiệu quả nhất khi bạn đọc đúng log, match đúng hành vi và ban ở đúng chain. Với service publish qua Docker, hãy ưu tiên DOCKER-USER, test bằng IP giả trước khi triển khai rộng và luôn giữ console/SSH dự phòng khi thay đổi firewall production.
Nguồn tham khảo: Fail2Ban documentation; Nginx ngx_http_limit_req_module; Docker packet filtering and firewalls.
