Sổ tay kỹ thuật

nftables firewall – ruleset cơ bản, set, NAT, persist

how-to#nftables#linux#firewall· cập nhật 2026-11-06

Dùng khi cần viết firewall host cho server Linux (jump host, collector, gateway lab) bằng nftables, kèm NAT và cách lưu cấu hình qua reboot.

Sửa trang này
Mục lục

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

/etc/nftables.conf
#!/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ì thay flush ruleset bằng hai dòng table inet filter + delete table inet filter để chỉ làm mới bảng của mình (và bỏ bảng ip nat nếu không cần).
  • policy drop ở forward chỉ cần khi máy làm router/NAT. Muốn forward phải bật net.ipv4.ip_forward=1.
  • 192.0.2.0/24 và eth0/eth1 là 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 boot

Lệ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