/static/codemoomoo2.png

การใช้งาน nftables อย่างครบถ้วน: จาก Packet Filtering พื้นฐานสู่ Advanced Firewall Architecture (nftables Complete Guide)

Complete Guide to nftables: The Modern Linux Firewall Framework

nftables คือกรอบการทำงานกรองแพ็กเก็ต (packet filtering framework) รุ่นใหม่ของ Linux kernel ที่ออกแบบมาแทนที่ iptables/ip6tables/arptables/ebtables ด้วยสถาปัตยกรรมแบบรวมศูนย์ (unified architecture) และ virtual machine ภายใน kernel เอกสารนี้ครอบคลุมตั้งแต่แนวคิดพื้นฐาน โครงสร้าง สถาปัตยกรรม ไวยากรณ์ การกรองแพ็กเก็ต, NAT, connection tracking, sets/maps, rate limiting, ไปจนถึง flowtables, DDoS mitigation และการ migrate จาก iptables ในระดับ production พร้อมตัวอย่างคำสั่งที่ใช้งานได้จริง ไดอะแกรม Mermaid สมการทางคณิตศาสตร์ และชุดข้อมูลสำหรับทดลองในทุกหัวข้อ


1. บทนำ (Introduction)

nftables เป็นระบบย่อยของ kernel (kernel subsystem) ชื่อ nf_tables ที่ทำหน้าที่เป็น packet classification framework รุ่นถัดไปของโครงการ netfilter ซึ่งเป็นโครงสร้างพื้นฐานสำหรับการดักจับและจัดการแพ็กเก็ตเครือข่ายภายใน Linux kernel มาตั้งแต่ปี 1998 บทนี้จะอธิบายที่มา แนวคิดออกแบบ และเหตุผลที่ทำให้ nftables กลายเป็นมาตรฐานใหม่ของ firewall บน Linux

1.1 nftables คืออะไร และเหตุผลที่มาแทน iptables

nftables ถูกออกแบบโดย Patrick McHardy เริ่มพัฒนาในช่วงปี 2008-2009 และถูก merge เข้าสู่ mainline Linux kernel เวอร์ชัน 3.13 ในเดือนมกราคม ปี 2014 จุดประสงค์หลักคือแก้ปัญหาเชิงสถาปัตยกรรมของชุดเครื่องมือ iptables เดิมที่สะสมมานานกว่า 15 ปี

ปัญหาหลักของ iptables ที่ nftables ถูกสร้างขึ้นมาแก้ไข มีดังนี้:

nftables แก้ปัญหาเหล่านี้ด้วยการสร้าง bytecode-based virtual machine ภายใน kernel (เรียกว่า nftables VM) ที่ประมวลผลกฎในรูปแบบชุดคำสั่ง (instruction set) เดียวกันสำหรับทุก address family โดยมี libnftnl และ nft เป็นเครื่องมือฝั่ง user-space เพียงตัวเดียวที่แทนที่ iptables, ip6tables, arptables, ebtables และ ipset ทั้งหมด

1.2 เปรียบเทียบ nftables vs iptables/ipset/ebtables/arptables

ตารางด้านล่างสรุปความแตกต่างเชิงสถาปัตยกรรมที่สำคัญ:

คุณสมบัติ (Feature) iptables + ip6tables + arptables + ebtables + ipset nftables
จำนวนเครื่องมือ user-space 5 เครื่องมือแยกกัน เครื่องมือเดียว (nft)
การรองรับ dual-stack (IPv4/IPv6) ต้องเขียนกฎซ้ำ 2 ชุด (iptables + ip6tables) ใช้ address family inet เขียนครั้งเดียวครอบคลุมทั้งคู่
Native set/map ไม่มี (ต้องพึ่ง ipset ภายนอก) มีในตัว (built-in sets และ maps)
ความซับซ้อนการค้นหา (lookup) O(n) สำหรับกฎเรียงลำดับ, O(1) เฉพาะเมื่อใช้ ipset O(1) เมื่อใช้ named set/map, O(log n) สำหรับ interval set
การโหลด ruleset ใหม่ iptables-restore แบบ atomic บางส่วน nft -f เป็น atomic ทั้งหมดในระดับ transaction
ตัวแทน kernel-space โมดูลแยกตาม family (ip_tables, ip6_tables, ebtables, arp_tables) โมดูลเดียว nf_tables + bytecode VM
Syntax Positional flags (-A, -p, -j) Declarative statement-based syntax
การ debug iptables -L -v -n, ไม่มี trace ในตัว nft monitor trace และ meta nftrace
สถานะการพัฒนา (ปี 2026) Maintenance mode (ใช้งานผ่าน iptables-nft backend) พัฒนาแอ็กทีฟ เป็นค่าเริ่มต้นของ distro หลัก

ประวัติศาสตร์การพัฒนาของระบบกรองแพ็กเก็ตบน Linux สามารถสรุปเป็น timeline ได้ดังนี้:

