So sánh các phương pháp chặn IP hàng loạt trên Linux
Thấy server bị botnet nã SSH dồn dập, phản xạ đầu tiên của nhiều anh em sysadmin là mở terminal rồi cấm từng IP một. Cách này ổn nếu chỉ có vài chục IP quấy rối. Nhưng khi danh sách đen chạm mốc hàng chục nghìn địa chỉ, server sẽ đuối rất nhanh.
Trong thực tế vận hành, chúng ta thường cân nhắc giữa 3 giải pháp sau:
- Cách 1: Thêm rule tuần tự bằng rich-rule hoặc iptables truyền thống
Mỗi IP hoặc subnet là một dòng rule riêng biệt (chẳng hạnfirewall-cmd --add-rich-rule='rule family="ipv4" source address="x.x.x.x" drop'). - Cách 2: Chặn tại tầng reverse proxy (Nginx, Caddy hoặc WAF)
Dùng module geoip2 trên web server để ngắt kết nối ngay từ bước bắt tay TLS/HTTP. - Cách 3: Tận dụng IPset ở tầng Kernel
Gom toàn bộ danh sách IP vào bảng băm (hash table) trong kernel space. Firewall lúc này chỉ cần chạy đúng một rule đối chiếu duy nhất.
Hiệu năng thực tế: Đặt 3 giải pháp lên bàn cân
Mỗi cách tiếp cận xử lý gói tin theo một cơ chế hoàn toàn khác nhau dưới tầng network stack.
| Tiêu chí | Rule tuần tự (firewalld/iptables) | Tầng Web Server (Nginx GeoIP) | IPset + firewalld (Kernel Hash) |
|---|---|---|---|
| Độ phức tạp tra cứu | O(N) – Tuyến tính | O(log N) – Cây tìm kiếm | O(1) – Bảng băm |
| Tải CPU khi danh sách lớn | Tăng vọt từ 2.000 rule trở lên | Vừa phải, nhưng ngốn tài nguyên worker | Gần như bằng 0, ổn định tuyệt đối |
| Phạm vi bảo vệ | Mọi cổng (SSH, DB, Web…) | Chỉ gói gọn ở HTTP/HTTPS | Toàn bộ cổng mạng (L3/L4) |
| Thời gian nạp danh sách | Rất chậm, reload mất vài chục giây đến cả phút | Nhanh khi reload config | Gần như tức thì (vài mili-giây) |
Điểm yếu chí mạng của rule tuần tự là độ phức tạp O(N). Hãy tưởng tượng danh sách đen có 30.000 dòng. Mỗi gói tin từ người dùng hợp lệ phải chạy qua lần lượt 30.000 phép so sánh trước khi được đi tiếp. CPU server sẽ tăng vọt lên 90–100%, gây trễ mạng và rớt gói diện rộng.
Nginx xử lý traffic web rất tốt. Dù vậy, nó bỏ ngỏ hoàn toàn cổng SSH (22) hay PostgreSQL (5432). Kết hợp IPset trực tiếp vào firewall là cách duy nhất chặn đứng cuộc tấn công ngay tại cửa ngõ kernel.
Kiến trúc tối ưu: Dùng IPset native trên Fedora
Fedora từ lâu đã dùng nftables làm backend mặc định cho firewalld. Nhờ đó, bạn có thể tạo và quản trị ipset trực tiếp qua câu lệnh firewall-cmd. Không cần cài thêm gói ipset rời rạc của iptables cũ.
Quy trình triển khai gồm 3 bước gọn gàng:
- Tạo một IPset kiểu
hash:nettrên firewalld. - Gán tập IPset này vào zone
dropđể hủy gói tin trong im lặng. - Thiết lập script tự động kéo dải CIDR từ IPdeny rồi nạp vào bảng băm định kỳ.
Các bước triển khai chi tiết trên Fedora
Bước 1: Khởi tạo IPset trong firewalld
Trước hết, hãy chắc chắn firewalld đang hoạt động bình thường:
sudo systemctl status firewalld
Tiếp theo, tạo bảng băm mang tên blacklist-country với kiểu hash:net. Kiểu dữ liệu này cho phép lưu các dải mạng CIDR như 192.168.1.0/24 hay 10.0.0.0/8:
# Khởi tạo ipset mới cho IPv4 với sức chứa 200.000 dải mạng
sudo firewall-cmd --permanent --new-ipset=blacklist-country --type=hash:net --option=family=inet --option=hashsize=4096 --option=maxelem=200000
# Nạp lại cấu hình để firewalld nhận diện set mới
sudo firewall-cmd --reload
Mức maxelem=200000 là quá đủ để gom toàn bộ dải IP của vài quốc gia có lượng botnet lớn mà không sợ tràn bộ nhớ.
Bước 2: Gán IPset vào zone drop
Giải pháp triệt để nhất là đưa toàn bộ lưu lượng xuất phát từ tập IPset này sang zone drop. Khi một IP trong danh sách gửi gói tin đến, kernel sẽ âm thầm vứt bỏ gói đó. Server không phản hồi gói RST hay thông báo ICMP, giúp tiết kiệm tối đa băng thông chiều ra.
# Chuyển hướng toàn bộ IP trong set sang zone drop
sudo firewall-cmd --permanent --zone=drop --add-source=ipset:blacklist-country
# Áp dụng cấu hình ngay lập tức
sudo firewall-cmd --reload
Xác nhận lại thiết lập bằng lệnh kiểm tra nguồn của zone drop:
sudo firewall-cmd --zone=drop --list-sources
Màn hình hiển thị ipset:blacklist-country nghĩa là cấu hình đã bắt đầu có hiệu lực.
Bước 3: Viết script tự động nạp dải GeoIP
Dịch vụ IPdeny cung cấp danh sách CIDR miễn phí theo từng quốc gia dưới dạng file text thô. Chẳng hạn, bạn có thể tải dải IP của khu vực thường xuyên phát sinh brute-force SSH.
Hãy tạo một thư mục riêng để chứa script bảo trì hệ thống:
sudo mkdir -p /opt/firewall-scripts
sudo nano /opt/firewall-scripts/update-geoip.sh
Nội dung script tự động tải và cập nhật như sau:
#!/usr/bin/env bash
set -euo pipefail
IPSET_NAME="blacklist-country"
TEMP_FILE="/tmp/zone_data.txt"
# Ví dụ: tải danh sách IP từ IPdeny
URL="https://www.ipdeny.com/ipblocks/data/countries/cn.zone"
echo "[+] Bắt đầu tải danh sách IP..."
curl -sSL --fail "${URL}" -o "${TEMP_FILE}"
echo "[+] Làm rỗng danh sách hiện tại..."
# Xóa các entry cũ để tránh phình dữ liệu rác
sudo firewall-cmd --permanent --ipset="${IPSET_NAME}" --remove-entries-from-file="${TEMP_FILE}" 2>/dev/null || true
echo "[+] Nạp danh sách mới vào kernel..."
sudo firewall-cmd --permanent --ipset="${IPSET_NAME}" --add-entries-from-file="${TEMP_FILE}"
echo "[+] Reload firewall..."
sudo firewall-cmd --reload
rm -f "${TEMP_FILE}"
echo "[+] Cập nhật IPset hoàn tất!"
Cấp quyền thực thi và chạy thử script một lần:
sudo chmod 750 /opt/firewall-scripts/update-geoip.sh
sudo /opt/firewall-scripts/update-geoip.sh
Bước 4: Kiểm tra và xác minh kết quả
Vì Fedora chạy firewalld trên nền nftables, bạn có thể kiểm tra danh sách đã được nạp trực tiếp qua firewalld hoặc qua lệnh nft:
# Kiểm tra thông tin tập hợp và số lượng entries
sudo firewall-cmd --info-ipset=blacklist-country
# Hoặc xem bảng băm thực tế trong nftables
sudo nft list set inet firewalld blacklist-country | head -n 30
Về mặt tài nguyên, lưu trữ 20.000 dải mạng chỉ tốn khoảng 2–4 MB RAM trong kernel space. Con số này quá nhỏ bé ngay cả với VPS giá rẻ 1 GB RAM.
Muốn test nhanh xem một IP cụ thể có bị chặn hay không, bạn chỉ cần gõ:
sudo firewall-cmd --ipset=blacklist-country --query-entry="1.2.4.0/24"
Lệnh trả về yes tức là mọi kết nối từ dải mạng này sẽ bị chặn đứng ngay lập tức.
Bước 5: Tự động hóa bằng Systemd Timer
Bảng phân bổ IP trên Internet thay đổi liên tục. Thay vì dùng crontab khó theo dõi output, systemd timer là lựa chọn tối ưu trên Fedora nhờ khả năng xem log tập trung qua journalctl.
Tạo file service tại /etc/systemd/system/update-geoip.service:
[Unit]
Description=Cập nhật IPset GeoIP cho Firewalld
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
ExecStart=/opt/firewall-scripts/update-geoip.sh
StandardOutput=journal
StandardError=journal
Tạo tiếp file timer tại /etc/systemd/system/update-geoip.timer để chạy định kỳ vào 03:00 sáng mỗi Chủ Nhật:
[Unit]
Description=Kích hoạt cập nhật IPset hàng tuần
[Timer]
OnCalendar=Sun *-*-* 03:00:00
Persistent=true
[Install]
WantedBy=timers.target
Bật và kích hoạt timer:
sudo systemctl daemon-reload
sudo systemctl enable --now update-geoip.timer
# Kiểm tra lịch chạy kế tiếp
sudo systemctl list-timers update-geoip.timer
Hai điểm cần nhớ trước khi đưa vào Production
- Đừng tự khóa cửa nhốt mình: Luôn cho IP tĩnh cá nhân hoặc dải mạng VPN (như WireGuard/OpenVPN) vào zone
trusted. Rule ở zone này có độ ưu tiên cao hơn zonedrop, giúp bạn không bao giờ mất quyền điều khiển máy chủ. - Cẩn thận khi tải danh sách quá lớn: Nếu nạp trên 100.000 subnet một lúc, hãy dùng HTTPS và kiểm tra mã trả về của lệnh curl. Tránh nạp file lỗi hoặc file HTML trắng vào firewall khiến script gặp sự cố.
