Chuông báo 2 giờ sáng và bài toán chọn giải pháp IDS/IPS
02:14 sáng, PagerDuty hú liên hồi. Cụm Nginx gateway hứng trọn 350.000 req/s từ botnet. CPU vọt lên 98%, RAM cạn dần, kết nối rớt như sung rụng. Bạn chỉ có 5 phút để hành động: chặn đứng IP bẩn mà không bóp chết tập khách hàng đang thanh toán giỏ hàng. Đây là lúc một engine Network IDS/IPS đủ tải phải chứng minh giá trị.
Hạ tầng Linux hiện có ba ứng viên phổ biến nhất:
- Snort (bản 2.x): Lão làng nhưng kẹt ở kiến trúc đơn luồng (single-thread). Khi traffic vọt qua ngưỡng 1.5 Gbps, một core CPU gánh inspection sẽ nghẽn cứng, dẫn tới drop gói hàng loạt. Bản Snort 3 đã hỗ trợ đa luồng nhưng hệ sinh thái và plugin vẫn còn khá phân mảnh.
- Zeek (Bro cũ): Vua bóc tách metadata tầng ứng dụng để phục vụ forensics. Tuy nhiên, Zeek ngốn RAM khủng khiếp (chạy thực tế thường nuốt 16–32 GB RAM dễ như chơi). Công cụ này không thiết kế để chặn gói tin theo thời gian thực (inline IPS).
- Suricata: Kiến trúc multi-threading native từ ngày đầu. Engine này scale mượt trên máy chủ 32–64 core, hỗ trợ tăng tốc phần cứng, parse sâu HTTP/2, TLS, DNS và tương thích 100% với bộ rule của Snort.
Phân tích kiến trúc dưới áp lực tải thật
Khi chọn công cụ production, hiệu năng bắt gói quan trọng hơn tính năng trên slide thuyết trình.
1. Khả năng scale CPU đa nhân
Suricata phân bổ tải gói tin qua AF_PACKET hoặc PF_RING rất nhịp nhàng. Mỗi core CPU sẽ nhận một luồng xử lý riêng biệt từ RX ring buffer của card mạng. Ngược lại, nếu ép Snort 2.x chạy đa nhân, bạn phải spawn 8–16 process riêng lẻ kèm balancer phức tạp, stateful tracking rất dễ vỡ vụn.
2. Độ trễ và nguy cơ rớt mạng khi bật Inline IPS
Chế độ thụ động (IDS) bắt gói qua SPAN/Mirror port rất an toàn. Engine quá tải thì log bị drop, dịch vụ của bạn vẫn sống. Bước sang IPS (Inline mode với NFQUEUE hoặc AF_PACKET IPS) là câu chuyện hoàn toàn khác. Mỗi packet trước khi ra Internet đều phải qua tay Suricata kiểm duyệt.
Chỉ cần ruleset quá nặng, độ trễ RTT sẽ nhảy từ 2ms lên 80ms ngay lập tức. Bài học nhớ đời của tôi là ca drop 4.8% packet vào khung giờ vàng 20h. Metric CPU và RAM lúc đó vẫn báo xanh rì dưới 50%. Chỉ khi gõ suricata --dump-counters, tôi mới tá hỏa thấy biến capture.kernel_drops đã nhảy lên con số vài triệu gói do ring buffer bị tràn.
Chiến lược triển khai khôn ngoan cho máy chủ Linux
Đừng vội bật chế độ chặn ngay từ ngày đầu. Hãy khởi động với AF_PACKET IDS trong 1–2 tuần đầu tiên. Mục tiêu là đo đạc baseline mạng, lập danh sách whitelist nội bộ và gọt giũa ruleset để triệt tiêu toàn bộ cảnh báo giả (false positives). Khi engine chạy ổn định với tỷ lệ drop dưới 0.01%, bạn hẵng chuyển sang mode IPS inline.
Các bước cài đặt và cấu hình Suricata trên Ubuntu Server
Bước 1: Cài đặt bản mới nhất từ PPA chính thức
Kho mặc định của Ubuntu thường lưu bản Suricata khá cũ (nhiều bản v6.x đã hết hạn hỗ trợ). Hãy kéo bản v7.x từ repo chính thức của OISF:
# Thêm PPA chính thức và cập nhật package list
sudo add-apt-repository ppa:oisf/suricata-stable -y
sudo apt update
# Cài đặt Suricata kèm jq để lọc log JSON
sudo apt install suricata jq -y
Kiểm tra các cờ build hỗ trợ phần cứng:
suricata --build-info
Bước 2: Tắt Network Hardware Offloading
Card mạng hiện đại thường tự gộp nhiều gói tin nhỏ thành siêu gói 64KB (LRO/GRO) để giảm tải cho CPU. Tuy nhiên, hành động này khiến engine IDS không thể soi payload signature theo từng MTU chuẩn 1500 bytes. Giả sử card mạng chính là eth0, hãy tắt ngay các tính năng này:
sudo ethtool -K eth0 rx off tx off gso off gro off
Bước 3: Tối ưu thông số trong suricata.yaml
Mở file cấu hình chính:
sudo nano /etc/suricata/suricata.yaml
Chỉnh sửa các khối cấu hình mạng và buffer:
vars:
address-groups:
HOME_NET: "[192.168.1.0/24,10.0.0.0/8]"
EXTERNAL_NET: "!$HOME_NET"
default-log-dir: /var/log/suricata
af-packet:
- interface: eth0
threads: auto # Tự động gán thread theo số core CPU
cluster-id: 99
cluster-type: cluster_flow # Đảm bảo cùng flow session vào chung 1 core
defrag: yes
use-mmap: yes
tpacket-v3: yes
ring-size: 8192 # Nâng lên 8192 để triệt tiêu packet drops khi burst
Bước 4: Đồng bộ tập luật với suricata-update
Thay vì tải file nén thủ công, công cụ suricata-update sẽ tự kéo bộ luật Emerging Threats (ET Open) cập nhật hàng ngày:
# Cập nhật database luật mới nhất
sudo suricata-update
# Kiểm tra file luật sau khi gộp
ls -lh /var/lib/suricata/rules/suricata.rules
Bước 5: Viết Custom Rule thực tế
Đừng tin hệ thống đang bảo vệ bạn nếu chưa từng thấy nó kích hoạt cảnh báo. Mở file rule tự định nghĩa:
sudo nano /var/lib/suricata/rules/local.rules
Thêm hai rule bắt gói ICMP và hành vi dò quét file nhạy cảm tầng HTTP:
alert icmp any any -> $HOME_NET any (msg:"SECURITY SCAN: PING test detect"; sid:1000001; rev:1;)
alert http any any -> $HOME_NET any (msg:"EXPLOIT ATTEMPT: LFI /etc/passwd in URI"; flow:established,to_server; http.uri; content:"/etc/passwd"; nocase; sid:1000002; rev:1;)
Khai báo local.rules vào danh sách nạp của suricata.yaml:
rule-files:
- suricata.rules
- local.rules
Bước 6: Test cú pháp và kích hoạt dịch vụ
Test kỹ file cấu hình trước khi reload daemon:
sudo suricata -T -c /etc/suricata/suricata.yaml -v
Khi thấy dòng Configuration provided was successfully parsed, bật service:
sudo systemctl restart suricata
sudo systemctl enable suricata
Mở một tab terminal để theo dõi log nhanh:
sudo tail -f /var/log/suricata/fast.log
Từ máy client, giả lập request tấn công thử nghiệm:
curl -s -A "AttackScanner/1.0" "http://<IP_SERVER>/?file=/etc/passwd"
Ngay sau 1 mili-giây, log fast.log sẽ ghi nhận:
[**] [1:1000002:1] EXPLOIT ATTEMPT: LFI /etc/passwd in URI [**] [Priority: 1] {TCP} 10.0.0.15:48922 -> 10.0.0.5:80
Với log chi tiết eve.json, lọc cảnh báo bằng jq:
sudo tail -f /var/log/suricata/eve.json | jq 'select(.event_type=="alert") | {src: .src_ip, alert: .alert.signature, uri: .http.url}'
Bước 7: Chuyển sang Inline IPS với Linux NFQUEUE
Khi tự tin với độ chính xác của rule, bạn có thể biến Suricata thành tường lửa chủ động chặn đòn tấn công.
Đẩy traffic cổng 80 qua hàng đợi số 0 của kernel:
sudo iptables -I INPUT -p tcp --dport 80 -j NFQUEUE --queue-num 0
Sửa hành động trong local.rules từ alert sang drop:
drop http any any -> $HOME_NET any (msg:"BLOCKED: LFI /etc/passwd"; flow:established,to_server; http.uri; content:"/etc/passwd"; nocase; sid:1000003; rev:1;)
Chạy Suricata với cờ lắng nghe NFQUEUE:
sudo suricata -c /etc/suricata/suricata.yaml -q 0 -D
Từ bây giờ, mọi request vi phạm sẽ bị kernel hủy gói ngay lập tức. Client nhận về lỗi Connection Reset hoặc Timeout mà web server phía sau không hề tốn một chu kỳ CPU nào.
