Cấu hình Fail2ban bảo vệ server Ubuntu khỏi tấn công brute-force

Ubuntu tutorial - IT technology blog
Ubuntu tutorial - IT technology blog

2 giờ sáng và log SSH của bạn đang rực lửa

Mình nhớ lần đầu tiên nhìn vào /var/log/auth.log của một con VPS mới dựng được 3 ngày — có hơn 4.000 lần thử đăng nhập SSH từ các IP khác nhau trong vòng 6 tiếng. Không có cái nào thành công. Nhưng cứ nghĩ đến việc chúng vẫn đang hammer server 24/7 là đủ mất ngủ rồi.

Fail2ban là thứ mình cài ngay sau khi thiết lập SSH key trên bất kỳ server nào. Sau hơn 20 con VPS Ubuntu Server 22.04, cái quy trình này gần như thành phản xạ. Bài này đi thẳng vào vấn đề — cài, cấu hình, test, và xử lý một số tình huống hay gặp.

Fail2ban hoạt động như thế nào?

Cơ chế đơn giản hơn bạn tưởng: Fail2ban theo dõi log file (auth.log, nginx/access.log, v.v.), phát hiện IP nào thử đăng nhập sai quá nhiều lần trong khoảng thời gian nhất định, rồi tự động thêm rule iptables/nftables để block IP đó.

Ba khái niệm cốt lõi:

  • jail — một bộ quy tắc giám sát cho một service cụ thể (SSH, Nginx, v.v.)
  • filter — regex pattern để nhận diện dòng log “đáng ngờ”
  • action — việc làm khi phát hiện vi phạm (ban IP, gửi email, v.v.)

Fail2ban đã đi kèm filter sẵn cho hàng chục service phổ biến. Phần lớn trường hợp, bạn chỉ cần bật jail là xong.

Cài đặt Fail2ban trên Ubuntu

sudo apt update
sudo apt install fail2ban -y

Sau khi cài, Fail2ban chạy tự động. Kiểm tra:

sudo systemctl status fail2ban

Bạn sẽ thấy active (running). Nếu không, chạy:

sudo systemctl enable --now fail2ban

Cấu hình đúng cách — đừng sửa file gốc

Fail2ban có hai lớp config:

  • /etc/fail2ban/jail.conf — file gốc, bị ghi đè khi update package
  • /etc/fail2ban/jail.local — file override của bạn, luôn được ưu tiên

Nguyên tắc: không bao giờ sửa jail.conf. Tạo jail.local từ bản copy:

sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
sudo nano /etc/fail2ban/jail.local

Cấu hình phần [DEFAULT]

Thiết lập ở đây áp dụng cho tất cả jail trừ khi bị override tại từng jail cụ thể:

[DEFAULT]
# Danh sách IP/subnet không bao giờ bị ban — QUAN TRỌNG
ignoreip = 127.0.0.1/8 ::1 YOUR_HOME_IP

# Thời gian ban (giây). -1 = ban vĩnh viễn
bantime = 3600

# Khoảng thời gian tính số lần thất bại
findtime = 600

# Số lần thất bại tối đa trước khi ban
maxretry = 5

# Backend đọc log — systemd hoặc auto
backend = systemd

Thay YOUR_HOME_IP bằng IP thật của bạn. Mình từng bị tự lock khỏi server vì quên bước này — không vui chút nào lúc 11 giờ đêm, không có console access.

Bật jail SSH

Trên Ubuntu 22.04 với systemd, cấu hình SSH jail trong jail.local như sau:

[sshd]
enabled = true
port = ssh
logpath = %(sshd_log)s
backend = %(sshd_backend)s
maxretry = 3
bantime = 86400

Mình chọn maxretry = 3bantime = 86400 (24 giờ) cho SSH — nghiêm hơn mặc định vì SSH là mục tiêu số một của mọi script scanner.

Reload để áp dụng:

sudo systemctl reload fail2ban

Kiểm tra jail đang hoạt động

# Xem danh sách jail đang chạy
sudo fail2ban-client status

# Xem chi tiết jail sshd
sudo fail2ban-client status sshd