flowchart TB
    subgraph Era1["ยุคเริ่มต้น / Early Era 1994-1998"]
        A["ipfwadm
Linux kernel 1.2 (1994)
อิงจาก BSD ipfw"] --> B["ipchains
Linux kernel 2.2 (1998)"] end subgraph Era2["ยุค Netfilter/iptables / iptables Era 1998-2008"] B --> C["ก่อตั้งโครงการ Netfilter
โดย Rusty Russell (1998)"] C --> D["iptables เปิดตัว
Linux kernel 2.4 (2000)"] D --> E["ip6tables, arptables, ebtables
แยกเครื่องมือตาม address family"] end subgraph Era3["ยุคเปลี่ยนผ่าน / Transition Era 2008-2014"] E --> F["เริ่มออกแบบ nftables
โดย Patrick McHardy (2008-2009)"] F --> G["nftables merge เข้า mainline kernel
Linux kernel 3.13 (มกราคม 2014)"] end subgraph Era4["ยุคปัจจุบัน / Modern Era 2018-ปัจจุบัน"] G --> H["RHEL/CentOS 8
ใช้ nftables เป็นค่าเริ่มต้น (2019)"] H --> I["Debian 10 Buster
ใช้ nftables เป็นค่าเริ่มต้น (2019)"] I --> J["iptables-nft compatibility layer
รองรับ syntax เดิมบน backend ใหม่"] J --> K["Flowtables + Hardware Offload
เร่งความเร็วระดับ Router/Gateway"] end

1.3 ภาพรวมของ netfilter framework และตำแหน่งของ nftables

Netfilter คือชื่อของโครงการและ framework ระดับ kernel ที่จัดเตรียม hook points ไว้ตลอดเส้นทางการเดินทางของแพ็กเก็ตผ่าน network stack ของ Linux ตัว netfilter เองไม่ได้ตัดสินใจว่าจะ accept หรือ drop แพ็กเก็ต แต่เปิดให้ระบบย่อยอื่น ๆ มา "เกาะ" (register) เข้ากับ hook เหล่านี้เพื่อประมวลผล

ระบบย่อยที่ทำงานอยู่บน netfilter framework ได้แก่:

nftables จึงไม่ใช่สิ่งที่แยกออกจาก netfilter แต่เป็น ผู้บริโภค (consumer) รายหนึ่งของ hook ที่ netfilter framework จัดเตรียมไว้ ซึ่งจะอธิบายรายละเอียดของ hook เหล่านี้ในบทถัดไป


2. สถาปัตยกรรมพื้นฐาน (Core Architecture)

หัวใจของ nftables ประกอบด้วยองค์ประกอบ 4 ระดับที่ซ้อนกันเป็นลำดับชั้น: Address Family → Table → Chain → Rule การเข้าใจลำดับชั้นนี้คือกุญแจสำคัญในการออกแบบ ruleset ที่ถูกต้อง

2.1 Address Family: ip, ip6, inet, arp, bridge, netdev

Address family กำหนดประเภทของแพ็กเก็ตที่ table นั้นจะประมวลผล nftables รองรับ address family ทั้งหมด 6 ประเภท:

Address Family ประเภทแพ็กเก็ตที่กรอง กรณีใช้งานทั่วไป
ip เฉพาะ IPv4 Firewall ที่ต้องการกฎแยกสำหรับ IPv4 โดยเฉพาะ
ip6 เฉพาะ IPv6 Firewall ที่ต้องการกฎแยกสำหรับ IPv6 โดยเฉพาะ
inet ทั้ง IPv4 และ IPv6 พร้อมกัน มาตรฐานใหม่สำหรับ host-based firewall (แนะนำ)
arp ARP (Address Resolution Protocol) ป้องกัน ARP spoofing บน Layer 2/3
bridge แพ็กเก็ตที่ผ่าน Linux bridge (Layer 2) ทดแทน ebtables สำหรับกรองที่ bridge
netdev แพ็กเก็ตที่ interface ระดับ ingress ก่อนเข้า network stack Filtering เร็วมาก เหมาะกับ DDoS mitigation

ข้อมูลตัวอย่างสำหรับทดลอง: สร้าง table แบบ inet เพื่อดูว่าเขียนกฎครั้งเดียวครอบคลุมทั้ง IPv4/IPv6:

# สร้าง table ชื่อ filter ใน address family inet (รองรับทั้ง IPv4/IPv6)
nft add table inet filter

# ตรวจสอบว่า table ถูกสร้างแล้ว
nft list tables
# ผลลัพธ์ที่คาดหวัง: table inet filter

2.2 Tables — การจัดกลุ่ม ruleset

Table คือ container ระดับบนสุดที่ใช้จัดกลุ่ม chain เข้าด้วยกัน table หนึ่งผูกกับ address family หนึ่งเสมอ และสามารถมีได้หลาย table ในระบบเดียวกัน (เช่น table สำหรับ filtering แยกจาก table สำหรับ NAT) การลบ table จะลบทุก chain และ rule ภายในโดยอัตโนมัติ

# ไวยากรณ์ทั่วไป: nft <add|delete|flush> table <family> <name>
nft add table inet myfw          # สร้าง table
nft flush table inet myfw        # ล้างกฎทั้งหมดใน table แต่ยังคง table ไว้
nft delete table inet myfw       # ลบ table ทั้งหมด

2.3 Chains — Base chain vs Regular chain

Chain คือกลุ่มของกฎ (rule) ที่เรียงตามลำดับ nftables มี chain 2 ประเภท:

  1. Base chain (chain หลัก): ผูกเข้ากับ hook ของ netfilter โดยตรง แพ็กเก็ตที่ผ่าน hook นั้นจะถูกส่งเข้ามาประมวลผลใน chain นี้โดยอัตโนมัติ ต้องระบุ type, hook, priority เสมอ
  2. Regular chain (chain ย่อย): ไม่ผูกกับ hook ใด ๆ ทำหน้าที่เป็นเหมือน subroutine ที่ถูกเรียกใช้ผ่าน jump หรือ goto จาก chain อื่นเท่านั้น มีประโยชน์สำหรับการจัดระเบียบกฎที่ใช้ซ้ำ
# Base chain: ผูกกับ hook input โดยตรง
nft add chain inet myfw input { type filter hook input priority 0 \; policy drop \; }

# Regular chain: ไม่ผูกกับ hook ใด ๆ เรียกใช้ด้วย jump/goto เท่านั้น
nft add chain inet myfw rate_limit_checks
nft add rule inet myfw input tcp dport 22 jump rate_limit_checks

2.4 Hooks: prerouting, input, forward, output, postrouting, ingress

Hook คือจุดที่ netfilter framework แทรกตัวเองเข้าไปใน network stack ของ kernel เพื่อดักแพ็กเก็ต มี 6 hook หลัก (5 hook มาตรฐาน + ingress ที่เร็วกว่า):

flowchart TB
    IN["แพ็กเก็ตเข้า
Incoming Packet"] --> ING["ingress hook
(netdev family เท่านั้น)"] ING --> PR["prerouting hook
ก่อนตัดสินใจ routing"] PR --> ROUTE{"Routing Decision
ปลายทางคือเครื่องนี้เอง
หรือไม่?"} ROUTE -->|"ปลายทางคือเครื่องนี้ / local"| IH["input hook
ก่อนส่งให้ local process"] ROUTE -->|"ส่งต่อ / forward ไปเครื่องอื่น"| FH["forward hook"] IH --> LP["Local Process
(application ในเครื่อง)"] LP --> OH["output hook
แพ็กเก็ตที่สร้างจาก local process"] FH --> POH["postrouting hook"] OH --> POH POH --> OUT["แพ็กเก็ตออก
Outgoing Packet"]

รายละเอียดของแต่ละ hook:

2.5 Priority และลำดับการประมวลผล

เมื่อมีหลาย base chain ผูกกับ hook เดียวกัน (เช่น ทั้ง filter table และ nat table ต่างก็มี chain ที่ hook prerouting) ค่า priority (จำนวนเต็ม สามารถติดลบได้) จะเป็นตัวกำหนดลำดับการประมวลผล ค่ายิ่งน้อย ยิ่งประมวลผลก่อน

ชื่อ Priority มาตรฐาน ค่าตัวเลข การใช้งานทั่วไป
raw -300 ก่อน connection tracking (bypass conntrack)
mangle -150 แก้ไข packet header/metadata
dstnat -100 DNAT (ทำงานที่ prerouting)
filter 0 การกรองแพ็กเก็ตทั่วไป (ค่าเริ่มต้น)
security 50 SELinux/AppArmor hooks
srcnat 100 SNAT/Masquerade (ทำงานที่ postrouting)
# ตัวอย่าง: ตั้ง priority ชัดเจนด้วยตัวเลข
nft add chain inet myfw input { type filter hook input priority 0 \; policy drop \; }
nft add chain ip nat prerouting { type nat hook prerouting priority -100 \; }

2.6 Policy (accept/drop) ของ base chain

Policy คือค่าเริ่มต้น (default verdict) ที่ใช้เมื่อไม่มีกฎใดใน chain ตรงกับแพ็กเก็ตนั้นเลย (fall off the end of the chain) มี 2 ค่าเท่านั้นคือ accept และ drop

แนวปฏิบัติที่ดี (best practice): ใช้หลักการ default deny คือตั้ง policy เป็น drop แล้วเปิดเฉพาะสิ่งที่ต้องการอนุญาตด้วยกฎ accept เท่านั้น

# แนวทาง default-deny ที่แนะนำสำหรับ input chain
nft add chain inet myfw input { type filter hook input priority 0 \; policy drop \; }
nft add rule inet myfw input iif lo accept
nft add rule inet myfw input ct state established,related accept
nft add rule inet myfw input tcp dport 22 accept
# ไม่จำเป็นต้องมีกฎ drop ท้ายสุด เพราะ policy ทำหน้าที่นั้นอยู่แล้ว

3. การติดตั้งและเริ่มต้นใช้งาน (Installation & Getting Started)

3.1 ติดตั้งบน distro ต่าง ๆ (Debian/Ubuntu, RHEL/Fedora, Arch)

# Debian / Ubuntu
sudo apt update && sudo apt install -y nftables

# RHEL / Fedora / CentOS Stream
sudo dnf install -y nftables

# Arch Linux
sudo pacman -S nftables

# ตรวจสอบเวอร์ชันหลังติดตั้ง (แนะนำเวอร์ชัน 0.9.x ขึ้นไปเพื่อรองรับฟีเจอร์ใหม่ครบถ้วน)
nft --version

หมายเหตุสำคัญ: ต้องตรวจสอบด้วยว่า Linux kernel ของระบบ (uname -r) มีเวอร์ชันตั้งแต่ 3.13 ขึ้นไป (แนะนำ 5.x ขึ้นไปเพื่อรองรับ flowtable และ feature ใหม่ที่กล่าวถึงในบทที่ 13)

3.2 โครงสร้างไฟล์ /etc/nftables.conf และการเปิดใช้ systemd service

ไฟล์ /etc/nftables.conf เป็นไฟล์ script หลักที่ระบบจะโหลดเมื่อบูตผ่าน nftables.service โครงสร้างไฟล์ทั่วไปมีดังนี้:

#!/usr/sbin/nft -f
# /etc/nftables.conf — ไฟล์ ruleset หลัก

flush ruleset

table inet filter {
    chain input {
        type filter hook input priority 0; policy drop;

        iif lo accept
        ct state established,related accept
        ct state invalid drop
        tcp dport 22 accept
        icmp type echo-request accept
    }

    chain forward {
        type filter hook forward priority 0; policy drop;
    }

    chain output {
        type filter hook output priority 0; policy accept;
    }
}

การเปิดใช้งานและจัดการ service:

sudo systemctl enable nftables       # ให้เริ่มทำงานอัตโนมัติเมื่อบูต
sudo systemctl start nftables        # เริ่มทำงานทันที
sudo systemctl restart nftables      # โหลดไฟล์ /etc/nftables.conf ใหม่
sudo systemctl status nftables       # ตรวจสอบสถานะ

3.3 คำสั่งพื้นฐาน: nft list ruleset, nft flush ruleset, nft -f

คำสั่ง หน้าที่
nft list ruleset แสดง ruleset ทั้งหมดที่ทำงานอยู่ในปัจจุบัน (running configuration)
nft list ruleset > backup.nft สำรอง ruleset ปัจจุบันออกเป็นไฟล์
nft flush ruleset ล้างกฎทั้งหมดในระบบ (ใช้ระวัง อาจทำให้เปิดทุก connection ชั่วคราว)
nft -f /path/to/file.nft โหลด ruleset จากไฟล์สคริปต์แบบ atomic
nft -c -f /path/to/file.nft ตรวจสอบ syntax (check-only) โดยไม่โหลดจริง — ควรใช้ก่อน apply เสมอ

ชุดข้อมูลทดลอง: ทดสอบ workflow การแก้ไขที่ปลอดภัย:

# 1. สำรอง ruleset ปัจจุบันก่อนแก้ไขเสมอ
nft list ruleset > /root/nftables-backup-$(date +%Y%m%d).nft

# 2. ตรวจสอบ syntax ของไฟล์ใหม่ก่อน apply จริง
nft -c -f /etc/nftables.conf && echo "Syntax OK" || echo "Syntax Error!"

# 3. หากถูกต้อง จึง apply
sudo nft -f /etc/nftables.conf

4. ไวยากรณ์และการเขียนกฎ (Syntax & Rule Writing)

4.1 รูปแบบคำสั่ง interactive vs script file

nftables รองรับการเขียนกฎได้ 2 รูปแบบ:

  1. Interactive mode: เรียกใช้คำสั่ง nft ทีละบรรทัดผ่าน shell โดยตรง เหมาะสำหรับทดสอบหรือแก้ไขชั่วคราว
  2. Script file mode: เขียนกฎทั้งหมดไว้ในไฟล์ .nft แล้วโหลดด้วย nft -f เหมาะสำหรับ production เพราะโหลดแบบ atomic ทั้งไฟล์
# Interactive mode
nft add table inet t1
nft add chain inet t1 c1 { type filter hook input priority 0 \; }
nft add rule inet t1 c1 tcp dport 80 accept

# Script file mode (เนื้อหาในไฟล์ example.nft)
# table inet t1 {
#     chain c1 {
#         type filter hook input priority 0;
#         tcp dport 80 accept
#     }
# }
nft -f example.nft

4.2 การสร้าง table/chain/rule แบบ command line

รูปแบบไวยากรณ์ (syntax) โดยทั่วไปของ nft:

nft [add|insert|delete|replace] rule <family> <table> <chain> [handle <n>] <matches...> <statement>
# add: เพิ่มกฎท้าย chain
nft add rule inet filter input tcp dport 443 accept

# insert: เพิ่มกฎที่ตำแหน่งบนสุดของ chain
nft insert rule inet filter input tcp dport 22 accept

# แสดงกฎพร้อม handle number (จำเป็นสำหรับ delete/replace)
nft -a list chain inet filter input

# delete: ลบกฎด้วย handle number ที่ได้จากคำสั่งด้านบน
nft delete rule inet filter input handle 4

# replace: แทนที่กฎเดิมด้วยกฎใหม่ (ต้องระบุ handle)
nft replace rule inet filter input handle 4 tcp dport 8443 accept

4.3 ตัวแปร (variables) และ include statement

ตัวแปร (define) ช่วยลดการเขียนค่าซ้ำซ้อนและทำให้ ruleset อ่านง่ายขึ้น ส่วน include ช่วยแยกไฟล์ ruleset ขนาดใหญ่ออกเป็นหลายไฟล์ย่อยเพื่อความเป็นระเบียบ

#!/usr/sbin/nft -f

# ประกาศตัวแปรแบบค่าเดี่ยว
define WAN_IF = eth0
define LAN_IF = eth1

# ประกาศตัวแปรแบบ set (รายการหลายค่า)
define TRUSTED_NETS = { 192.168.1.0/24, 10.0.0.0/8 }
define ALLOWED_TCP_PORTS = { 22, 80, 443 }

table inet filter {
    chain input {
        type filter hook input priority 0; policy drop;
        iifname $LAN_IF ip saddr $TRUSTED_NETS accept
        tcp dport $ALLOWED_TCP_PORTS accept
    }
}

# แยกไฟล์ย่อยออกไปเพื่อความเป็นระเบียบ
include "/etc/nftables.d/*.nft"

4.4 Comment และการจัดระเบียบไฟล์ ruleset

nftables รองรับ comment แบบ # และยังสามารถแนบ comment เข้ากับตัวกฎแต่ละบรรทัดได้โดยตรงด้วย keyword comment ซึ่งจะถูกเก็บไว้ใน kernel และแสดงเมื่อ list ruleset — มีประโยชน์มากสำหรับการทำเอกสารกฎในทีมงาน

table inet filter {
    chain input {
        type filter hook input priority 0; policy drop;

        # อนุญาต loopback interface เสมอ
        iif lo accept

        # กฎนี้แนบ comment ไว้ในตัว rule เอง (จะแสดงเมื่อ list ruleset)
        tcp dport 22 accept comment "SSH access - ticket INFRA-4521"
        tcp dport 443 accept comment "HTTPS public web server"
    }
}

5. การกรองแพ็กเก็ตพื้นฐาน (Basic Packet Filtering)

5.1 Matching โดย interface (iifname/oifname)

iifname (input interface name) และ oifname (output interface name) ใช้จับคู่แพ็กเก็ตตามชื่อ network interface มีประโยชน์มากสำหรับ router/gateway ที่มีหลาย interface

# อนุญาตทุกอย่างจาก loopback
nft add rule inet filter input iifname lo accept

# บล็อกทุกอย่างที่มาจาก WAN โดยตรง ยกเว้นกฎที่อนุญาตไว้ก่อนหน้า
nft add rule inet filter input iifname eth0 counter drop

# อนุญาตเฉพาะแพ็กเก็ตที่ออกไปทาง WAN interface
nft add rule inet filter output oifname eth0 accept

5.2 Matching โดย IP address/subnet

# จับคู่ source address เดี่ยว
nft add rule inet filter input ip saddr 203.0.113.10 drop

# จับคู่ subnet ด้วย CIDR notation
nft add rule inet filter input ip saddr 192.168.1.0/24 accept

# จับคู่ destination address
nft add rule inet filter output ip daddr 8.8.8.8 accept

# ปฏิเสธด้วยเครื่องหมาย != (not equal)
nft add rule inet filter input ip saddr != 192.168.1.0/24 drop

5.3 Matching โดย protocol และ port (tcp/udp dport/sport)

# tcp destination port เดี่ยว
nft add rule inet filter input tcp dport 22 accept

# tcp destination port แบบช่วง (range)
nft add rule inet filter input tcp dport 1024-65535 accept

# tcp destination port แบบหลายค่า (list) ในวงเล็บปีกกา
nft add rule inet filter input tcp dport { 80, 443, 8080 } accept

# udp source port
nft add rule inet filter input udp sport 53 accept

# รวม protocol + saddr + dport ในกฎเดียว
nft add rule inet filter input ip saddr 10.0.0.0/8 tcp dport 3306 accept

5.4 Verdict statements: accept, drop, reject, queue, continue, jump, goto

Verdict พฤติกรรม
accept ยอมรับแพ็กเก็ต หยุดการประมวลผลใน chain นี้ (แต่ยังผ่าน hook ถัดไปได้)
drop ทิ้งแพ็กเก็ตแบบเงียบ (ไม่ตอบกลับผู้ส่ง)
reject ปฏิเสธและส่ง ICMP/TCP RST กลับไปแจ้งผู้ส่ง
queue ส่งแพ็กเก็ตไปยัง user-space ผ่าน NFQUEUE (เช่นสำหรับ IDS/IPS)
continue ประมวลผลกฎถัดไปต่อ (ใช้ร่วมกับ counter/log ที่ไม่ต้องการตัดสินใจ)
jump <chain> กระโดดไป regular chain อื่น และกลับมาทำงานต่อที่กฎถัดไปเมื่อ chain นั้นจบ
goto <chain> กระโดดไป regular chain อื่นแบบไม่กลับมา (เหมือน tail call)
# reject พร้อมระบุประเภทข้อความตอบกลับ
nft add rule inet filter input tcp dport 8080 reject with tcp reset
nft add rule inet filter input ip saddr 10.99.0.0/16 reject with icmp type host-prohibited

# jump ไปยัง regular chain แล้วกลับมาทำงานต่อ
nft add chain inet filter ssh_checks
nft add rule inet filter input tcp dport 22 jump ssh_checks
nft add rule inet filter ssh_checks ct state new limit rate 5/minute accept
nft add rule inet filter ssh_checks ct state new drop

5.5 การสร้าง input/output/forward chain สำหรับ host-based firewall

ชุดข้อมูลทดลอง: ตัวอย่าง host-based firewall ที่พร้อมใช้งานจริงสำหรับเซิร์ฟเวอร์ทั่วไป (เปิด SSH, HTTP, HTTPS):

#!/usr/sbin/nft -f
flush ruleset

table inet host_firewall {
    chain input {
        type filter hook input priority 0; policy drop;

        iif lo accept
        ct state established,related accept
        ct state invalid drop

        icmp type { echo-request, destination-unreachable, time-exceeded } accept
        icmpv6 type { echo-request, nd-neighbor-solicit, nd-neighbor-advert } accept

        tcp dport 22 accept comment "SSH"
        tcp dport { 80, 443 } accept comment "Web server"
    }

    chain forward {
        type filter hook forward priority 0; policy drop;
    }

    chain output {
        type filter hook output priority 0; policy accept;
    }
}

6. Connection Tracking (Stateful Firewall)

Connection tracking (conntrack) เป็นระบบที่ทำให้ firewall สามารถ "จดจำ" สถานะของการเชื่อมต่อแต่ละคู่ (5-tuple: protocol, saddr, sport, daddr, dport) ได้ ทำให้เขียนกฎแบบ stateful ได้ง่ายกว่าการเขียนกฎแบบ stateless ที่ต้องเปิดพอร์ตทั้งสองทิศทางเอง

State ความหมาย
new แพ็กเก็ตแรกของการเชื่อมต่อใหม่ที่ conntrack ยังไม่เคยเห็นมาก่อน
established แพ็กเก็ตที่เป็นส่วนหนึ่งของการเชื่อมต่อที่มีการตอบกลับแล้วอย่างน้อยหนึ่งครั้ง
related แพ็กเก็ตที่เกี่ยวข้องกับ connection ที่มีอยู่แต่ไม่ใช่ connection เดียวกันโดยตรง เช่น ICMP error ที่เกิดจาก connection เดิม หรือ FTP data channel ที่เกิดจาก control channel
invalid แพ็กเก็ตที่ conntrack ไม่สามารถระบุสถานะได้ หรือผิดปกติ (เช่น TCP flag ที่เป็นไปไม่ได้) ควร drop เสมอ

6.2 การเขียนกฎแบบ stateful firewall ทั่วไป

รูปแบบมาตรฐานที่ใช้กันแพร่หลายที่สุดในโลกจริงคือ 3 บรรทัดแรกของทุก base chain:

nft add rule inet filter input ct state established,related accept
nft add rule inet filter input ct state invalid drop
nft add rule inet filter input ct state new tcp dport 22 accept

หลักการคือ: อนุญาต traffic ขาตอบกลับ (established/related) ก่อนเป็นอันดับแรกเสมอ เพื่อประสิทธิภาพ (ไม่ต้องไล่ตรวจกฎอื่นซ้ำสำหรับ connection ที่รู้จักแล้ว) จากนั้นจึง drop invalid และค่อยตรวจสอบกฎเฉพาะสำหรับ new connection

6.3 conntrack helpers (FTP, SIP ฯลฯ)

โปรโตคอลบางตัว (เช่น FTP แบบ active mode, SIP, PPTP) เปิด connection รองเพิ่มเติมแบบไดนามิกด้วย IP/port ที่ตกลงกันภายใน payload ของ connection หลัก ทำให้ conntrack ทั่วไปตรวจจับไม่ได้ จึงต้องใช้ conntrack helper module เพื่อ "แกะ" (inspect) เนื้อหา payload และสร้าง related connection ให้อัตโนมัติ

# โหลด kernel module conntrack helper สำหรับ FTP
modprobe nf_conntrack_ftp

# กำหนด helper ผ่าน ct helper object ใน nftables
table inet filter {
    ct helper ftp_helper {
        type "ftp" protocol tcp
    }

    chain prerouting {
        type filter hook prerouting priority -150;
        tcp dport 21 ct helper set "ftp_helper"
    }
}

ข้อควรระวังด้านความปลอดภัย: conntrack helper แบบ auto-assign ถูกปิดโดยค่าเริ่มต้นในเคอร์เนลรุ่นใหม่ เนื่องจากเคยเป็นช่องโหว่ด้านความปลอดภัย ควรกำหนด helper แบบ explicit ผ่าน ct helper set เท่านั้นตามตัวอย่างข้างต้น


7. NAT (Network Address Translation)

7.1 SNAT (Source NAT)

SNAT ใช้เปลี่ยน source address ของแพ็กเก็ตขาออก เหมาะสำหรับกรณีที่มี public IP แบบ คงที่ (static) เท่านั้น ทำงานที่ hook postrouting

nft add table ip nat
nft add chain ip nat postrouting { type nat hook postrouting priority 100 \; }
nft add rule ip nat postrouting ip saddr 192.168.1.0/24 oifname eth0 snat to 203.0.113.5

7.2 Masquerade (สำหรับ dynamic IP)

Masquerade คือ SNAT รูปแบบพิเศษที่ไม่ต้องระบุ IP ปลายทางตายตัว แต่จะอ่าน IP ปัจจุบันของ outbound interface โดยอัตโนมัติ เหมาะกับกรณี public IP เป็นแบบ dynamic (DHCP/PPPoE) — เป็นรูปแบบที่ router ตามบ้านใช้กันมากที่สุด

nft add rule ip nat postrouting oifname "ppp0" masquerade

7.3 DNAT (Destination NAT / port forwarding)

DNAT ใช้เปลี่ยน destination address/port ของแพ็กเก็ตขาเข้า ทำงานที่ hook prerouting — เป็นพื้นฐานของ port forwarding

nft add chain ip nat prerouting { type nat hook prerouting priority -100 \; }

# port forward: แพ็กเก็ตที่มาที่ public IP พอร์ต 8080 ให้ส่งต่อไปยัง web server ภายใน
nft add rule ip nat prerouting iifname eth0 tcp dport 8080 dnat to 192.168.1.100:80

7.4 Redirect (transparent proxy)

Redirect เป็น DNAT รูปแบบพิเศษที่ส่งแพ็กเก็ตกลับไปยัง เครื่องเดียวกัน (localhost) โดยอัตโนมัติ ใช้กันมากในการทำ transparent proxy

# redirect port 80 ทั้งหมดไปยัง proxy ที่ฟังอยู่ port 3128 บนเครื่องเดียวกัน
nft add rule ip nat prerouting tcp dport 80 redirect to 3128

7.5 NAT ร่วมกับ IPv6 (NPTv6)

เนื่องจากปรัชญาของ IPv6 คือการมี address พอสำหรับทุกอุปกรณ์โดยไม่ต้องทำ NAT แบบ IPv4 แต่ในบางองค์กรยังต้องการซ่อน prefix ภายในเพื่อความเป็นส่วนตัวหรือทำ multihoming จึงใช้ NPTv6 (Network Prefix Translation for IPv6) ซึ่งแปลงเฉพาะ network prefix โดยไม่แตะ interface identifier (คงความเป็น 1:1 mapping ไว้ ต่างจาก NAT44 ที่เป็น many-to-one)

nft add table ip6 nptv6
nft add chain ip6 nptv6 postrouting { type nat hook postrouting priority 100 \; }
nft add rule ip6 nptv6 postrouting oifname eth0 snat ip6 prefix to 2001:db8:1::/48

8. Sets และ Maps

8.1 Anonymous set vs Named set

Anonymous set คือ set ที่ประกาศตรง ๆ ในตัวกฎด้วยวงเล็บปีกกา { } ใช้ครั้งเดียวแล้วหายไปพร้อมกฎนั้น ส่วน Named set คือ set ที่ประกาศแยกต่างหากด้วยชื่อ สามารถแก้ไข เพิ่ม/ลบสมาชิกได้แบบ dynamic โดยไม่ต้องแก้ไข rule ซึ่งเร็วกว่าการ reload ทั้ง ruleset มาก

# Anonymous set — ฝังอยู่ในกฎโดยตรง
nft add rule inet filter input tcp dport { 22, 80, 443 } accept

# Named set — ประกาศแยก แก้ไขได้ภายหลังโดยไม่กระทบกฎ
nft add set inet filter blacklist_ips { type ipv4_addr \; }
nft add rule inet filter input ip saddr @blacklist_ips drop

# เพิ่ม/ลบสมาชิกใน named set แบบ dynamic โดยไม่ต้อง reload ruleset
nft add element inet filter blacklist_ips { 203.0.113.66, 198.51.100.23 }
nft delete element inet filter blacklist_ips { 203.0.113.66 }

8.2 Interval set (CIDR ranges, port ranges)

การเพิ่ม flag interval ให้ set ทำให้เก็บและค้นหาข้อมูลแบบช่วงได้ (เช่น CIDR block หรือช่วงพอร์ต) โดยใช้โครงสร้างข้อมูลภายในแบบ interval tree ทำให้ lookup เป็น O(log n)

nft add set inet filter trusted_networks { type ipv4_addr \; flags interval \; }
nft add element inet filter trusted_networks { 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 }
nft add rule inet filter input ip saddr @trusted_networks accept

8.3 Timeout set (dynamic/ephemeral entries)

set ที่มี flag timeout จะทำให้สมาชิกแต่ละตัวมีอายุ (TTL) และถูกลบออกอัตโนมัติเมื่อครบกำหนด — เป็นเทคนิคหลักที่ใช้สร้างระบบ auto-ban สำหรับ brute-force protection

nft add set inet filter dynamic_ban { type ipv4_addr \; flags dynamic,timeout \; timeout 10m \; }

nft add rule inet filter input ip saddr @dynamic_ban drop
nft add rule inet filter input tcp dport 22 ct state new \
    add @dynamic_ban { ip saddr limit rate over 3/minute }

8.4 Verdict maps สำหรับ routing rule แบบ table-driven

Map คล้าย set แต่แต่ละ key จะจับคู่กับ value — เมื่อ value เป็น verdict (accept/drop/jump) จะเรียกว่า verdict map ทำให้เขียน logic แบบ "table-driven" แทนการเขียนกฎซ้ำหลายบรรทัด

nft add map inet filter port_to_verdict { type inet_service : verdict \; }
nft add element inet filter port_to_verdict { 22 : accept, 23 : drop, 80 : accept, 443 : accept }
nft add rule inet filter input tcp dport vmap @port_to_verdict

8.5 การใช้ set ร่วมกับ blacklist/whitelist IP

ชุดข้อมูลทดลอง: ระบบ whitelist/blacklist ที่ใช้งานได้จริง โดย whitelist มีสิทธิ์เหนือ blacklist เสมอ:

table inet filter {
    set whitelist { type ipv4_addr; flags interval; elements = { 10.0.0.0/8 } }
    set blacklist { type ipv4_addr; flags interval; elements = { 198.51.100.0/24 } }

    chain input {
        type filter hook input priority 0; policy drop;
        ip saddr @whitelist accept
        ip saddr @blacklist drop
        ct state established,related accept
    }
}

9. Rate Limiting และ Quota

9.1 limit rate สำหรับป้องกัน brute force/flood

limit rate ใน nftables ใช้อัลกอริทึม token bucket ภายใน kernel เพื่อจำกัดจำนวนแพ็กเก็ต/connection ต่อหน่วยเวลา หลักการคือมี "ถัง" (bucket) ที่เติม token ด้วยอัตราคงที่ และทุกแพ็กเก็ตที่ผ่านต้องใช้ token หนึ่งหน่วย หากถังว่างแพ็กเก็ตนั้นจะถูกปฏิเสธ (ไม่ตรงกับกฎ)

สมการอัตราการเติม token ของ token bucket:

r = NT

โดยที่:

ตัวอย่างการคำนวณ: หากตั้งค่า limit rate 5/minute จะได้ N = 5, T = 60 วินาที ดังนั้น r = 5/60 ≈ 0.083 token ต่อวินาที หมายความว่าทุก ๆ 12 วินาทีจะมี 1 token ใหม่ถูกเติมเข้าถัง

# จำกัดการเชื่อมต่อ SSH ใหม่ไม่เกิน 5 ครั้งต่อนาที ป้องกัน brute force
nft add rule inet filter input tcp dport 22 ct state new limit rate 5/minute accept
nft add rule inet filter input tcp dport 22 ct state new drop

9.2 limit rate over สำหรับจำกัด bandwidth

limit rate over กลับตรรกะจาก "อนุญาตถ้าไม่เกิน" เป็น "ตรงกับกฎเมื่อเกินขีดจำกัด" มีประโยชน์สำหรับ trigger การ log หรือ drop เมื่อเกิน bandwidth ที่กำหนด

# ตรวจจับและ log เมื่อ traffic เกิน 100 Mbytes/second (สำหรับ monitoring)
nft add rule inet filter forward limit rate over 100 mbytes/second log prefix "BW-EXCEED: " drop

9.3 quota statement สำหรับจำกัดปริมาณข้อมูล

quota แตกต่างจาก limit rate ตรงที่จำกัดปริมาณรวม (cumulative total) ไม่ใช่อัตราต่อเวลา เหมาะกับการจำกัดโควตาการใช้งานรายเดือน/รายวัน

# จำกัดปริมาณ traffic ขาออกรวมไม่เกิน 10 GB แล้วจึง drop ส่วนเกิน
nft add rule inet filter output ip daddr 198.51.100.0/24 quota 10 gbytes drop

10. Logging และ Monitoring

10.1 log statement และ log prefix

log statement จะส่งข้อมูลแพ็กเก็ตที่ตรงกฎไปยัง kernel log (dmesg/journald) โดยไม่ตัดสินใจ accept/drop เอง (เป็น non-terminating statement) จึงต้องวางไว้ก่อนกฎที่มี verdict เสมอ

nft add rule inet filter input tcp dport 22 ct state new \
    log prefix "SSH-NEW: " flags all \
    limit rate 5/minute accept

10.2 counter statement

counter ใช้นับจำนวนแพ็กเก็ตและ byte ที่ผ่านกฎนั้น มีประโยชน์มากสำหรับ monitoring และ troubleshooting โดยไม่ต้องเปลี่ยน verdict

nft add rule inet filter input tcp dport 443 counter accept

# ดูค่า counter ปัจจุบัน
nft list ruleset | grep -A1 "dport 443"
# ผลลัพธ์ตัวอย่าง: tcp dport 443 counter packets 15234 bytes 8912004 accept

10.3 nft monitor สำหรับ debug แบบ real-time

nft monitor แสดงเหตุการณ์แบบ real-time ทั้งการเปลี่ยนแปลง ruleset และ trace ของแพ็กเก็ต มีประโยชน์มากในการ debug ขณะทดสอบกฎใหม่

# ติดตามการเปลี่ยนแปลง ruleset (เมื่อมีคนอื่น add/delete กฎ)
nft monitor

# ติดตามเฉพาะ trace ของแพ็กเก็ต (ต้องเปิด rule ที่มี meta nftrace set 1 ก่อน — ดูบทที่ 18)
nft monitor trace

10.4 ส่ง log ไปยัง syslog/journald และวิเคราะห์

# ตรวจสอบ log ที่ nftables ส่งออกผ่าน journald แบบ real-time
journalctl -k -f | grep "SSH-NEW"

# กรอง log เฉพาะช่วงเวลาที่สนใจ
journalctl -k --since "1 hour ago" | grep "BW-EXCEED"

ชุดข้อมูลทดลอง: สคริปต์วิเคราะห์ log แบบง่ายเพื่อนับจำนวน IP ที่ถูก log บ่อยที่สุด:

#!/bin/bash
# analyze_ssh_attempts.sh — สรุป IP ที่พยายามเชื่อมต่อ SSH บ่อยที่สุดจาก kernel log
journalctl -k --since "24 hours ago" | grep "SSH-NEW" | \
    grep -oP 'SRC=\K[0-9.]+' | sort | uniq -c | sort -rn | head -20

11. Matching ขั้นสูง (Advanced Matching)

11.1 Meta expressions (mark, iif, oif, skuid, cgroup)

Meta expression คือข้อมูล metadata ของแพ็กเก็ตที่ไม่ได้อยู่ใน header โดยตรง แต่ kernel เก็บไว้ระหว่างการประมวลผล เช่น mark ที่แปะไว้เอง, user ID ของ process ที่สร้างแพ็กเก็ต

# mark: ใช้ "แปะป้าย" แพ็กเก็ตเพื่อส่งต่อให้ chain/policy routing อื่นใช้งาน
nft add rule inet filter output meta mark set 0x1 ip daddr 10.0.0.0/8

# skuid: จับคู่ตาม user ID ของ process ที่สร้างแพ็กเก็ต (ใช้กับ output chain เท่านั้น)
nft add rule inet filter output skuid root accept
nft add rule inet filter output skuid != root tcp dport 25 drop

# cgroup: จับคู่ตาม control group (มีประโยชน์กับ container/systemd service isolation)
nft add rule inet filter output socket cgroupv2 level 2 "system.slice/docker.service" accept

11.2 Payload matching ระดับ header (IP/IPv6/TCP/UDP/ICMP)

nftables สามารถจับคู่ field ใด ๆ ใน header ของ protocol ได้โดยตรงผ่าน payload expression ไม่จำกัดเฉพาะ field ที่มี shortcut (เช่น ip saddr) เท่านั้น

# จับคู่ IP TTL (มีประโยชน์สำหรับตรวจจับ traceroute หรือ TTL evasion)
nft add rule inet filter input ip ttl < 5 drop

# จับคู่ IPv6 hop limit
nft add rule inet filter input ip6 hoplimit 1 accept

# จับคู่ ICMP type แบบละเอียด
nft add rule inet filter input icmp type { echo-request, echo-reply, time-exceeded } accept

# จับคู่ IP header length field โดยตรง (raw payload access)
nft add rule inet filter input @ih,0,4 0x4 accept

11.3 การตรวจสอบ TCP flags และ options

# ตรวจจับ NULL scan (ไม่มี flag ใดตั้งเลย) — เป็นเทคนิคการสแกนพอร์ตแบบซ่อนเร้น
nft add rule inet filter input tcp flags & (fin|syn|rst|psh|ack|urg) == 0x0 drop

# ตรวจจับ Xmas scan (ตั้ง flag FIN, PSH, URG พร้อมกัน)
nft add rule inet filter input tcp flags & (fin|syn|rst|psh|ack|urg) == (fin|psh|urg) drop

# ตรวจจับ SYN-FIN ผิดปกติ (เป็นไปไม่ได้ตาม TCP state machine ปกติ)
nft add rule inet filter input tcp flags & (fin|syn) == (fin|syn) drop

# อนุญาตเฉพาะแพ็กเก็ต SYN แรกของการเชื่อมต่อใหม่ (ป้องกัน TCP flag ผิดปกติ)
nft add rule inet filter input tcp flags != syn ct state new drop

11.4 fib expression (routing lookup)

fib (Forwarding Information Base) expression ให้ nftables สามารถสอบถามตาราง routing ได้โดยตรงระหว่างประมวลผลกฎ มีประโยชน์มากสำหรับ anti-spoofing โดยไม่ต้องพึ่ง rp_filter ของ kernel

# ตรวจสอบว่า source address ของแพ็กเก็ตขาเข้ามีเส้นทาง reverse-path ที่ถูกต้องหรือไม่
nft add rule inet filter prerouting fib saddr . iif oif missing drop

# ตรวจสอบว่า destination address เป็น local address ของเครื่องนี้หรือไม่ (ใช้แยก input/forward logic)
nft add rule inet filter prerouting fib daddr type local accept

12. Bridge และ netdev Family

12.1 การกรองบน Layer 2 (bridge family) แทน ebtables

address family bridge ใช้กรองแพ็กเก็ตที่ระดับ Layer 2 (Ethernet frame) ก่อนที่จะถูกส่งผ่าน Linux bridge — เป็นสิ่งที่ทดแทน ebtables เดิมได้อย่างสมบูรณ์ เหมาะสำหรับสภาพแวดล้อม virtualization ที่ bridge interface เชื่อมต่อ VM หลายตัวเข้าด้วยกัน

nft add table bridge filter
nft add chain bridge filter forward { type filter hook forward priority 0 \; }

# กรองตาม MAC address (Layer 2)
nft add rule bridge filter forward ether saddr 00:11:22:33:44:55 drop

# กรองตาม EtherType (เช่น บล็อก IPX แบบเก่า)
nft add rule bridge filter forward ether type 0x8137 drop

# อนุญาตเฉพาะ ARP และ IPv4/IPv6 traffic ผ่าน bridge
nft add rule bridge filter forward ether type { arp, ip, ip6 } accept

12.2 netdev family สำหรับ ingress filtering (เร็วก่อนเข้า network stack)

address family netdev ทำงานที่ hook ingress ซึ่งอยู่ก่อน prerouting — เป็นจุดที่เร็วที่สุดในการดักแพ็กเก็ต เพราะยังไม่ผ่านการประมวลผลใด ๆ ของ network stack เลย เหมาะสำหรับ drop แพ็กเก็ตอันตรายจำนวนมากโดยไม่กิน CPU

nft add table netdev filter_ingress
nft add chain netdev filter_ingress ingress { type filter hook ingress device eth0 priority -500 \; }

# drop แพ็กเก็ตจาก subnet ที่ขึ้นบัญชีดำ ก่อนเข้า network stack เลย (ประสิทธิภาพสูงสุด)
nft add rule netdev filter_ingress ingress ip saddr @blacklist_ips drop

12.3 การใช้งานร่วมกับ XDP (eXpress Data Path)

XDP เป็นเทคโนโลยีที่รันโปรแกรม eBPF ที่ระดับ driver ของ network card โดยตรง เร็วกว่าแม้แต่ netdev ingress hook เนื่องจากทำงานก่อนที่ kernel จะจอง sk_buff (socket buffer) ด้วยซ้ำ nftables ระดับ netdev family เป็นชั้นที่อยู่ถัดจาก XDP ในเชิงสถาปัตยกรรม — ในระบบ production ขนาดใหญ่มักใช้ XDP สำหรับ drop แบบ volumetric (เช่น DDoS layer 3/4 ขนาดใหญ่มาก) ร่วมกับ nftables netdev family สำหรับกฎที่ซับซ้อนกว่าเล็กน้อยแต่ยังต้องการความเร็วสูง


13. Flowtables และการเร่งความเร็ว (Performance)

13.1 แนวคิด flowtable สำหรับ fast-path forwarding

โดยปกติแล้ว แพ็กเก็ตทุกตัวที่ผ่าน router ต้องเดินทางผ่าน hook ครบทั้งห่วงโซ่ (prerouting → forward → postrouting) พร้อมกับการประมวลผล routing lookup, conntrack lookup และกฎทั้งหมดซ้ำทุกครั้ง Flowtable แก้ปัญหานี้ด้วยแนวคิด fast-path: เมื่อ connection หนึ่งถูกยืนยันแล้วว่าเป็น established และไม่มีการแก้ไข (เช่น NAT ที่ทำเสร็จแล้ว) แพ็กเก็ตถัดไปของ connection เดียวกันจะถูก "เพิ่ม" เข้า flowtable และประมวลผล forwarding ในเส้นทางลัด โดยข้ามการไล่ตรวจกฎทั้งหมดซ้ำ

flowchart TB
    P1["แพ็กเก็ตแรกของ connection
First Packet"] --> SLOW["Slow Path:
ผ่าน prerouting/forward/postrouting เต็มรูปแบบ
+ conntrack lookup + rule matching ทั้งหมด"] SLOW --> DECIDE{"Connection ผ่านการตรวจสอบ
และไม่มีการ NAT/mangle เพิ่มเติมแล้ว?"} DECIDE -->|"ใช่ / Yes"| ADDFLOW["เพิ่มเข้า flowtable
Add to Flowtable"] DECIDE -->|"ไม่ / No"| SLOW ADDFLOW --> P2["แพ็กเก็ตถัดไปของ connection เดียวกัน
Subsequent Packets"] P2 --> FAST["Fast Path:
Forward ทันทีผ่าน flowtable lookup
ข้าม rule chain ทั้งหมด"] FAST --> OUT["ส่งออกทันที / Immediate Output"]
# สร้าง flowtable ผูกกับ interface ขาเข้า/ขาออกของ router
nft add table inet fastpath
nft add flowtable inet fastpath ft { hook ingress priority 0 \; devices = { eth0, eth1 } \; }

# เพิ่มกฎใน forward chain เพื่อ "ส่ง" connection ที่เหมาะสมเข้า flowtable
nft add chain inet fastpath forward { type filter hook forward priority 0 \; }
nft add rule inet fastpath forward ip protocol { tcp, udp } flow add @ft accept

13.2 Software offload vs Hardware offload

ประเภท Offload การทำงาน ข้อกำหนด
Software offload flowtable ทำงานบน CPU ปกติ แต่ข้ามขั้นตอนการประมวลผลกฎที่ซ้ำซ้อน ทำงานได้กับ network card ทั่วไปทุกรุ่น
Hardware offload ส่ง flow entry ลงไปประมวลผลที่ NIC (Network Interface Card) โดยตรง ผ่าน driver ที่รองรับ ต้องใช้ NIC ระดับ enterprise ที่รองรับ ndo_setup_tc (เช่น Mellanox/NVIDIA ConnectX, Netronome)
# เปิดใช้งาน hardware offload (ต้องมี NIC และ driver ที่รองรับ)
nft add flowtable inet fastpath ft { hook ingress priority 0 \; devices = { eth0, eth1 } \; flags offload \; }

13.3 กรณีใช้งาน: router ที่ต้องการ throughput สูง

Flowtable เหมาะที่สุดสำหรับสถานการณ์ที่มี traffic ปริมาณมากแบบ bulk transfer (เช่น การดาวน์โหลดไฟล์ขนาดใหญ่, video streaming) ที่ connection คงอยู่นานและมีจำนวนแพ็กเก็ตต่อ connection สูง เพราะยิ่ง connection นานเท่าไร สัดส่วนแพ็กเก็ตที่ได้ประโยชน์จาก fast-path ก็ยิ่งมากขึ้น ในทางกลับกัน สำหรับ traffic ที่เป็น connection สั้น ๆ จำนวนมาก (เช่น DNS query) ประโยชน์จาก flowtable จะน้อยกว่าเพราะแต่ละ connection จบก่อนที่จะได้ใช้ fast-path เต็มที่


14. ความปลอดภัยและการป้องกันการโจมตี (Security Hardening)

14.1 ป้องกัน SYN flood (syn proxy)

SYN flood คือการโจมตีที่ผู้โจมตีส่ง TCP SYN จำนวนมหาศาลโดยไม่ทำ handshake ให้ครบ ทำให้ตาราง connection state ของเซิร์ฟเวอร์เต็มจนปฏิเสธ connection ใหม่ที่ถูกต้อง nftables มี statement synproxy ที่ทำหน้าที่แทนเซิร์ฟเวอร์จริงในการทำ 3-way handshake ก่อน โดยไม่จองทรัพยากร state จนกว่าจะยืนยันว่าเป็น client จริง

table inet raw {
    chain prerouting {
        type filter hook prerouting priority -300;
        tcp flags syn ct state new synproxy mss 1460 wscale 7 sack-perm timestamp
    }
}

14.2 ป้องกัน port scanning

# ตรวจจับและขึ้นบัญชีดำ IP ที่พยายามเชื่อมต่อพอร์ตต่าง ๆ จำนวนมากในเวลาสั้น (port scan)
nft add set inet filter portscan_ban { type ipv4_addr; flags dynamic,timeout; timeout 1h; }
nft add rule inet filter input ct state new \
    add @portscan_ban { ip saddr limit rate over 20/second } \
    log prefix "PORTSCAN: " drop
nft add rule inet filter input ip saddr @portscan_ban drop

14.3 Anti-spoofing (rp_filter ร่วมกับ nftables)

rp_filter (reverse path filter) ของ kernel เป็นแนวป้องกันชั้นแรก แต่ nftables สามารถเสริมด้วย fib expression (ตามที่กล่าวในบท 11.4) เพื่อควบคุมพฤติกรรมได้ละเอียดกว่า

# เปิด rp_filter แบบ strict mode ที่ระดับ kernel (ค่า 1 = strict)
sysctl -w net.ipv4.conf.all.rp_filter=1

# เสริมด้วย nftables: drop แพ็กเก็ตที่อ้าง source address เป็นวง private จาก WAN interface (anti-spoofing เพิ่มเติม)
nft add rule inet filter prerouting iifname eth0 ip saddr { 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 } drop

14.4 ป้องกัน ICMP flood / smurf attack

# จำกัดอัตรา ICMP echo-request ไม่ให้เกิน 10 ครั้งต่อวินาที ป้องกัน ping flood
nft add rule inet filter input icmp type echo-request limit rate 10/second accept
nft add rule inet filter input icmp type echo-request drop

# ป้องกัน smurf attack: บล็อก ICMP ที่ส่งไปยัง broadcast address
nft add rule inet filter input ip daddr 255.255.255.255 icmp type echo-request drop

14.5 Rate-based DDoS mitigation

การกำหนดขีดจำกัดอัตราที่เหมาะสมสำหรับ DDoS mitigation ต้องคำนวณจาก baseline traffic ปกติ ของระบบ บวกด้วยส่วนเผื่อ (margin) เพื่อไม่ให้ปฏิเสธ traffic ที่ถูกต้อง สูตรที่ใช้ประมาณค่า threshold:

Rthreshold = Rbaseline × ( 1 + α )

โดยที่:

ตัวอย่างการคำนวณ: หาก baseline วัดได้ 200 connection/second และเลือก α = 0.3 จะได้ Rthreshold = 200 × 1.3 = 260 connection/second

# ตั้ง rate limit ตามค่าที่คำนวณได้จากสูตรข้างต้น
nft add rule inet filter input tcp flags syn ct state new limit rate 260/second accept
nft add rule inet filter input tcp flags syn ct state new drop

15. การย้ายระบบจาก iptables (Migration)

15.1 เครื่องมือ iptables-translate และ iptables-restore-translate

Netfilter project จัดเตรียมเครื่องมือแปลงกฎอัตโนมัติจาก iptables syntax เดิมเป็น nft syntax เพื่อลดภาระงานย้ายระบบ

# แปลงกฎเดี่ยวจาก iptables เป็น nft (แสดงผลออกทาง stdout เท่านั้น ไม่ apply จริง)
iptables-translate -A INPUT -p tcp --dport 22 -j ACCEPT
# ผลลัพธ์: nft add rule ip filter INPUT tcp dport 22 counter accept

# แปลงทั้งไฟล์ ruleset ที่ export มาจาก iptables-save
iptables-save > old-iptables-rules.txt
iptables-restore-translate -f old-iptables-rules.txt > new-nftables-rules.nft

# ตรวจสอบ syntax ก่อน apply จริงเสมอ
nft -c -f new-nftables-rules.nft

15.2 ข้อควรระวังเรื่อง semantic ที่ต่างกัน

การแปลงอัตโนมัติไม่ได้สมบูรณ์แบบ 100% เสมอไป จุดที่ต้องตรวจสอบด้วยตนเองหลังแปลง:

15.3 การรัน nftables แบบ backward compatible (nft ruleset จาก legacy iptables)

หลาย distro ในปัจจุบันใช้ iptables-nft เป็น compatibility layer — คือยังคงรับคำสั่ง iptables แบบเดิม แต่แปลงเป็น nf_tables rule ใน kernel เบื้องหลังโดยอัตโนมัติ ทำให้ script เก่าที่เขียนด้วย iptables ยังทำงานต่อไปได้ในช่วงเปลี่ยนผ่าน

# ตรวจสอบว่า iptables บนระบบกำลังใช้ backend ใดอยู่
update-alternatives --display iptables

# สลับไปใช้ iptables-nft backend (Debian/Ubuntu)
sudo update-alternatives --set iptables /usr/sbin/iptables-nft

# ตรวจสอบว่ากฎที่สร้างผ่าน iptables ปรากฏใน nft ruleset จริงหรือไม่ (ยืนยันว่าใช้ backend nft)
iptables -A INPUT -p tcp --dport 8888 -j ACCEPT
nft list ruleset | grep 8888

16. การจัดการและ Automation

16.1 การจัดการ ruleset ด้วย systemd และ atomic reload

# ตรวจสอบ syntax ก่อนเสมอ แล้วจึง restart service เพื่อโหลด ruleset ใหม่แบบ atomic
nft -c -f /etc/nftables.conf && sudo systemctl restart nftables

# ทำ rollback อัตโนมัติหาก ruleset ใหม่ทำให้ SSH หลุดการเชื่อมต่อ (ป้องกัน lock-out ตัวเอง)
( sleep 10 && nft -f /root/nftables-backup.nft ) &
ROLLBACK_PID=$!
sudo nft -f /etc/nftables.conf
read -p "ยืนยันว่ากฎยังใช้งานได้ปกติ กด Enter เพื่อยกเลิก rollback: "
kill $ROLLBACK_PID 2>/dev/null

16.2 การใช้ Ansible/Terraform จัดการ nftables

# ตัวอย่าง Ansible playbook สำหรับ deploy nftables ruleset ไปยังหลายเครื่องพร้อมกัน
- name: Deploy nftables ruleset
  hosts: web_servers
  become: true
  tasks:
    - name: ตรวจสอบว่าติดตั้ง nftables แล้ว
      package:
        name: nftables
        state: present

    - name: คัดลอกไฟล์ ruleset ไปยังเครื่องปลายทาง
      template:
        src: templates/nftables.conf.j2
        dest: /etc/nftables.conf
        validate: 'nft -c -f %s'
      notify: restart nftables

  handlers:
    - name: restart nftables
      systemd:
        name: nftables
        state: restarted
        enabled: true

16.3 Integration กับ Docker/Podman/Kubernetes (CNI, firewall สำหรับ container)

ข้อควรระวังสำคัญ: Docker สร้างกฎ nftables/iptables ของตัวเองในระดับ DOCKER chain โดยอัตโนมัติ ซึ่งอาจoverride กฎ firewall ที่ผู้ดูแลระบบตั้งไว้เองหากไม่ได้ออกแบบให้ถูกต้อง ควรใช้ chain แยกและกำหนด priority ให้ชัดเจนเพื่อควบคุมลำดับ

# ตรวจสอบว่า Docker สร้าง table/chain ของตัวเองใน nftables หรือไม่
nft list tables | grep -i docker

# แนวทางที่แนะนำ: ใช้ DOCKER-USER chain (จุดเชื่อมที่ Docker เตรียมไว้ให้ผู้ดูแลระบบแทรกกฎเอง)
nft insert rule ip filter DOCKER-USER ip saddr 203.0.113.0/24 drop

สำหรับ Kubernetes การเลือก CNI plugin (เช่น Cilium) ที่รองรับ nftables/eBPF โดยตรงจะช่วยให้การบังคับใช้ Network Policy มีประสิทธิภาพสูงกว่าการพึ่งพา kube-proxy แบบ iptables mode แบบดั้งเดิม

16.4 การจัดการหลายเครื่องด้วย config management

ชุดข้อมูลทดลอง: โครงสร้างไฟล์แบบ modular สำหรับจัดการ ruleset หลายเครื่องด้วย config management:

/etc/nftables.conf                  # ไฟล์หลัก มีแค่ include statement
/etc/nftables.d/00-variables.nft    # ตัวแปรร่วม (WAN_IF, TRUSTED_NETS)
/etc/nftables.d/10-base.nft         # table/chain พื้นฐาน + policy
/etc/nftables.d/20-services.nft     # กฎเฉพาะ service ของเครื่องนั้น ๆ
/etc/nftables.d/90-nat.nft          # กฎ NAT (เฉพาะเครื่องที่เป็น gateway)

17. กรณีศึกษาเชิงปฏิบัติ (Use Cases)

17.1 Home router / Gateway firewall (NAT + port forward + parental control)

ชุดข้อมูลทดลอง: ruleset สมบูรณ์สำหรับ home router ที่มี WAN (eth0) และ LAN (eth1):

#!/usr/sbin/nft -f
flush ruleset

define WAN_IF = eth0
define LAN_IF = eth1
define LAN_NET = 192.168.1.0/24

table inet router {
    chain input {
        type filter hook input priority 0; policy drop;
        iif lo accept
        ct state established,related accept
        iifname $LAN_IF accept
        iifname $WAN_IF icmp type echo-request limit rate 5/second accept
    }

    chain forward {
        type filter hook forward priority 0; policy drop;
        ct state established,related accept
        ct state invalid drop
        iifname $LAN_IF oifname $WAN_IF accept
        # parental control: บล็อกอุปกรณ์เฉพาะ (เช่น MAC ของแท็บเล็ตเด็ก) ช่วง 22:00-06:00
        ether saddr aa:bb:cc:dd:ee:ff drop
    }

    chain output {
        type filter hook output priority 0; policy accept;
    }
}

table ip nat {
    chain prerouting {
        type nat hook prerouting priority -100;
        # port forward กล้องวงจรปิดภายในบ้านออกสู่อินเทอร์เน็ต
        iifname $WAN_IF tcp dport 8554 dnat to 192.168.1.50:554
    }
    chain postrouting {
        type nat hook postrouting priority 100;
        oifname $WAN_IF masquerade
    }
}

17.2 Server firewall แบบ single host (web server, database server)

table inet webserver_fw {
    chain input {
        type filter hook input priority 0; policy drop;
        iif lo accept
        ct state established,related accept
        ct state invalid drop
        tcp dport 22 ip saddr 203.0.113.0/24 accept comment "SSH เฉพาะ office IP"
        tcp dport { 80, 443 } accept comment "Web public"
        tcp dport 5432 ip saddr 10.0.2.0/24 accept comment "PostgreSQL เฉพาะ internal app tier"
    }
}

17.3 Firewall สำหรับ container/VM host

สำหรับ hypervisor/container host ควรแยก chain สำหรับ traffic ที่เป็นของ host เองออกจาก traffic ที่ผ่าน (forward) ไปยัง VM/container ภายใน โดยใช้ bridge interface name เป็นตัวจับคู่

nft add rule inet filter forward iifname "br-*" oifname "br-*" accept comment "อนุญาต traffic ระหว่าง container ในวง bridge เดียวกัน"
nft add rule inet filter forward iifname "vnet*" ip saddr @vm_whitelist accept

17.4 Multi-homed server (หลาย interface, policy routing ร่วมกับ fib)

สำหรับเซิร์ฟเวอร์ที่มีหลาย uplink (เช่น ISP 2 เจ้า) ใช้ meta mark ร่วมกับ policy routing ของ kernel (ip rule) เพื่อบังคับเส้นทางตาม mark ที่ nftables กำหนด

# แปะ mark ตาม source port เพื่อบังคับให้ traffic บางประเภทออกทาง ISP รอง
nft add rule inet filter output tcp sport 25 meta mark set 0x2
# จากนั้นตั้ง policy routing ที่ระดับ kernel ให้ตรงกับ mark (ทำนอก nftables)
# ip rule add fwmark 0x2 table isp2
# ip route add default via 198.51.100.1 table isp2

17.5 Transparent proxy setup

table ip nat {
    chain prerouting {
        type nat hook prerouting priority -100;
        # ยกเว้น traffic ที่มาจาก proxy เอง เพื่อป้องกัน redirect loop
        meta skuid proxy return
        tcp dport 80 redirect to 3128
    }
}

17.6 Load balancing แบบง่ายด้วย verdict map + DNAT

การทำ weighted load balancing แบบง่ายด้วย nftables ใช้หลักการสุ่มค่าตัวเลข (numgen) แล้ว map เข้ากับ backend server ตามสัดส่วนน้ำหนักที่ต้องการ สูตรการกำหนดสัดส่วนช่วงตัวเลขสำหรับ backend แต่ละตัว:

Si = wi ∑j=1nwj × M

โดยที่:

ตัวอย่างการคำนวณ: มี backend 2 ตัว น้ำหนัก w₁=3, w₂=1 (backend แรกแรงกว่า 3 เท่า) และ M=100 จะได้ S₁ = (3/4)×100 = 75 และ S₂ = (1/4)×100 = 25 หมายความว่า backend ตัวแรกควรรับ traffic 75% และตัวที่สองรับ 25%

nft add map ip nat lb_map { type integer : ipv4_addr; }
nft add element ip nat lb_map { 0 : 192.168.1.10, 1 : 192.168.1.10, 2 : 192.168.1.10, 3 : 192.168.1.20 }

nft add rule ip nat prerouting tcp dport 80 \
    dnat to numgen random mod 4 map @lb_map

18. การ Debug และแก้ปัญหา (Troubleshooting)

18.1 การอ่าน ruleset ปัจจุบันแบบละเอียด (nft -a list ruleset)

# แสดง ruleset พร้อม handle number ของทุกกฎ (จำเป็นสำหรับ delete/replace)
nft -a list ruleset

# แสดงเฉพาะ table เดียวแบบละเอียด
nft -a list table inet filter

# แสดงพร้อม counter แบบละเอียด (packets/bytes ของแต่ละกฎ)
nft list ruleset -a -s

18.2 การใช้ trace statement วิเคราะห์เส้นทางแพ็กเก็ต

meta nftrace set 1 เปิดการ trace แพ็กเก็ตที่ตรงกับกฎนั้นแบบละเอียดทุกขั้นตอนที่ผ่าน chain ต่าง ๆ ช่วยวิเคราะห์ได้ว่าทำไมแพ็กเก็ตถึงถูก accept/drop โดยไม่คาดคิด

# เปิด trace เฉพาะแพ็กเก็ตจาก IP ที่สงสัยปัญหา (ไม่ควรเปิด trace ทุกแพ็กเก็ตใน production เพราะกิน performance)
nft add rule inet filter input ip saddr 203.0.113.99 meta nftrace set 1

# ในอีก terminal หนึ่ง เปิดดู trace แบบ real-time
nft monitor trace

18.3 ปัญหาที่พบบ่อย (order ของ chain, priority ชนกัน, conntrack ไม่ทำงาน)

ปัญหาที่พบบ่อย สาเหตุที่เป็นไปได้ แนวทางแก้ไข
กฎ accept ทำงานแต่แพ็กเก็ตยังถูก drop มี base chain อื่นที่ priority ต่ำกว่า (ทำงานก่อน) drop แพ็กเก็ตไปแล้ว ใช้ nft -a list ruleset ตรวจดู priority ของทุก chain ที่ผูก hook เดียวกัน
ct state established ไม่ตรงกับ traffic ที่ควรจะ established conntrack module ไม่ได้โหลด หรือ raw table มีกฎ notrack กันไว้ก่อน ตรวจสอบ `lsmod
NAT ทำงานเฉพาะ connection แรก connection ถัดไปไม่ถูก NAT ลืมว่า nftables ทำ NAT เฉพาะแพ็กเก็ตแรกของ conntrack entry เท่านั้น (แพ็กเก็ตถัดไปอาศัย conntrack ที่จำ NAT ไว้แล้ว) ตรวจสอบด้วย conntrack -L ว่า entry มี NAT mapping ถูกต้องหรือไม่
กฎที่เพิ่มด้วย nft add rule หายไปหลัง reboot ลืมบันทึกกฎลง /etc/nftables.conf (คำสั่ง nft add เปลี่ยนเฉพาะ running config ใน memory) ใช้ nft list ruleset > /etc/nftables.conf หลังแก้ไขเสร็จเสมอ

19. สรุปและแหล่งอ้างอิงเพิ่มเติม (Summary & References)

19.1 สรุปแนวคิดหลัก

19.2 เอกสารทางการ (wiki.nftables.org) และ RFC ที่เกี่ยวข้อง

เอกสารและแหล่งอ้างอิงทางการ:

RFC และมาตรฐานที่เกี่ยวข้อง:

เอกสาร หัวข้อที่เกี่ยวข้อง
RFC 793 TCP protocol specification — พื้นฐานของการเข้าใจ TCP flags matching (บทที่ 11.3)
RFC 792 ICMP protocol specification — พื้นฐานของ ICMP type matching (บทที่ 14.4)
RFC 2663 IP Network Address Translator (NAT) Terminology and Considerations
RFC 6296 NPTv6: Network Prefix Translation for IPv6 (บทที่ 7.5)
RFC 4787 NAT Behavioral Requirements for Unicast UDP

แนวทางการศึกษาต่อ: ผู้อ่านที่ต้องการเชี่ยวชาญเพิ่มเติมควรศึกษาต่อในหัวข้อ eBPF/XDP (ซึ่งทำงานร่วมกับ nftables ในระดับ performance สูงสุดตามที่กล่าวในบทที่ 12.3) และหัวข้อ Kubernetes Network Policy implementation ที่ใช้ nftables/eBPF เป็น backend ในโครงการเช่น Cilium


เอกสารนี้จัดทำขึ้นเพื่อการศึกษา ตัวอย่างคำสั่งทั้งหมดควรทดสอบในสภาพแวดล้อมทดลอง (lab environment) ก่อนนำไปใช้งานจริงบนระบบ production เสมอ