Tóm tắt: Viết toàn bộ ruleset vào /etc/nftables.conf (bảng inet xử lý cả IPv4/IPv6), mặc định policy drop ở input, cho phép established,related, SSH chỉ từ dải quản trị qua set. Kiểm tra cú pháp bằng nft -c -f, áp bằng nft -f, bật nftables.service để tự nạp khi boot.
Luồng hoạt động
flowchart LR P[Packet vào] --> PR["prerouting (dstnat)"] PR --> R{"Đích là host?"} R -->|có| IN["input (filter)"] R -->|không| FW["forward (filter)"] IN --> L["Process local"] L --> OUT["output (filter)"] FW --> PO["postrouting (srcnat)"] OUT --> PO PO --> X[Packet ra]
Cấu hình
Ruleset mẫu
#!/usr/sbin/nft -f
flush ruleset
define MGMT_V4 = { 198.51.100.0/24 }
define MGMT_V6 = { 2001:db8:100::/48 }
table inet filter {
set mgmt4 {
type ipv4_addr
flags interval
elements = $MGMT_V4
}
set blocklist4 {
type ipv4_addr
flags dynamic, timeout
timeout 1h
}
chain input {
type filter hook input priority filter; policy drop;
ct state established,related accept
ct state invalid drop
iifname "lo" accept
ip saddr @blocklist4 drop
# ICMP/ICMPv6 cần cho PMTUD và IPv6 ND
ip protocol icmp icmp type { echo-request, destination-unreachable, time-exceeded } accept
meta l4proto ipv6-icmp accept
# SSH chỉ từ dải quản trị, giới hạn tốc độ kết nối mới
tcp dport 22 ip saddr @mgmt4 ct state new limit rate 10/minute accept
tcp dport 22 ip6 saddr $MGMT_V6 accept
# Web public
tcp dport { 80, 443 } accept
# gNMI dial-in từ router
tcp dport 57400 ip saddr 203.0.113.0/24 accept
limit rate 5/second log prefix "nft-drop: " level info
}
chain forward {
type filter hook forward priority filter; policy drop;
ct state established,related accept
ct status dnat accept
iifname "eth1" oifname "eth0" accept
}
chain output {
type filter hook output priority filter; policy accept;
}
}
table ip nat {
chain prerouting {
type nat hook prerouting priority dstnat; policy accept;
iifname "eth0" tcp dport 8443 dnat to 192.0.2.20:443
}
chain postrouting {
type nat hook postrouting priority srcnat; policy accept;
oifname "eth0" ip saddr 192.0.2.0/24 masquerade
}
}Ghi chú:
flush rulesetở đầu làm file idempotent: chạy lại bao nhiêu lần cũng ra cùng kết quả. Lưu ý nó xoá cả rule do Docker/libvirt/fail2ban tạo; host chạy Docker thì thayflush rulesetbằng hai dòngtable inet filter+delete table inet filterđể chỉ làm mới bảng của mình (và bỏ bảngip natnếu không cần).policy dropởforwardchỉ cần khi máy làm router/NAT. Muốn forward phải bậtnet.ipv4.ip_forward=1.192.0.2.0/24vàeth0/eth1là ví dụ, thay bằng mạng thật.
Làm qua SSH thì hẹn giờ tự gỡ trước khi áp: sudo systemd-run --on-active=5m nft flush ruleset, áp ruleset, mở phiên SSH mới thử, rồi huỷ timer bằng systemctl stop run-<id>.timer (tên in ra khi chạy systemd-run).
Áp dụng và persist
sudo nft -c -f /etc/nftables.conf # chỉ kiểm tra cú pháp, không áp
sudo nft -f /etc/nftables.conf # áp (atomic: lỗi thì không đổi gì)
sudo systemctl enable --now nftables # Debian/Ubuntu/RHEL: nạp /etc/nftables.conf (RHEL: /etc/sysconfig/nftables.conf) khi bootLệnh hằng ngày
| Mục đích | Lệnh |
|---|---|
| Xem toàn bộ ruleset | sudo nft list ruleset |
| Xem kèm handle (để xoá rule) | sudo nft -a list chain inet filter input |
| Thêm rule cuối chain | sudo nft add rule inet filter input tcp dport 9100 ip saddr 198.51.100.0/24 accept |
| Chèn rule đầu chain | sudo nft insert rule inet filter input tcp dport 9100 drop |
| Xoá rule theo handle | sudo nft delete rule inet filter input handle 27 |
| Thêm IP vào set | sudo nft add element inet filter mgmt4 { 198.51.100.77 } |
Ban IP 2 giờ (set có timeout) |
sudo nft add element inet filter blocklist4 { 203.0.113.50 timeout 2h } |
| Xem phần tử set | sudo nft list set inet filter blocklist4 |
| Đếm packet theo rule | thêm counter vào rule, rồi nft list ruleset |
| Dump ruleset đang chạy ra file | sudo nft list ruleset > /etc/nftables.conf (thêm flush ruleset lên đầu) |
| Theo dõi packet đi qua rule | sudo nft monitor trace (rule cần meta nftrace set 1) |
| Log bị drop | journalctl -k | grep nft-drop |
Thay đổi tạm bằng nft add sẽ mất khi reboot hoặc khi nft -f chạy lại. Luôn sửa file rồi nft -f, file nằm trong Git/Ansible.
So với iptables và ufw
| iptables | nftables | ufw / firewalld | |
|---|---|---|---|
| Bản chất | Công cụ cũ (backend iptables-nft trên distro mới đã dùng nftables bên dưới) |
Framework hiện hành của kernel | Frontend, sinh rule nftables/iptables |
| IPv4 + IPv6 | Hai bộ lệnh iptables/ip6tables |
Một bảng inet |
Có |
| Set / map | Cần ipset riêng |
Có sẵn, cập nhật atomic | Hạn chế |
| Áp ruleset | Từng rule, không atomic | Cả file, atomic | Theo lệnh |
| Hợp với | Hệ thống cũ, script có sẵn | Server, gateway, cấu hình bằng Ansible | Desktop, server đơn giản |
Chuyển rule cũ: iptables-save > old.txt && iptables-restore-translate -f old.txt in ra cú pháp nft tương đương. Không chạy song song ufw/firewalld với file nftables tự viết: chúng ghi đè lẫn nhau.
Lỗi thường gặp
| Triệu chứng | Nguyên nhân | Cách xử lý |
|---|---|---|
| Mất SSH ngay khi áp | policy drop nhưng thiếu rule SSH / IP quản trị không nằm trong set |
Console/IPMI; lần sau dùng timer tự gỡ |
Docker container mất mạng sau nft -f |
flush ruleset xoá bảng của Docker |
systemctl restart docker; chỉ flush bảng của mình |
| IPv6 chập chờn, mất neighbor | Chặn ICMPv6 | Cho phép meta l4proto ipv6-icmp |
| PMTU/MTU lỗi, kết nối treo khi gửi gói lớn | Chặn ICMP destination-unreachable |
Cho phép ICMP type đó |
| DNAT không chạy | forward policy drop, thiếu ct status dnat accept, hoặc chưa bật ip_forward |
Thêm rule, sysctl net.ipv4.ip_forward=1 |
| Rule đúng nhưng không khớp | Thứ tự rule, hoặc rule nằm trong chain không có hook |
nft -a list ruleset, dùng counter và nft monitor trace |
| Ruleset mất sau reboot | Chưa enable nftables hoặc service đọc file khác |
systemctl cat nftables xem ExecStart |
Tham khảo
- nftables wiki
man nft- Liên quan: SSH hardening, Docker networking troubleshooting