Output của lệnh cuối cho thấy số IP đang bị ban, tổng số lần ban, và danh sách IP cụ thể:

Status for the jail: sshd
|- Filter
|  |- Currently failed:	2
|  |- Total failed:	47
|  `- Journal matches:  _SYSTEMD_UNIT=ssh.service + _COMM=sshd
`- Actions
   |- Currently banned:	3
   |- Total banned:	12
   `- Banned IP list:	203.0.113.42 198.51.100.17 192.0.2.88

Thực chiến: bảo vệ Nginx và WordPress

Brute-force không chỉ đến từ SSH. Nếu bạn chạy web server, WordPress login và Nginx cũng là điểm bị tấn công thường xuyên — đôi khi còn ồn ào hơn cả SSH.

Jail cho Nginx – chặn scan và 4xx flood

[nginx-http-auth]
enabled = true
port = http,https
logpath = /var/log/nginx/error.log

[nginx-botsearch]
enabled = true
port = http,https
logpath = /var/log/nginx/access.log
maxretry = 2

Custom jail cho WordPress xmlrpc.php

File xmlrpc.php là điểm tấn công cực kỳ phổ biến — thường bị dùng để brute-force credential qua XML-RPC multicall. Tạo filter riêng:

sudo nano /etc/fail2ban/filter.d/wordpress-xmlrpc.conf
[Definition]
failregex = <HOST> .* "POST /xmlrpc.php
ignoreregex =

Rồi thêm jail vào jail.local:

[wordpress-xmlrpc]
enabled = true
port = http,https
filter = wordpress-xmlrpc
logpath = /var/log/nginx/access.log
maxretry = 5
bantime = 86400

Unban IP và debug

Tình huống kinh điển: bạn test SSH từ máy khác, nhỡ tay nhập sai password 3 lần và bị ban chính mình. Xử lý nhanh:

# Unban IP cụ thể
sudo fail2ban-client set sshd unbanip 1.2.3.4

# Test filter có match đúng log không
sudo fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/sshd.conf

# Xem log của fail2ban
sudo tail -f /var/log/fail2ban.log

Lệnh fail2ban-regex đặc biệt hữu ích khi viết custom filter — nó cho biết chính xác có bao nhiêu dòng log khớp với pattern, tiết kiệm rất nhiều thời gian debug.

Tăng bantime tự động theo lịch sử vi phạm

IP tái phạm nên bị phạt nặng hơn. Fail2ban hỗ trợ bantime.increment để làm đúng điều đó — mỗi lần vi phạm tiếp theo, thời gian ban tăng gấp đôi. Thêm vào phần [DEFAULT]:

[DEFAULT]
bantime.increment = true
bantime.factor = 1
bantime.formula = ban.Time * (1<<(ban.Count if ban.Count<20 else 20)) * banFactor
bantime.multiplier = 2
bantime.maxtime = 604800  # Tối đa 7 ngày

Kết quả thực tế với bantime = 3600 (1 giờ) làm gốc: lần 1 bị ban 1 giờ, lần 2 bị ban 2 giờ, lần 3 bị ban 4 giờ… IP nào cứng đầu sẽ leo thang đến mức bị block cả tuần.

Kiểm tra toàn bộ cấu hình trước khi reload

# Kiểm tra syntax config
sudo fail2ban-client --test

# Reload nếu không có lỗi
sudo systemctl reload fail2ban

# Xác nhận các jail đang active
sudo fail2ban-client status

Kết luận

Fail2ban không giải quyết được mọi thứ — nó không thay thế việc dùng SSH key thay password, hay cập nhật package thường xuyên. Nhưng ở vai trò của mình, nó làm rất tốt: ít tốn resource, tự động xử lý phần lớn noise từ các bot scanner, không cần can thiệp thủ công.

15 phút setup, đổi lại là đêm ngủ ngon hơn. Sau khi cấu hình xong, mình thường chạy sudo fail2ban-client status sshd mỗi sáng qua cron để nắm tình hình. Nhìn vào danh sách IP bị ban dài ra mỗi ngày mà thấy yên tâm — server đang tự lo được phần của nó.

Share: