Khi tường lửa truyền thống bị qua mặt
Hơn 6 tháng trước, văn phòng 50 máy của mình gặp một sự cố nhớ đời. Một bạn nhân viên vô tình click vào link lừa đảo ngân hàng qua email. Ngay sau đó, mã độc trên máy liên tục query DNS ra các domain Dynamic DNS lạ nhằm tải payload thứ cấp. Firewall biên lúc đó có bật IDS/IPS. Tuy nhiên, do toàn bộ traffic đã bị mã hóa TLS/HTTPS, firewall xử lý rất chậm chạp và không chặn kịp thời.
Giải pháp hiệu quả nhất lúc này là chặn ngay tại tầng phân giải tên miền (DNS Resolver nội bộ). Bạn không cần chi cả chục ngàn USD mua Next-Gen Firewall đắt đỏ. Bạn cũng không cần cài agent nặng nề lên từng máy nhân viên. BIND9 có sẵn một vũ khí cực kỳ lợi hại: Response Policy Zones (RPZ). Tính năng này biến con DNS Server thông thường thành một DNS Firewall mạnh mẽ, bẻ gãy liên lạc của mã độc ngay từ bước tra cứu địa chỉ IP.
Cơ chế hoạt động của Response Policy Zones (RPZ)
Nói một cách dễ hiểu, RPZ hoạt động như một lớp lọc trung gian trong quá trình phân giải đệ quy (recursive DNS). Khi client gửi query, BIND9 vẫn đi hỏi các Root và Authoritative DNS Server ngoài Internet như bình thường. Nhưng trước khi trả lời máy trạm, BIND9 sẽ đối chiếu kết quả với "bảng luật" cục bộ do bạn thiết lập.
Các hành động (Policy Actions) thường dùng trong RPZ
- NXDOMAIN: Trả về mã lỗi tên miền không tồn tại (Name Error). Domain độc hại coi như biến mất khỏi mạng nội bộ. Mã độc lập tức đứt kết nối.
- NODATA: Báo domain có tồn tại nhưng không có bản ghi A/AAAA tương ứng.
- Redirect / Sinkhole (Local Data): Trỏ truy vấn về IP Web Server nội bộ (ví dụ
10.10.10.254). Trang này sẽ hiện cảnh báo bảo mật cho nhân viên hoặc ghi nhận request để cô lập máy nhiễm độc. - PASSTHRU: Cho phép DNS query đi qua bình thường. Hành động này dùng để whitelist gấp các domain nghiệp vụ bị chặn nhầm.
- DROP: Vứt bỏ gói tin truy vấn, khiến client bị timeout (ít dùng vì dễ làm treo ứng dụng phía người dùng).
7 bước dựng DNS Firewall với BIND9 RPZ
Bước 1: Bật tính năng response-policy trên BIND9
Đầu tiên, bạn mở file cấu hình chính của BIND9 (trên Ubuntu/Debian là /etc/bind/named.conf.options, trên CentOS/RHEL là /etc/named.conf). Sau đó, thêm block response-policy vào trong mệnh đề options:
sudo nano /etc/bind/named.conf.options
options {
directory "/var/cache/bind";
// Cho phép recursive DNS cho dải mạng LAN
recursion yes;
allow-query { 127.0.0.1; 192.168.1.0/24; 10.10.0.0/16; };
allow-recursion { 127.0.0.1; 192.168.1.0/24; 10.10.0.0/16; };
// Khai báo danh sách các zone RPZ
response-policy {
zone "rpz.whitelist.local" policy passthru;
zone "rpz.malware.local" policy given;
} qname-wait-recurse no;
dnssec-validation auto;
listen-on-v6 { any; };
};
Lưu ý: Thứ tự khai báo quyết định độ ưu tiên. BIND9 sẽ duyệt rule từ trên xuống dưới. Zone rpz.whitelist.local luôn phải đặt trước để các domain phục vụ công việc không bao giờ bị chặn nhầm.
Bước 2: Khai báo Zone RPZ trong named.conf.local
Bạn khai báo định nghĩa zone tương tự như zone DNS thông thường trong file /etc/bind/named.conf.local:
sudo nano /etc/bind/named.conf.local
// Whitelist RPZ Zone
zone "rpz.whitelist.local" {
type master;
file "/etc/bind/rpz/db.rpz.whitelist";
allow-query { localhost; };
allow-transfer { none; };
};
// Malware & Phishing Blacklist RPZ Zone
zone "rpz.malware.local" {
type master;
file "/etc/bind/rpz/db.rpz.malware";
allow-query { localhost; };
allow-transfer { none; };
};
Bước 3: Khởi tạo Zone File và định nghĩa bộ lọc
Hãy tạo một thư mục riêng để quản lý các file RPZ và phân quyền cho user chạy dịch vụ:
sudo mkdir -p /etc/bind/rpz
sudo chown -R bind:bind /etc/bind/rpz
File whitelist /etc/bind/rpz/db.rpz.whitelist giúp bỏ qua các domain an toàn:
$TTL 300
@ IN SOA localhost. root.localhost. (
2024040101 ; Serial
3600 ; Refresh
1800 ; Retry
604800 ; Expire
300 ) ; Minimum
IN NS localhost.
; Cho phép phân giải bình thường dù domain có nằm trong blacklist
github.com IN CNAME rpz-passthru.
*.github.com IN CNAME rpz-passthru.
File chặn mã độc /etc/bind/rpz/db.rpz.malware định nghĩa các quy tắc xử lý:
$TTL 300
@ IN SOA localhost. root.localhost. (
2024040101 ; Serial
3600 ; Refresh
1800 ; Retry
604800 ; Expire
300 ) ; Minimum
IN NS localhost.
; 1. Chặn domain lừa đảo bằng NXDOMAIN (dấu chấm biểu thị root null)
bad-phishing-site.xyz IN CNAME .
*.bad-phishing-site.xyz IN CNAME .
; 2. Chặn C2 Server bằng NODATA (dấu sao kèm dấu chấm)
malware-c2-server.top IN CNAME *.
*.malware-c2-server.top IN CNAME *.
; 3. Chuyển hướng (Sinkhole) về trang cảnh báo nội bộ
trojan-download.online IN A 10.10.10.254
*.trojan-download.online IN A 10.10.10.254
Bước 4: Cấu hình kênh Log riêng cho RPZ
Khi có máy trạm gửi query đến domain độc, bạn cần biết chính xác IP đó để cách ly thiết bị kịp thời. Hãy tách riêng category rpz vào file log độc lập trong /etc/bind/named.conf.log:
sudo nano /etc/bind/named.conf.log
logging {
channel rpz_log {
file "/var/log/named/rpz.log" versions 5 size 20m;
severity info;
print-time yes;
print-category yes;
print-severity yes;
};
category rpz {
rpz_log;
};
};
Đừng quên tạo thư mục chứa log và cấp quyền ghi cho daemon DNS:
sudo mkdir -p /var/log/named
sudo chown -R bind:bind /var/log/named
Bước 5: Kiểm tra cú pháp và kích hoạt dịch vụ
Trước khi reload BIND9, hãy kiểm tra kỹ cú pháp để tránh làm gián đoạn phân giải DNS toàn mạng:
# Kiểm tra file cấu hình tổng
sudo named-checkconf
# Kiểm tra từng zone file
sudo named-checkzone rpz.malware.local /etc/bind/rpz/db.rpz.malware
# Áp dụng cấu hình an toàn không làm ngắt kết nối client
sudo rndc reload
Bước 6: Kiểm tra hoạt động thực tế từ Client
Đứng từ một máy trạm trong mạng LAN (ví dụ máy 192.168.1.45), bạn dùng dig để query thử domain độc hại:
dig @192.168.1.2 bad-phishing-site.xyz
Kết quả trả về sẽ báo trạng thái NXDOMAIN ngay lập tức mà không có ANSWER SECTION:
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 48215
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1
Mở file log trên DNS Server để xem thông tin chi tiết:
sudo tail -f /var/log/named/rpz.log
01-Apr-2024 14:22:10.104 rpz: info: client @0x7f88a0 192.168.1.45#51234 (bad-phishing-site.xyz): rpz QNAME NXDOMAIN rewrite bad-phishing-site.xyz via bad-phishing-site.xyz.rpz.malware.local
Dòng log chỉ rõ máy 192.168.1.45 vừa truy cập domain lừa đảo. Bạn có thể dựa vào đây để khoanh vùng và xử lý sự cố.
Bước 7: Tự động hóa cập nhật Threat Feed
Thêm từng domain bằng tay chắc chắn không khả thi. Trong môi trường thực tế, mình viết một script bash nhỏ chạy qua cronjob định kỳ 2 giờ/lần để kéo feed từ URLhaus hoặc MalwareDomainList về cập nhật:
#!/bin/bash
# Script: /usr/local/bin/update-rpz.sh
set -euo pipefail
FEED_URL="https://urlhaus.abuse.ch/downloads/rpz/"
DEST_FILE="/etc/bind/rpz/db.rpz.urlhaus"
TMP_FILE="/tmp/urlhaus.rpz"
# Tải feed mới
if curl -s -f -L "$FEED_URL" -o "$TMP_FILE"; then
# Kiểm tra tính hợp lệ của zone file trước khi ghi đè
if named-checkzone rpz.urlhaus.local "$TMP_FILE" > /dev/null 2>&1; then
mv "$TMP_FILE" "$DEST_FILE"
chown bind:bind "$DEST_FILE"
rndc reload rpz.urlhaus.local
else
rm -f "$TMP_FILE"
fi
fi
3 bài học xương máu sau 6 tháng vận hành
- Luôn ép $TTL thấp (300s): Nếu lỡ chặn nhầm một trang đối tác quan trọng, bạn chỉ cần gỡ rule. Máy client sẽ nhận diện thay đổi sau tối đa 5 phút thay vì chịu cảnh nghẽn mạng cả buổi.
- Cẩn thận với Wildcard: Cú pháp
*.domain.com IN CNAME .sẽ chặn đứng mọi subdomain. Nếu feed chứa các domain dùng chung như*.github.iohay*.pages.dev, toàn bộ trang tài liệu kỹ thuật của team sẽ bị sập theo. Vì vậy, bạn luôn cần zone whitelist ưu tiên bên trên. - Chi phí tài nguyên siêu nhẹ: Nạp một zone chứa hơn 100.000 domain độc hại chỉ tốn thêm khoảng 70MB RAM trên máy chủ BIND9. Thời gian phản hồi DNS tăng chưa tới 1ms, người dùng hoàn toàn không cảm nhận được độ trễ.
Chỉ với vài dòng cấu hình trên BIND9, hệ thống mạng văn phòng của bạn đã có thêm một tầng bảo vệ chủ động, chặn đứng nguy cơ botnet và lừa đảo ngay từ cửa ngõ.

