
คู่มือฉบับสมบูรณ์สำหรับการใช้งาน iptables ครอบคลุมตั้งแต่สถาปัตยกรรมของ Netfilter, การกรองแพ็กเก็ต (packet filtering), การทำ NAT, ไปจนถึงเทคนิคการป้องกันการโจมตี (security hardening) และแบบฝึกหัดปฏิบัติจริง เหมาะสำหรับผู้ดูแลระบบ (system administrator), วิศวกรเครือข่าย (network engineer) และผู้ที่ต้องการทำความเข้าใจการทำงานของไฟร์วอลล์ (firewall) ในระดับ kernel เนื้อหาอ้างอิงจาก Netfilter/iptables project และ man pages เวอร์ชันล่าสุด พร้อมตัวอย่างที่พร้อมนำไปทดลองใช้จริง
iptables คือโปรแกรม user-space (พื้นที่ผู้ใช้) ที่ใช้สำหรับกำหนดค่ากฎการกรองแพ็กเก็ต (packet filtering rules) ให้กับ Netfilter ซึ่งเป็น framework ที่ฝังอยู่ใน Linux kernel ตั้งแต่เวอร์ชัน 2.4 เป็นต้นมา
กล่าวโดยสรุป iptables ไม่ได้เป็นตัวกรองแพ็กเก็ตเอง แต่เป็น "เครื่องมือควบคุม" (control tool) ที่ส่งกฎ (rules) เข้าไปเก็บไว้ในตาราง (tables) ภายใน kernel space ผ่านโมดูล x_tables โดยตัวที่ทำหน้าที่ตรวจสอบและกรองแพ็กเก็ตจริง ๆ คือ Netfilter hooks ที่ฝังอยู่ในเส้นทางการประมวลผลแพ็กเก็ตของ kernel
Netfilter hooks มีอยู่ 5 จุดหลักในเส้นทางเครือข่ายของ kernel:
NF_IP_PRE_ROUTING — ก่อนตัดสินใจเรื่องเส้นทาง (routing decision)NF_IP_LOCAL_IN — แพ็กเก็ตที่ปลายทางคือเครื่องนี้เองNF_IP_FORWARD — แพ็กเก็ตที่ต้องส่งต่อ (forward) ไปเครื่องอื่นNF_IP_LOCAL_OUT — แพ็กเก็ตที่สร้างจากโปรเซสในเครื่องนี้NF_IP_POST_ROUTING — หลังตัดสินใจเรื่องเส้นทางแล้ว ก่อนออกจากการ์ดเครือข่ายความสัมพันธ์ระหว่าง iptables และ Netfilter คือ iptables เขียนกฎ, Netfilter บังคับใช้กฎ (enforce) — ทุกครั้งที่แพ็กเก็ตผ่านจุด hook เหล่านี้ kernel จะไปตรวจสอบกับกฎที่ตรงกับ chain นั้น ๆ ว่าควรจะ ACCEPT (อนุญาต), DROP (ทิ้งแบบเงียบ), REJECT (ปฏิเสธพร้อมแจ้งกลับ) หรือทำ action อื่น ๆ
ประวัติความเป็นมา (History) ของเครื่องมือกรองแพ็กเก็ตบน Linux มีวิวัฒนาการผ่านหลายยุค ดังแผนภาพด้านล่าง:
flowchart TB
subgraph ERA1["ยุคที่ 1 (Era 1): 1994-1998"]
A1["ipfw / ipfwadm
เลียนแบบจาก BSD ipfw
รองรับ kernel 1.x - 2.0"]
end
subgraph ERA2["ยุคที่ 2 (Era 2): 1998-2001"]
A2["ipchains
Kernel 2.2
เพิ่มแนวคิด chain หลายชุด"]
end
subgraph ERA3["ยุคที่ 3 (Era 3): 2001-2014"]
A3["iptables + Netfilter
Kernel 2.4 / 2.6 / 3.x
เพิ่ม stateful inspection
(connection tracking), NAT module"]
end
subgraph ERA4["ยุคที่ 4 (Era 4): 2014-ปัจจุบัน"]
A4["nftables
Kernel 3.13+
รวม IPv4/IPv6/ARP/Bridge
เป็น framework เดียว"]
end
A1 --> A2 --> A3 --> A4
สรุปประเด็นสำคัญ:
filter, nat, mangle, raw, securityแม้ nftables จะถูกออกแบบมาเพื่อทดแทน iptables อย่างเป็นทางการตั้งแต่ปี 2014 แต่เหตุผลที่ iptables ยังคงมีความสำคัญและถูกใช้งานอย่างแพร่หลาย ได้แก่:
iptables-nft)iptables-nft เป็นค่าเริ่มต้น ทำให้คำสั่ง iptables แบบเดิมยังคงใช้งานได้จริงโดยมี nftables ทำงานอยู่เบื้องหลังกล่าวโดยสรุป: iptables ยังไม่ตาย เพียงแต่เปลี่ยนบทบาทจาก "engine หลัก" เป็น "compatibility interface" ที่วางอยู่บน nftables backend
iptables และ ip6tablesiptables แบ่งกฎออกเป็น ตาราง (tables) 5 ประเภท แต่ละตารางมีจุดประสงค์เฉพาะและถูกเรียกใช้งานในลำดับ (priority) ที่ต่างกันเมื่อแพ็กเก็ตผ่าน Netfilter hook เดียวกัน
| Table | จุดประสงค์ (Purpose) | Chains ที่รองรับ | ลำดับความสำคัญ (Priority) |
|---|---|---|---|
| raw | ยกเว้นแพ็กเก็ตจาก connection tracking (NOTRACK) |
PREROUTING, OUTPUT | สูงสุด (ทำงานก่อน conntrack) |
| mangle | แก้ไข header ของแพ็กเก็ต เช่น TOS, TTL, MARK | PREROUTING, INPUT, FORWARD, OUTPUT, POSTROUTING | รองจาก raw |
| nat | แปลงที่อยู่ (address translation) — SNAT/DNAT | PREROUTING, INPUT, OUTPUT, POSTROUTING | ทำงานเฉพาะแพ็กเก็ตแรกของ connection |
| filter | กรองแพ็กเก็ต (ACCEPT/DROP/REJECT) — ตารางเริ่มต้น (default) | INPUT, FORWARD, OUTPUT | หลัง nat/mangle |
| security | ใช้ร่วมกับ SELinux Mandatory Access Control (MAC) | INPUT, FORWARD, OUTPUT | ต่ำสุด (ทำงานหลัง filter) |
หมายเหตุสำคัญ: หากไม่ระบุ -t <table> ในคำสั่ง iptables จะถือว่าใช้ตาราง filter โดยอัตโนมัติเสมอ
Chain คือรายการกฎ (list of rules) ที่เรียงตามลำดับ โดยแต่ละ chain ผูกอยู่กับ Netfilter hook จุดใดจุดหนึ่ง:
| Chain | ทำงานเมื่อไร | ตัวอย่างการใช้งาน |
|---|---|---|
| PREROUTING | แพ็กเก็ตเข้ามาก่อนตัดสินใจ routing | DNAT (port forwarding), mangle TOS |
| INPUT | แพ็กเก็ตที่ปลายทางคือเครื่องนี้ (local) | กรอง SSH, HTTP เข้าเครื่อง |
| FORWARD | แพ็กเก็ตที่ผ่านเครื่องนี้ไปยังปลายทางอื่น | ใช้เมื่อเครื่องทำหน้าที่เป็น router/gateway |
| OUTPUT | แพ็กเก็ตที่สร้างจากโปรเซสในเครื่องนี้เอง | ควบคุม traffic ขาออกจากเซิร์ฟเวอร์ |
| POSTROUTING | แพ็กเก็ตขาออกหลังตัดสินใจ routing แล้ว | SNAT, MASQUERADE |
ข้อควรระวัง: แพ็กเก็ตที่ ไม่ได้มีปลายทางเป็นเครื่องนี้ (เช่น เครื่องที่ทำ NAT router) จะไม่มีทางผ่าน chain INPUT หรือ OUTPUT เลย แต่จะผ่าน FORWARD เท่านั้น — นี่คือสาเหตุที่คนตั้งค่า router มักลืมเปิด FORWARD แล้วสงสัยว่าทำไม traffic ไม่ผ่าน
แผนภาพด้านล่างแสดงเส้นทางการไหลของแพ็กเก็ต (packet flow) ผ่านทุก table และ chain ตามลำดับจริงที่ kernel ประมวลผล:
flowchart TD
IN["แพ็กเก็ตเข้า Network Interface
(Incoming Packet)"] --> PRE_RAW["PREROUTING
table: raw
(connection tracking exemption)"]
PRE_RAW --> CONNTRACK["Connection Tracking Engine
(สร้าง/ตรวจสอบ state)"]
CONNTRACK --> PRE_MANGLE["PREROUTING
table: mangle"]
PRE_MANGLE --> PRE_NAT["PREROUTING
table: nat (DNAT)"]
PRE_NAT --> ROUTE{"Routing Decision
(ปลายทางคือเครื่องนี้หรือไม่)"}
ROUTE -->|"ปลายทาง = เครื่องนี้ (Local)"| IN_MANGLE["INPUT
table: mangle"]
IN_MANGLE --> IN_FILTER["INPUT
table: filter"]
IN_FILTER --> IN_SEC["INPUT
table: security"]
IN_SEC --> LOCAL_PROCESS["ส่งต่อให้โปรเซสในเครื่อง
(Local Process / Application)"]
ROUTE -->|"ปลายทาง = เครื่องอื่น (Forward)"| FWD_MANGLE["FORWARD
table: mangle"]
FWD_MANGLE --> FWD_FILTER["FORWARD
table: filter"]
FWD_FILTER --> FWD_SEC["FORWARD
table: security"]
FWD_SEC --> POST_MANGLE["POSTROUTING
table: mangle"]
LOCAL_PROCESS --> OUT_RAW["OUTPUT
table: raw"]
OUT_RAW --> OUT_MANGLE["OUTPUT
table: mangle"]
OUT_MANGLE --> OUT_NAT["OUTPUT
table: nat"]
OUT_NAT --> OUT_FILTER["OUTPUT
table: filter"]
OUT_FILTER --> OUT_SEC["OUTPUT
table: security"]
OUT_SEC --> POST_MANGLE
POST_MANGLE --> POST_NAT["POSTROUTING
table: nat (SNAT/MASQUERADE)"]
POST_NAT --> OUT_IFACE["ออกทาง Network Interface
(Outgoing Packet)"]
จุดสำคัญที่ต้องจำ:
NOTRACK ใน raw table)นอกจาก built-in chains (INPUT, OUTPUT, FORWARD, PREROUTING, POSTROUTING) ผู้ใช้สามารถสร้าง custom chain ของตัวเองเพื่อจัดกลุ่มกฎให้เป็นระเบียบ และเรียกใช้ผ่าน target -j <chain_name> (jump) โดยเมื่อกฎใน custom chain จบลง (หรือใช้ RETURN) การประมวลผลจะกลับไปที่ chain เดิมที่เรียกมา
flowchart LR
INPUT_CHAIN["INPUT chain
(built-in)"] -->|"-j LOG_AND_DROP
(jump)"| CUSTOM["LOG_AND_DROP
(custom chain)"]
CUSTOM --> RULE1["Rule 1: LOG
บันทึก log"]
RULE1 --> RULE2["Rule 2: DROP
ทิ้งแพ็กเก็ต"]
RULE2 -.->|"RETURN
(ถ้าไม่ตรงกฎใด)"| INPUT_CHAIN
ตัวอย่างการสร้างและใช้งาน custom chain:
# สร้าง custom chain ชื่อ LOG_AND_DROP สำหรับ log แล้วทิ้งแพ็กเก็ตที่น่าสงสัย
iptables -N LOG_AND_DROP
# เพิ่มกฎภายใน custom chain: log ก่อน แล้วค่อย drop
iptables -A LOG_AND_DROP -j LOG --log-prefix "SUSPICIOUS: " --log-level 4
iptables -A LOG_AND_DROP -j DROP
# เรียกใช้ custom chain จาก INPUT เมื่อพบแพ็กเก็ตจาก IP ต้องสงสัย
iptables -A INPUT -s 203.0.113.99 -j LOG_AND_DROP
# ตัวอย่างการใช้งาน: ทดสอบด้วยการ ping จาก IP ดังกล่าว แล้วตรวจสอบ log
# dmesg | grep SUSPICIOUS
การจัดกลุ่มกฎด้วย custom chain ช่วยให้จัดการกฎจำนวนมากได้เป็นระบบ (modular) และลดการไล่กฎซ้ำซ้อน (linear scan) เพราะสามารถ jump ตรงไปยังกลุ่มกฎที่เกี่ยวข้องเท่านั้น
iptables [-t table] COMMAND [chain] [rule-spec] [options]โครงสร้างคำสั่ง iptables ประกอบด้วย 4 ส่วนหลัก:
iptables [-t table] COMMAND [chain] [rule-specification] [options]
# │ │ │ │ │
# │ │ │ │ └─ ตัวเลือกเพิ่มเติม เช่น -j, --log-prefix
# │ │ │ └─ เงื่อนไขการจับคู่ เช่น -s, -d, -p, --dport
# │ │ └─ ชื่อ chain เช่น INPUT, OUTPUT, custom chain
# │ └─ คำสั่งหลัก เช่น -A (append), -I (insert), -D (delete)
# └─ ตารางที่ต้องการแก้ไข (ค่าเริ่มต้นคือ filter หากไม่ระบุ)
ตัวอย่างพื้นฐาน:
# อนุญาต SSH (port 22) เข้าเครื่องนี้ผ่าน table filter, chain INPUT
iptables -t filter -A INPUT -p tcp --dport 22 -j ACCEPT
# เนื่องจาก filter เป็นค่า default จึงเขียนแบบย่อได้เหมือนกัน
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
| Option | ความหมาย | ตัวอย่าง |
|---|---|---|
-p, --protocol |
ระบุ protocol (tcp, udp, icmp, all) | -p tcp |
-s, --source |
IP หรือ subnet ต้นทาง | -s 192.168.1.0/24 |
-d, --destination |
IP หรือ subnet ปลายทาง | -d 10.0.0.5 |
-i, --in-interface |
interface ขาเข้า | -i eth0 |
-o, --out-interface |
interface ขาออก | -o eth1 |
--sport |
source port (ต้องใช้ร่วมกับ -p tcp/udp) |
--sport 1024:65535 |
--dport |
destination port | --dport 80 |
! |
negation (ปฏิเสธเงื่อนไข) | ! -s 192.168.1.1 |
| Target | พฤติกรรม | Table ที่ใช้ได้ |
|---|---|---|
| ACCEPT | อนุญาตแพ็กเก็ตผ่าน หยุดการประมวลผล chain นี้ | ทุกตาราง |
| DROP | ทิ้งแพ็กเก็ตแบบเงียบ (ไม่แจ้งกลับผู้ส่ง) | ทุกตาราง |
| REJECT | ปฏิเสธและส่ง ICMP/TCP RST กลับไปแจ้งผู้ส่ง | filter |
| LOG | บันทึกข้อมูลแพ็กเก็ตลง kernel log แล้วให้กฎถัดไปทำงานต่อ (ไม่ terminate) | ทุกตาราง |
| RETURN | กลับไปยัง chain ที่เรียกมา (คล้าย return ในโปรแกรม) |
ทุกตาราง |
| SNAT | แปลง source IP (ใช้กับ IP คงที่) | nat (POSTROUTING) |
| DNAT | แปลง destination IP (ใช้ทำ port forwarding) | nat (PREROUTING) |
| MASQUERADE | เหมือน SNAT แต่ใช้กับ IP แบบ dynamic | nat (POSTROUTING) |
| REDIRECT | เปลี่ยนปลายทางไปยัง port อื่นบนเครื่องเดียวกัน (transparent proxy) | nat (PREROUTING/OUTPUT) |
ข้อแตกต่างสำคัญระหว่าง DROP กับ REJECT: DROP ทำให้ผู้โจมตี (attacker) ไม่รู้ว่าพอร์ตถูกปิดหรือไม่มีเครื่องอยู่จริง (เหมาะกับการป้องกัน port scanning) ในขณะที่ REJECT จะแจ้งกลับทันทีว่าการเชื่อมต่อถูกปฏิเสธ (เหมาะกับ internal network ที่ต้องการ responsiveness ที่ดีสำหรับผู้ใช้ทั่วไป)
| คำสั่ง | ความหมาย | ตัวอย่าง |
|---|---|---|
-A (Append) |
เพิ่มกฎต่อท้าย chain | iptables -A INPUT -p tcp --dport 443 -j ACCEPT |
-I (Insert) |
แทรกกฎที่ตำแหน่งที่ระบุ (ค่าเริ่มต้น = ตำแหน่งแรกสุด) | iptables -I INPUT 1 -p tcp --dport 22 -j ACCEPT |
-D (Delete) |
ลบกฎ (ระบุด้วย rule-spec หรือหมายเลขบรรทัด) | iptables -D INPUT 3 หรือ iptables -D INPUT -p tcp --dport 22 -j ACCEPT |
-R (Replace) |
แทนที่กฎที่ตำแหน่งที่ระบุด้วยกฎใหม่ | iptables -R INPUT 2 -p tcp --dport 8080 -j ACCEPT |
ตัวอย่างการใช้งานจริง:
# เพิ่มกฎอนุญาต HTTPS ต่อท้าย INPUT chain
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
# แทรกกฎอนุญาต SSH ไว้บรรทัดแรกสุด (สำคัญมาก! ต้องอยู่ก่อนกฎ DROP ใด ๆ)
iptables -I INPUT 1 -p tcp --dport 22 -j ACCEPT
# ลบกฎที่บรรทัดที่ 3 ใน INPUT chain
iptables -D INPUT 3
# แทนที่กฎที่บรรทัด 2 ด้วยกฎใหม่ (เปลี่ยน port 8080 เป็น 8443)
iptables -R INPUT 2 -p tcp --dport 8443 -j ACCEPT
คำเตือนสำคัญ: ลำดับของกฎมีผลโดยตรงต่อผลลัพธ์ เพราะ iptables ประมวลผลกฎแบบ first-match-wins (กฎแรกที่ตรงเงื่อนไขจะถูกใช้ทันที ไม่ไล่ต่อ) การใช้ -I โดยไม่ระบุตำแหน่งจะแทรกไว้บนสุดเสมอ ซึ่งอาจ override กฎเดิมโดยไม่ตั้งใจ
| คำสั่ง | ความหมาย | ตัวอย่าง |
|---|---|---|
-L (List) |
แสดงรายการกฎทั้งหมด | iptables -L -n -v --line-numbers |
-S (List rules, iptables-save format) |
แสดงกฎในรูปแบบคำสั่งที่ใช้สร้างกฎ | iptables -S INPUT |
-F (Flush) |
ล้างกฎทั้งหมดใน chain (หรือทุก chain ถ้าไม่ระบุ) | iptables -F INPUT |
-Z (Zero) |
รีเซ็ตตัวนับ packet/byte counter เป็นศูนย์ | iptables -Z INPUT |
# แสดงกฎทั้งหมดพร้อมหมายเลขบรรทัดและไม่ resolve DNS (เร็วกว่า)
iptables -L -n -v --line-numbers
# แสดงกฎในรูปแบบที่ copy ไปรันซ้ำได้ทันที
iptables -S
# ล้างกฎทั้งหมดใน chain FORWARD เท่านั้น
iptables -F FORWARD
# รีเซ็ตตัวนับ packet/byte เพื่อเริ่มวัดสถิติใหม่
iptables -Z
ข้อควรระวัง: คำสั่ง iptables -F (flush ทั้งหมดโดยไม่ระบุ chain) จะล้างกฎทุกอย่างทันที หาก default policy เป็น DROP อยู่ก่อนหน้า และไม่มีการตั้งกฎ ACCEPT ใหม่ทัน ระบบอาจตัดการเชื่อมต่อ SSH ของตัวเองได้ (ดูรายละเอียดในหัวข้อ 12.3)
# สร้าง custom chain ใหม่ชื่อ WHITELIST
iptables -N WHITELIST
# ลบ custom chain (ต้องไม่มีกฎอื่นอ้างอิงถึง chain นี้อยู่ และ chain ต้องว่างเปล่าก่อน)
iptables -F WHITELIST # ล้างกฎภายในก่อน
iptables -X WHITELIST # จึงลบ chain ได้
Default policy คือพฤติกรรมที่ใช้เมื่อไม่มีกฎใดใน chain ตรงกับแพ็กเก็ตเลย ใช้ได้เฉพาะกับ built-in chain เท่านั้น (custom chain ไม่มี default policy ใช้ RETURN แทน)
# ตั้งนโยบายเริ่มต้น: ปฏิเสธทุกอย่างที่ไม่ได้อนุญาตไว้อย่างชัดเจน (whitelist model - แนะนำ)
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
แนวปฏิบัติที่ดี (Best Practice): ควรตั้งกฎ ACCEPT ที่จำเป็น (เช่น SSH, established connections) ก่อน เปลี่ยน default policy เป็น DROP เสมอ เพื่อป้องกันการล็อกตัวเองออกจากระบบ
-s, -d)# อนุญาตเฉพาะ IP เดียว
iptables -A INPUT -s 192.168.1.100 -j ACCEPT
# อนุญาตทั้ง subnet ด้วย CIDR notation
iptables -A INPUT -s 192.168.1.0/24 -j ACCEPT
# ปฏิเสธ IP เดียว (negation)
iptables -A INPUT ! -s 192.168.1.0/24 -j DROP
เมื่อระบุ subnet ด้วย CIDR (เช่น /24) จำนวน host ที่ใช้งานได้จริง ในเครือข่ายนั้นคำนวณได้จากสูตรต่อไปนี้ (MathML):
โดยที่:
/24)ตัวอย่างการคำนวณ: หากใช้ 192.168.1.0/24 (p = 24)
ดังนั้น 192.168.1.0/24 มี host ที่ใช้งานได้จริง 254 เครื่อง (ตั้งแต่ 192.168.1.1 ถึง 192.168.1.254) — ตัวเลขนี้สำคัญเวลาเขียนกฎ -s/-d เพื่อประเมินว่ากฎนั้นครอบคลุมเครื่องกี่เครื่อง
--dport, --sport)# อนุญาต TCP port 80 (HTTP) และ 443 (HTTPS)
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
# อนุญาต UDP port 53 (DNS)
iptables -A INPUT -p udp --dport 53 -j ACCEPT
# อนุญาต ICMP echo-request (ping)
iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT
# ระบุช่วง port (port range)
iptables -A INPUT -p tcp --dport 6000:6010 -j ACCEPT
-i, -o)# อนุญาตทุก traffic จาก loopback interface (สำคัญมาก - หลายบริการพึ่งพา localhost)
iptables -A INPUT -i lo -j ACCEPT
# อนุญาตเฉพาะ SSH ที่มาจาก interface ภายใน (eth1) ไม่ใช่จาก internet (eth0)
iptables -A INPUT -i eth1 -p tcp --dport 22 -j ACCEPT
iptables -A INPUT -i eth0 -p tcp --dport 22 -j DROP
-m conntrack)Connection tracking (conntrack) คือกลไกที่ Netfilter ใช้ติดตามสถานะ (state) ของแต่ละการเชื่อมต่อ ทำให้ iptables เป็น stateful firewall (จดจำบริบทของ connection ไม่ใช่ดูแค่แพ็กเก็ตทีละตัวแบบ stateless)
| State | ความหมาย | ตัวอย่างการใช้งาน |
|---|---|---|
| NEW | แพ็กเก็ตแรกที่เริ่มต้น connection ใหม่ | อนุญาตเฉพาะ connection ใหม่ที่ต้องการ |
| ESTABLISHED | แพ็กเก็ตที่อยู่ใน connection ที่มีการตอบโต้แล้ว (two-way traffic) | อนุญาตแพ็กเก็ตตอบกลับของ connection ที่เปิดไว้แล้ว |
| RELATED | connection ใหม่ที่สัมพันธ์กับ connection เดิม (เช่น FTP data channel, ICMP error) | จำเป็นสำหรับ protocol ที่เปิดหลาย connection เช่น FTP |
| INVALID | แพ็กเก็ตที่ conntrack ไม่สามารถระบุ state ได้ (มักเป็นสัญญาณของการโจมตี) | ควร DROP ทิ้งเสมอ |
# กฎพื้นฐานที่แนะนำสำหรับทุกระบบ (ควรอยู่ต้น ๆ ของ INPUT chain)
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -m conntrack --ctstate INVALID -j DROP
# อนุญาตเฉพาะ connection ใหม่สำหรับ SSH เท่านั้น (ไม่ต้องอนุญาต ESTABLISHED ซ้ำเพราะครอบคลุมแล้วด้านบน)
iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -j ACCEPT
หลักการสำคัญ: การอนุญาต ESTABLISHED,RELATED ไว้เป็นกฎแรก ๆ ทำให้ไม่ต้องเขียนกฎขาออก/ขาตอบกลับซ้ำซ้อนสำหรับทุก service เพราะ conntrack จะจดจำเองว่าแพ็กเก็ตขาเข้านั้นเป็นการตอบกลับของ connection ที่เราเป็นผู้เริ่มหรือไม่
| Module | หน้าที่ | ตัวอย่าง |
|---|---|---|
-m multiport |
ระบุหลาย port ที่ไม่ต่อเนื่องกันในกฎเดียว | -m multiport --dports 80,443,8080 |
-m iprange |
ระบุช่วง IP ที่ไม่ตรงกับ CIDR boundary | -m iprange --src-range 192.168.1.10-192.168.1.50 |
-m mac |
จับคู่ตาม MAC address | -m mac --mac-source 00:11:22:33:44:55 |
-m string |
ค้นหา pattern ใน payload ของแพ็กเก็ต | -m string --string "malicious" --algo bm |
-m time |
จำกัดกฎให้ทำงานเฉพาะช่วงเวลา | -m time --timestart 09:00 --timestop 18:00 |
-m limit |
จำกัดอัตราแพ็กเก็ตแบบ token bucket อย่างง่าย | -m limit --limit 10/second |
-m hashlimit |
เหมือน limit แต่แยกตาม IP/port ได้ (per-source rate limiting) | -m hashlimit --hashlimit-mode srcip |
-m recent |
จดจำ IP ที่เพิ่งเชื่อมต่อมาเพื่อตรวจจับ brute force | -m recent --update --seconds 60 --hitcount 5 |
# ตัวอย่าง multiport: อนุญาตหลาย port ในกฎเดียว
iptables -A INPUT -p tcp -m multiport --dports 80,443,8080,8443 -j ACCEPT
# ตัวอย่าง iprange: อนุญาตเฉพาะช่วง IP ที่ไม่ตรงกับ CIDR
iptables -A INPUT -m iprange --src-range 192.168.1.10-192.168.1.50 -j ACCEPT
รายละเอียดการใช้งาน limit, hashlimit, และ recent แบบเจาะลึกพร้อมสูตรคำนวณจะอธิบายในหัวข้อที่ 7 (Security Hardening)
SNAT ใช้แปลง source IP address ของแพ็กเก็ตขาออก เหมาะสำหรับกรณีที่เครื่องมี public IP แบบคงที่ (static IP)
# แปลง source IP ของทุกแพ็กเก็ตที่ออกทาง eth0 ให้เป็น 203.0.113.5 (static public IP)
iptables -t nat -A POSTROUTING -o eth0 -j SNAT --to-source 203.0.113.5
# SNAT พร้อมระบุช่วง port ที่ใช้ (port range) เพื่อควบคุมจำนวน connection พร้อมกัน
iptables -t nat -A POSTROUTING -o eth0 -j SNAT --to-source 203.0.113.5:10000-60000
เมื่อเครื่องหลายเครื่องใน network ภายในแชร์ public IP เดียวกันผ่าน SNAT/MASQUERADE แต่ละ connection ที่ออกไปจะถูกจับคู่กับ ephemeral port หนึ่งพอร์ต ทำให้จำนวน connection สูงสุดที่รองรับพร้อมกันถูกจำกัดด้วยจำนวนพอร์ตที่ใช้ได้ คำนวณได้ดังนี้:
โดยที่:
ตัวอย่างการคำนวณ: หากกำหนด --to-source 203.0.113.5:10000-60000
ดังนั้นสามารถรองรับได้สูงสุด 50,000 connection พร้อมกัน ผ่าน public IP เดียว หากเกินกว่านี้ connection ใหม่จะถูกปฏิเสธเนื่องจากไม่มีพอร์ตว่างให้จับคู่ (port exhaustion) — ปัญหานี้พบบ่อยใน NAT gateway ที่มีผู้ใช้จำนวนมากอยู่เบื้องหลัง
MASQUERADE ทำหน้าที่เหมือน SNAT แต่ไม่ต้องระบุ IP ตายตัว — ระบบจะดึง IP ปัจจุบันของ interface นั้นมาใช้อัตโนมัติทุกครั้ง เหมาะกับกรณีที่ IP เปลี่ยนแปลงได้ (เช่น เชื่อมต่อ internet ผ่าน DHCP หรือ PPPoE)
# แชร์อินเทอร์เน็ตจาก eth0 (WAN) ให้กับเครื่องใน network ภายใน (LAN)
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
# MASQUERADE เฉพาะ traffic จาก subnet ภายในที่กำหนด
iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -o eth0 -j MASQUERADE
ข้อแตกต่างจาก SNAT: MASQUERADE มี overhead สูงกว่าเล็กน้อยเพราะต้องตรวจสอบ IP ของ interface ทุกครั้งที่มีแพ็กเก็ตผ่าน (เผื่อกรณี IP เปลี่ยน) ในขณะที่ SNAT ระบุ IP ตายตัวไว้ล่วงหน้า ทำให้ประสิทธิภาพดีกว่าเล็กน้อยเมื่อ IP ไม่เปลี่ยนแปลง
DNAT ใช้แปลง destination IP/port ของแพ็กเก็ตขาเข้า เพื่อส่งต่อไปยังเครื่องภายใน (internal server) — เรียกกันทั่วไปว่า port forwarding
# forward port 80 (HTTP) ที่มาจากภายนอกไปยังเว็บเซิร์ฟเวอร์ภายใน 192.168.1.10:80
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j DNAT --to-destination 192.168.1.10:80
# forward พร้อมเปลี่ยน port ปลายทาง (external 8080 -> internal 3000)
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 8080 -j DNAT --to-destination 192.168.1.10:3000
# สำคัญ: ต้องเปิด FORWARD chain ให้ traffic ที่ถูก DNAT ผ่านได้ด้วย ไม่เช่นนั้นจะถูกบล็อกที่ filter table
iptables -A FORWARD -p tcp -d 192.168.1.10 --dport 80 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
ข้อควรจำ: DNAT เกิดขึ้นที่ table nat, chain PREROUTING ซึ่งทำงานก่อนการตัดสินใจ routing ดังนั้นแพ็กเก็ตที่ถูก DNAT แล้วจะถูกส่งต่อผ่าน chain FORWARD (ไม่ใช่ INPUT) เพราะปลายทางจริงไม่ใช่เครื่องนี้ — ต้องอนุญาตใน FORWARD chain ด้วยเสมอ มิฉะนั้น port forwarding จะไม่ทำงาน
แผนภาพด้านล่างแสดงโครงสร้างเครื่อง Linux ที่ทำหน้าที่เป็น NAT router เชื่อมระหว่าง LAN และ WAN:
flowchart LR
LAN1["เครื่องภายใน 1
(Internal Host 1)
192.168.1.10"] --> ETH1["eth1 (LAN)
192.168.1.1"]
LAN2["เครื่องภายใน 2
(Internal Host 2)
192.168.1.11"] --> ETH1
ETH1 --> ROUTER["Linux Router
(ip_forward = 1)
iptables NAT/MASQUERADE"]
ROUTER --> ETH0["eth0 (WAN)
Public IP: 203.0.113.5"]
ETH0 --> INTERNET["อินเทอร์เน็ต
(Internet)"]
ขั้นตอนการตั้งค่า:
# 1. เปิดใช้งาน IP forwarding ใน kernel (ค่าเริ่มต้นคือ 0 = ปิด)
echo 1 > /proc/sys/net/ipv4/ip_forward
# เพื่อให้ค่านี้คงอยู่ถาวรหลัง reboot ให้แก้ไขไฟล์ sysctl
echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf
sysctl -p
# 2. ตั้งค่า MASQUERADE สำหรับ traffic ที่ออกทาง WAN (eth0)
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
# 3. อนุญาตให้ traffic จาก LAN ผ่านไปยัง WAN
iptables -A FORWARD -i eth1 -o eth0 -j ACCEPT
# 4. อนุญาตเฉพาะ traffic ที่เป็นการตอบกลับ (established) จาก WAN กลับเข้า LAN
iptables -A FORWARD -i eth0 -o eth1 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# ตัวอย่างการทดสอบ: จากเครื่องภายใน (192.168.1.10) ลอง ping ออกอินเทอร์เน็ต
# ping -c 4 8.8.8.8
หากลืมขั้นตอนที่ 1 (ip_forward = 0) กฎ iptables ทั้งหมดจะไม่มีผลใด ๆ เพราะ kernel จะทิ้งแพ็กเก็ตที่ต้อง forward ไปก่อนที่จะถึงขั้นตอนตรวจสอบกฎด้วยซ้ำ
SYN Flood คือการโจมตีแบบ Denial of Service (DoS) ที่ส่ง TCP SYN packet จำนวนมากโดยไม่ทำ handshake ให้เสร็จ ทำให้ตาราง connection ของเป้าหมายเต็มและปฏิเสธ connection ใหม่ที่ถูกต้อง
# จำกัดอัตรา SYN packet ใหม่ที่เข้ามา (ป้องกัน SYN flood แบบพื้นฐาน)
iptables -A INPUT -p tcp --syn -m limit --limit 5/second --limit-burst 10 -j ACCEPT
iptables -A INPUT -p tcp --syn -j DROP
# ป้องกัน port scanning แบบ NULL scan, FIN scan, Xmas scan
iptables -A INPUT -p tcp --tcp-flags ALL NONE -j DROP
iptables -A INPUT -p tcp --tcp-flags ALL FIN,PSH,URG -j DROP
iptables -A INPUT -p tcp --tcp-flags SYN,FIN SYN,FIN -j DROP
iptables -A INPUT -p tcp --tcp-flags SYN,RST SYN,RST -j DROP
-m limit / -m hashlimitโมดูล limit และ hashlimit ทำงานบนหลักการ Token Bucket Algorithm ซึ่งเป็นแนวคิดพื้นฐานของการจำกัดอัตรา (rate limiting) ในระบบเครือข่าย
หลักการ Token Bucket: ระบบมี "ถัง" (bucket) ที่บรรจุ token ได้สูงสุดจำนวนหนึ่ง (burst) โดย token จะถูกเติมเข้าถังด้วยอัตราคงที่ (rate) ทุกครั้งที่มีแพ็กเก็ตเข้ามาต้องใช้ 1 token หากถังว่าง (ไม่มี token เหลือ) แพ็กเก็ตนั้นจะถูกปฏิเสธ
จำนวน token ที่มีอยู่ในถัง ณ เวลาใด ๆ คำนวณได้จาก:
โดยที่:
--limit-burst--limitตัวอย่างการคำนวณ: กำหนด --limit 5/second --limit-burst 10 เริ่มต้นถังเต็ม (T(t₀) = 10) แล้วไม่มีแพ็กเก็ตเข้ามาเลยเป็นเวลา 3 วินาที
ผลลัพธ์คือถังยังคงเต็มที่ 10 token (เพราะ min จำกัดไว้ที่ขนาดถังสูงสุด แม้สูตรจะคำนวณได้ 25 ก็ตาม) ซึ่งหมายความว่าเมื่อมีทราฟฟิกพุ่งเข้ามาทันที (burst) ระบบจะยอมให้ผ่านได้สูงสุด 10 แพ็กเก็ตในทันที จากนั้นจึงถูกจำกัดที่อัตรา 5 แพ็กเก็ตต่อวินาที
# ตัวอย่าง -m limit: จำกัดอัตรา ICMP echo-request โดยรวมทั้งระบบ (ไม่แยกตาม IP)
iptables -A INPUT -p icmp --icmp-type echo-request -m limit --limit 3/second --limit-burst 5 -j ACCEPT
iptables -A INPUT -p icmp --icmp-type echo-request -j DROP
# ตัวอย่าง -m hashlimit: จำกัดอัตราแยกตาม source IP แต่ละตัว (ดีกว่า limit เพราะไม่กระทบผู้ใช้อื่น)
iptables -A INPUT -p tcp --dport 80 -m hashlimit \
--hashlimit-name http_limit \
--hashlimit-mode srcip \
--hashlimit-upto 20/second \
--hashlimit-burst 40 \
-j ACCEPT
iptables -A INPUT -p tcp --dport 80 -j DROP
ข้อแตกต่างสำคัญ: -m limit จำกัดอัตรารวมของทั้ง chain (ทุก source IP ใช้ token bucket เดียวกัน) ในขณะที่ -m hashlimit สร้าง token bucket แยกตามแต่ละ key (เช่น per source IP ด้วย --hashlimit-mode srcip) ทำให้ผู้ใช้ปกติคนหนึ่งไม่ถูกจำกัดจากการที่ผู้ใช้อื่นส่ง traffic มาก
-m recentโมดูล recent ใช้จดจำรายการ IP ที่เพิ่งเชื่อมต่อเข้ามาในหน่วยความจำของ kernel ทำให้สามารถตรวจจับพฤติกรรมที่ผิดปกติ เช่น การพยายาม login ซ้ำ ๆ ในเวลาสั้น ๆ (brute force attack)
# สร้างรายการชื่อ "ssh_attack" และปฏิเสธ IP ที่พยายามเชื่อมต่อ SSH เกิน 4 ครั้งภายใน 60 วินาที
iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW \
-m recent --name ssh_attack --set
iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW \
-m recent --name ssh_attack --update --seconds 60 --hitcount 4 \
-j DROP
# ตัวอย่างการทดสอบ: จำลองการโจมตี brute force ด้วยการเชื่อมต่อ SSH ซ้ำ ๆ อย่างรวดเร็ว
# for i in {1..6}; do ssh -o ConnectTimeout=1 user@target_ip; done
# หลังครั้งที่ 4 ภายใน 60 วินาที IP นั้นจะถูก DROP โดยอัตโนมัติ
หลักการทำงาน: บรรทัดแรกบันทึกเวลาที่ IP นั้นเชื่อมต่อเข้ามาไว้ในรายการ ssh_attack ทุกครั้ง ส่วนบรรทัดที่สองตรวจสอบว่า IP นั้นปรากฏในรายการเกิน --hitcount ครั้งภายในช่วงเวลา --seconds ที่กำหนดหรือไม่ หากเกินจะ DROP ทันที
เมื่อต้องจัดการ IP จำนวนมาก (หลักพันถึงหลักล้านรายการ) การเขียนกฎ iptables แยกทีละ IP จะทำให้ประสิทธิภาพแย่ลงอย่างมาก (เพราะ iptables ค้นหาแบบ linear scan) จึงควรใช้ ipset ซึ่งเก็บข้อมูลด้วยโครงสร้าง hash table ทำให้ค้นหาได้เร็วในระดับ O(1)
# สร้าง ipset ชื่อ blacklist สำหรับเก็บ IP ที่ต้องการบล็อก
ipset create blacklist hash:ip
# เพิ่ม IP เข้า blacklist
ipset add blacklist 203.0.113.99
ipset add blacklist 198.51.100.50
# สร้างกฎ iptables ให้ตรวจสอบกับ ipset (กฎเดียวครอบคลุมทุก IP ใน set)
iptables -A INPUT -m set --match-set blacklist src -j DROP
# ตัวอย่างการทำ whitelist สำหรับ SSH เฉพาะ IP ที่เชื่อถือได้
ipset create ssh_whitelist hash:ip
ipset add ssh_whitelist 192.168.1.100
ipset add ssh_whitelist 203.0.113.10
iptables -A INPUT -p tcp --dport 22 -m set --match-set ssh_whitelist src -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -j DROP
Geo-blocking (บล็อกตามประเทศ) มักทำโดยดาวน์โหลดรายการ IP range ของแต่ละประเทศ (เช่นจาก ipdeny.com) แล้วนำเข้า ipset:
# ตัวอย่าง: สร้าง ipset สำหรับบล็อก IP range ของประเทศหนึ่ง (สมมติว่าดาวน์โหลดไฟล์ cn.zone มาแล้ว)
ipset create geo_block hash:net
while read -r range; do ipset add geo_block "$range"; done < cn.zone
iptables -A INPUT -m set --match-set geo_block src -j DROP
IP Spoofing คือการปลอมแปลง source IP address ของแพ็กเก็ตเพื่อหลอกระบบปลายทางหรือใช้ในการโจมตีแบบ reflection/amplification
# เปิดใช้งาน Reverse Path Filtering (rp_filter) ทุก interface - ตรวจสอบว่าเส้นทางกลับสมเหตุสมผลหรือไม่
for i in /proc/sys/net/ipv4/conf/*/rp_filter; do
echo 1 > "$i"
done
# หรือตั้งค่าถาวรผ่าน sysctl
echo "net.ipv4.conf.all.rp_filter = 1" >> /etc/sysctl.conf
echo "net.ipv4.conf.default.rp_filter = 1" >> /etc/sysctl.conf
sysctl -p
# ปฏิเสธแพ็กเก็ตที่อ้างว่ามาจาก private IP range แต่เข้ามาทาง interface ภายนอก (WAN)
iptables -A INPUT -i eth0 -s 10.0.0.0/8 -j DROP
iptables -A INPUT -i eth0 -s 172.16.0.0/12 -j DROP
iptables -A INPUT -i eth0 -s 192.168.0.0/16 -j DROP
iptables -A INPUT -i eth0 -s 127.0.0.0/8 -j DROP
# DROP แพ็กเก็ตที่ conntrack ระบุว่า INVALID เสมอ (มักเป็นสัญญาณของการโจมตีหรือแพ็กเก็ตผิดรูปแบบ)
iptables -A INPUT -m conntrack --ctstate INVALID -j DROP
dmesg, journalctl)Target LOG บันทึกข้อมูลของแพ็กเก็ตที่ตรงกับกฎลงใน kernel ring buffer โดยไม่หยุดการประมวลผล (ไม่ terminate chain) ทำให้ต้องมีกฎถัดไปเพื่อตัดสินใจ ACCEPT/DROP ต่อ
# บันทึก log ของแพ็กเก็ตที่พยายามเชื่อมต่อ SSH ก่อนที่จะถูก DROP
iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW \
-j LOG --log-prefix "SSH_ATTEMPT: " --log-level info
iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -j DROP
# อ่าน log จาก kernel ring buffer
dmesg | grep "SSH_ATTEMPT"
# อ่าน log ผ่าน systemd journal (ระบบที่ใช้ systemd-journald)
journalctl -k | grep "SSH_ATTEMPT"
# ติดตาม log แบบ real-time
journalctl -kf | grep --line-buffered "SSH_ATTEMPT"
หมายเหตุ: บาง distro (เช่น Ubuntu/Debian ที่ใช้ rsyslog) จะส่ง log ของ kernel ไปที่ /var/log/kern.log แทน ให้ตรวจสอบด้วย tail -f /var/log/kern.log หากไม่พบใน dmesg
Target NFLOG เป็นทางเลือกที่ทันสมัยกว่า LOG โดยส่งข้อมูลแพ็กเก็ตผ่าน netlink socket ไปยังโปรแกรม user-space (เช่น ulogd) แทนที่จะเขียนลง kernel log โดยตรง ทำให้สามารถประมวลผล, กรอง, และจัดเก็บ log ในรูปแบบที่ซับซ้อนกว่าได้ (เช่น เก็บลงฐานข้อมูล, ส่งไปยัง SIEM)
# ส่งแพ็กเก็ตที่ตรงกฎไปยัง NFLOG group หมายเลข 1 (ต้องมี ulogd รันอยู่เพื่อรับข้อมูล)
iptables -A INPUT -p tcp --dport 22 -j NFLOG --nflog-group 1 --nflog-prefix "SSH: "
# ติดตั้งและตั้งค่า ulogd เพื่อรับ log จาก NFLOG (ตัวอย่างสำหรับ Debian/Ubuntu)
# apt install ulogd2 ulogd2-json
# ulogd จะเขียน log ในรูปแบบ JSON ไปยัง /var/log/ulog/ulogd.json ตามค่าเริ่มต้น
ข้อดีของ NFLOG เหนือ LOG: รองรับ multicast (ส่งไปหลาย consumer พร้อมกัน), ไม่ผูกติดกับ syslog facility ที่จำกัด, และสามารถส่ง metadata ของแพ็กเก็ตแบบละเอียด (raw packet data) ไปประมวลผลต่อได้
การเปิด LOG โดยไม่จำกัดอัตราอาจทำให้ดิสก์เต็มอย่างรวดเร็วในกรณีถูกโจมตีแบบ flood จึงควรใช้ -m limit ควบคู่กับ LOG เสมอ
# จำกัด log ไม่เกิน 5 ครั้งต่อนาที (ป้องกัน log flood ระหว่างถูกโจมตี)
iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW \
-m limit --limit 5/minute --limit-burst 5 \
-j LOG --log-prefix "SSH_ATTEMPT: "
iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -j DROP
แนวปฏิบัติที่ดี: ควรตั้งค่า logrotate สำหรับไฟล์ log ที่เกี่ยวข้อง (เช่น /var/log/kern.log) และพิจารณาใช้ --log-level ที่เหมาะสม (เช่น warning แทน info) เพื่อไม่ให้ log ปะปนกับข้อความ kernel อื่น ๆ มากเกินไป
ip6tables เป็นโปรแกรมแยกต่างหากจาก iptables ที่ใช้ syntax เกือบเหมือนกันทุกประการ แต่ทำงานกับ Netfilter hooks ฝั่ง IPv6 โดยเฉพาะ กฎของทั้งสองไม่ได้ใช้ร่วมกัน ต้องตั้งค่าแยกกันอย่างสมบูรณ์
| หัวข้อ | iptables (IPv4) | ip6tables (IPv6) |
|---|---|---|
| ที่อยู่ (Address format) | -s 192.168.1.0/24 |
-s 2001:db8::/32 |
| NAT | รองรับเต็มรูปแบบ (SNAT/DNAT/MASQUERADE) | ไม่นิยมใช้ NAT (IPv6 ออกแบบมาให้มี address เพียงพอโดยไม่ต้อง NAT) แต่มี NPTv6 ทดแทน |
| ICMP | ใช้ -p icmp |
ใช้ -p icmpv6 และมี type ที่ต่างกัน |
| Broadcast | มี broadcast address | ไม่มี broadcast ใช้ multicast แทนทั้งหมด |
| ARP | ใช้ ARP protocol แยก (arptables) | ใช้ NDP (Neighbor Discovery Protocol) แทน ARP |
# ตัวอย่างกฎพื้นฐานสำหรับ ip6tables (คล้าย iptables มาก)
ip6tables -A INPUT -i lo -j ACCEPT
ip6tables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
ip6tables -A INPUT -p tcp --dport 22 -j ACCEPT
ip6tables -P INPUT DROP
ข้อควรระวังสำคัญ: ระบบจำนวนมากตั้งค่า iptables (IPv4) อย่างเข้มงวดแต่ ลืมตั้งค่า ip6tables ทำให้เกิดช่องโหว่ด้านความปลอดภัย หากเครื่องมี IPv6 address และเปิดใช้งานอยู่ ผู้โจมตีสามารถเลี่ยงกฎ IPv4 ทั้งหมดได้โดยเชื่อมต่อผ่าน IPv6 แทน
ต่างจาก ICMPv4 ที่สามารถบล็อกได้เกือบทั้งหมดโดยไม่กระทบการทำงานพื้นฐาน ICMPv6 เป็นส่วนสำคัญของโปรโตคอล IPv6 เอง โดยเฉพาะ Neighbor Discovery Protocol (NDP) ซึ่งทำหน้าที่แทน ARP — หากบล็อก ICMPv6 ทั้งหมด เครือข่าย IPv6 จะไม่ทำงาน
| ICMPv6 Type | ชื่อ | ความสำคัญ |
|---|---|---|
| 1 | Destination Unreachable | ควรอนุญาต (ใช้แจ้ง error) |
| 2 | Packet Too Big | จำเป็น สำหรับ Path MTU Discovery |
| 128 | Echo Request | ping — เลือกอนุญาต/ปฏิเสธได้ตามนโยบาย |
| 129 | Echo Reply | คู่กับ Echo Request |
| 133 | Router Solicitation | จำเป็น สำหรับ NDP |
| 134 | Router Advertisement | จำเป็น สำหรับ NDP |
| 135 | Neighbor Solicitation | จำเป็น (ทำหน้าที่แทน ARP request) |
| 136 | Neighbor Advertisement | จำเป็น (ทำหน้าที่แทน ARP reply) |
# กฎที่จำเป็นสำหรับ ip6tables เพื่อให้ NDP ทำงานได้ปกติ (ต้องมีเสมอ ไม่ว่าจะเข้มงวดแค่ไหน)
ip6tables -A INPUT -p icmpv6 --icmpv6-type 133 -j ACCEPT
ip6tables -A INPUT -p icmpv6 --icmpv6-type 134 -j ACCEPT
ip6tables -A INPUT -p icmpv6 --icmpv6-type 135 -j ACCEPT
ip6tables -A INPUT -p icmpv6 --icmpv6-type 136 -j ACCEPT
ip6tables -A INPUT -p icmpv6 --icmpv6-type 2 -j ACCEPT
ip6tables -A OUTPUT -p icmpv6 -j ACCEPT
iptables-save / iptables-restoreกฎที่ตั้งด้วยคำสั่ง iptables จะอยู่ใน memory ของ kernel เท่านั้น และจะหายไปทันทีเมื่อ reboot เครื่อง จึงต้องใช้ iptables-save เพื่อบันทึกกฎเป็นไฟล์ และ iptables-restore เพื่อโหลดกลับ
# บันทึกกฎปัจจุบันทั้งหมดลงไฟล์
iptables-save > /etc/iptables/rules.v4
ip6tables-save > /etc/iptables/rules.v6
# กู้คืนกฎจากไฟล์ (แทนที่กฎปัจจุบันทั้งหมด)
iptables-restore < /etc/iptables/rules.v4
ip6tables-restore < /etc/iptables/rules.v6
# ตัวอย่าง: backup ก่อนทดลองแก้กฎ เพื่อให้ rollback ได้ง่ายหากมีปัญหา
iptables-save > /root/iptables-backup-$(date +%Y%m%d-%H%M%S).rules
Debian/Ubuntu ใช้แพ็กเกจ iptables-persistent ร่วมกับ service netfilter-persistent:
# ติดตั้งแพ็กเกจ (จะถามให้บันทึกกฎปัจจุบันทันทีระหว่างติดตั้ง)
apt install iptables-persistent
# บันทึกกฎปัจจุบันด้วยตนเองในภายหลัง (เมื่อแก้ไขกฎเพิ่มเติม)
netfilter-persistent save
# โหลดกฎกลับด้วยตนเอง
netfilter-persistent reload
RHEL/CentOS/Fedora ใช้ service iptables โดยตรง (หรือ iptables-services package):
# ติดตั้งและเปิดใช้งาน service
dnf install iptables-services
systemctl enable iptables ip6tables
# บันทึกกฎปัจจุบัน
service iptables save
วิธี systemd แบบ manual (ใช้ได้ทุก distro) — สร้าง unit file ที่รัน iptables-restore ตอนบูต:
# /etc/systemd/system/iptables-restore.service
[Unit]
Description=Restore iptables rules
Before=network-pre.target
Wants=network-pre.target
[Service]
Type=oneshot
ExecStart=/usr/sbin/iptables-restore /etc/iptables/rules.v4
ExecStart=/usr/sbin/ip6tables-restore /etc/iptables/rules.v6
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
# เปิดใช้งาน unit ที่สร้างขึ้น
systemctl enable iptables-restore.service
แนวปฏิบัติที่ดีสำหรับสภาพแวดล้อม production คือการเขียนกฎทั้งหมดเป็น shell script แทนที่จะรันคำสั่งทีละบรรทัด เพื่อให้สามารถเก็บใน Git และตรวจสอบประวัติการเปลี่ยนแปลงได้
#!/bin/bash
# firewall-rules.sh - จัดการด้วย git version control
# วิธีใช้: sudo ./firewall-rules.sh
set -e # หยุดทันทีถ้ามีคำสั่งใดล้มเหลว
echo "[*] กำลังล้างกฎเดิมทั้งหมด..."
iptables -F
iptables -X
iptables -t nat -F
iptables -t mangle -F
echo "[*] กำลังตั้งค่ากฎพื้นฐาน..."
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -m conntrack --ctstate INVALID -j DROP
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
echo "[*] บันทึกกฎลงไฟล์..."
iptables-save > /etc/iptables/rules.v4
echo "[+] เสร็จสิ้น! กฎทั้งหมดถูกนำไปใช้และบันทึกแล้ว"
การเก็บสคริปต์นี้ใน Git repository ทำให้ทีมสามารถ review การเปลี่ยนแปลงผ่าน pull request, ย้อนกลับ (rollback) เวอร์ชันเก่าได้ง่าย และมี audit trail ว่าใครเปลี่ยนกฎอะไรเมื่อไร
#!/bin/bash
# สคริปต์ firewall สำหรับเว็บเซิร์ฟเวอร์ทั่วไป (web server)
iptables -F
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
# อนุญาต loopback และ established connections
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -m conntrack --ctstate INVALID -j DROP
# เปิดเฉพาะ SSH, HTTP, HTTPS
iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -j ACCEPT
iptables -A INPUT -p tcp --dport 80 -m conntrack --ctstate NEW -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -m conntrack --ctstate NEW -j ACCEPT
# อนุญาต ping แบบจำกัดอัตรา
iptables -A INPUT -p icmp --icmp-type echo-request -m limit --limit 1/second -j ACCEPT
# ตัวอย่างการทดสอบ: ตรวจสอบว่า port อื่นถูกปิดจริง
# nmap -p 1-1000 <server_ip> (ควรเห็นเฉพาะ 22, 80, 443 เปิด)
#!/bin/bash
# LAN = eth1 (192.168.1.0/24), WAN = eth0
echo 1 > /proc/sys/net/ipv4/ip_forward
iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -o eth0 -j MASQUERADE
iptables -A FORWARD -i eth1 -o eth0 -s 192.168.1.0/24 -j ACCEPT
iptables -A FORWARD -i eth0 -o eth1 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A FORWARD -j DROP
# ตัวอย่างการทดสอบจากเครื่องภายใน:
# curl -I https://example.com (ควรได้รับ response ปกติ)
# อนุญาต SSH เฉพาะจาก IP office และ VPN gateway เท่านั้น
iptables -A INPUT -p tcp --dport 22 -s 203.0.113.10 -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -s 198.51.100.20 -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -j DROP
# ตัวอย่างการทดสอบ: จาก IP ที่ไม่อยู่ในรายการ
# ssh user@server_ip -> ควร timeout / connection refused
# จาก IP ที่อนุญาต -> ควรเชื่อมต่อได้ปกติ
# ตัวอย่าง: forward port ของ game server (Minecraft, port 25565) ไปยังเครื่องภายใน
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 25565 -j DNAT --to-destination 192.168.1.50:25565
iptables -A FORWARD -p tcp -d 192.168.1.50 --dport 25565 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
# ตัวอย่าง: forward Minecraft ที่ใช้ UDP query (ถ้ามี)
iptables -t nat -A PREROUTING -i eth0 -p udp --dport 25565 -j DNAT --to-destination 192.168.1.50:25565
iptables -A FORWARD -p udp -d 192.168.1.50 --dport 25565 -j ACCEPT
# ตัวอย่างการทดสอบจากภายนอก:
# nc -zv <public_ip> 25565 (ควรเชื่อมต่อได้สำเร็จ)
Transparent proxy คือการดักจับ traffic ทั้งหมดที่ผ่านเครื่อง (โดยที่ client ไม่ต้องตั้งค่า proxy เอง) แล้วเปลี่ยนเส้นทางไปยัง proxy service ที่รันบน port อื่นบนเครื่องเดียวกัน
# redirect HTTP traffic (port 80) ทั้งหมดที่ผ่าน router ไปยัง Squid proxy ที่ port 3128
iptables -t nat -A PREROUTING -i eth1 -p tcp --dport 80 -j REDIRECT --to-port 3128
# ตัวอย่างการทดสอบ: จากเครื่องภายในเปิดเว็บโดยไม่ตั้งค่า proxy ใด ๆ
# curl -v http://example.com (traffic จะถูกดักและส่งผ่าน proxy โดยอัตโนมัติ)
# ตรวจสอบใน log ของ Squid ว่ามี request เข้ามาจริง: tail -f /var/log/squid/access.log
ข้อจำกัด: REDIRECT ใช้ได้เฉพาะ traffic ที่ผ่านเครื่องนี้เท่านั้น (PREROUTING/OUTPUT) และ proxy ปลายทางต้องรองรับโหมด transparent (เช่น Squid ต้องตั้งค่า http_port 3128 intercept)
Docker จัดการ iptables ของตัวเองโดยอัตโนมัติ (สร้าง chain ชื่อ DOCKER, DOCKER-ISOLATION-STAGE-1/2, DOCKER-USER) เพื่อทำ NAT และเปิด port ตาม -p flag ที่ใช้ตอนรัน container — ปัญหาที่พบบ่อยที่สุดคือผู้ดูแลระบบตั้งกฎ iptables -A INPUT ... DROP เพื่อบล็อก port แต่ container ยังเข้าถึงได้จากภายนอกอยู่ดี เพราะ Docker แทรกกฎของตัวเองไว้ใน chain FORWARD ซึ่งมาก่อนกฎ INPUT ทั่วไป
flowchart TD
PACKET["แพ็กเก็ตเข้ามาที่ port ของ container
(Incoming to Container Port)"] --> DOCKER_CHAIN["DOCKER chain
(DNAT ไปยัง container IP)"]
DOCKER_CHAIN --> FORWARD["FORWARD chain"]
FORWARD --> USER_CHAIN["DOCKER-USER chain
(จุดที่ผู้ดูแลระบบควรเพิ่มกฎเอง)"]
USER_CHAIN --> ISOLATION["DOCKER-ISOLATION-STAGE-1/2
(แยก network ระหว่าง container)"]
ISOLATION --> CONTAINER["ส่งถึง Container
(Container Application)"]
วิธีแก้ปัญหาที่ถูกต้อง: ต้องเพิ่มกฎที่ chain DOCKER-USER แทน INPUT เพราะ chain นี้ถูกออกแบบมาให้ผู้ดูแลระบบแทรกกฎของตัวเองได้โดยไม่ถูก Docker เขียนทับเมื่อ restart service
# บล็อก IP ที่ไม่ต้องการให้เข้าถึง container ใด ๆ เลย (ต้องเพิ่มที่ DOCKER-USER)
iptables -I DOCKER-USER -s 203.0.113.99 -j DROP
# อนุญาตเฉพาะ subnet ภายในให้เข้าถึง container ที่เปิด port ไว้ (whitelist)
iptables -I DOCKER-USER -p tcp --dport 8080 ! -s 192.168.1.0/24 -j DROP
# ตัวอย่างการทดสอบ: รัน container ที่เปิด port 8080 แล้วทดสอบจาก IP นอก whitelist
# docker run -d -p 8080:80 nginx
# curl http://<host_ip>:8080 (จาก IP นอก whitelist ควรถูกปฏิเสธ)
# ตัวอย่าง: WireGuard interface wg0 (10.8.0.0/24) ต้องการ route ผ่าน WAN (eth0)
echo 1 > /proc/sys/net/ipv4/ip_forward
# MASQUERADE traffic จาก VPN client ที่จะออกอินเทอร์เน็ตผ่าน WAN
iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE
# อนุญาต forward traffic ระหว่าง VPN interface และ WAN
iptables -A FORWARD -i wg0 -o eth0 -j ACCEPT
iptables -A FORWARD -i eth0 -o wg0 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# เปิด UDP port ของ WireGuard เอง (default 51820) ให้ client เชื่อมต่อเข้ามาได้
iptables -A INPUT -p udp --dport 51820 -j ACCEPT
# ตัวอย่างการทดสอบ: จากเครื่อง VPN client ตรวจสอบว่า traffic ออกผ่าน public IP ของ server
# curl https://ifconfig.me (ควรแสดง public IP ของ VPN server ไม่ใช่ IP ของ client เอง)
-v, Packet/Byte Counters)# แสดงกฎพร้อมตัวนับ packet/byte ที่ตรงกับแต่ละกฎ (มีประโยชน์มากในการ debug)
iptables -L -n -v --line-numbers
# ตัวอย่างผลลัพธ์ที่ควรสังเกต:
# Chain INPUT (policy DROP 42 packets, 3360 bytes)
# num pkts bytes target prot opt in out source destination
# 1 150 9000 ACCEPT tcp -- any any 0.0.0.0/0 0.0.0.0/0 tcp dpt:22
# 2 0 0 ACCEPT tcp -- any any 0.0.0.0/0 0.0.0.0/0 tcp dpt:443
#
# หากกฎที่ 2 (port 443) มีค่า pkts/bytes เป็น 0 ตลอด แสดงว่าไม่มีแพ็กเก็ตใดตรงกับกฎนี้เลย
# อาจเกิดจากกฎก่อนหน้าดักจับแพ็กเก็ตไปก่อน (first-match-wins) หรือ service ยังไม่ได้เปิดใช้งานจริง
# ตรวจสอบเฉพาะ chain เดียวแบบละเอียด
iptables -L INPUT -n -v --line-numbers
# รีเซ็ตตัวนับก่อนทดสอบ เพื่อดูว่ากฎไหนทำงานระหว่างการทดสอบจริง
iptables -Z INPUT
# จากนั้นทำการทดสอบ (เช่น curl, ssh) แล้วดูผลอีกครั้ง
iptables -L INPUT -n -v
conntrack -L ตรวจสอบ Connection Tracking Table# ติดตั้งเครื่องมือ conntrack (หากยังไม่มี)
apt install conntrack # Debian/Ubuntu
# หรือ dnf install conntrack-tools # RHEL/Fedora
# แสดงรายการ connection ทั้งหมดที่ conntrack กำลังติดตามอยู่
conntrack -L
# กรองเฉพาะ connection ที่เกี่ยวข้องกับ IP ปลายทางที่สงสัย
conntrack -L -d 192.168.1.10
# ดูสถิติสรุปของ conntrack (จำนวน connection ปัจจุบัน, ค่าสูงสุดที่ตั้งไว้)
conntrack -S
cat /proc/sys/net/netfilter/nf_conntrack_max
cat /proc/sys/net/netfilter/nf_conntrack_count
# ลบ connection ที่ค้างอยู่ (ใช้เมื่อสงสัยว่า state เก่าทำให้กฎใหม่ไม่มีผล)
conntrack -D -d 192.168.1.10
ปัญหาที่พบบ่อย: หากตาราง conntrack เต็ม (nf_conntrack_count ใกล้เคียงกับ nf_conntrack_max) แพ็กเก็ตใหม่ทั้งหมดจะถูกทิ้งแม้ว่ากฎ iptables จะอนุญาตไว้ก็ตาม เพราะ kernel ไม่สามารถสร้าง state ใหม่ได้ วิธีแก้คือเพิ่มค่า nf_conntrack_max ผ่าน sysctl
| ปัญหา | อาการ | สาเหตุ | วิธีแก้ |
|---|---|---|---|
| ลำดับกฎผิด | กฎ ACCEPT ที่เพิ่มทีหลังไม่มีผล | มีกฎ DROP ที่ตรงเงื่อนไขกว้างกว่าอยู่ก่อนหน้า (first-match-wins) | ใช้ -I แทรกกฎ ACCEPT ไว้ก่อนกฎ DROP หรือจัดลำดับกฎใหม่ทั้งหมด |
| ลืมอนุญาต ESTABLISHED,RELATED | connection ขาออกทำงาน แต่ traffic ขาเข้าที่เป็นการตอบกลับถูกบล็อก | ตั้งกฎกรองเฉพาะทิศทางเดียวโดยไม่คำนึงถึง state ของ connection | เพิ่ม -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT ไว้ต้น ๆ ของ chain เสมอ |
| Policy DROP ล็อกตัวเองออกจาก SSH | รัน iptables -P INPUT DROP แล้วเชื่อมต่อ SSH ไม่ได้ทันที |
ตั้ง default policy เป็น DROP ก่อนที่จะมีกฎ ACCEPT สำหรับ SSH อยู่แล้ว | เสมอ ตั้งกฎ ACCEPT SSH ก่อน แล้วค่อยเปลี่ยน policy เป็น DROP ทีหลัง หรือทดสอบผ่าน console ที่ไม่ผ่านเครือข่ายก่อนเสมอ |
| DNAT ไม่ทำงาน | port forward ตั้งค่าแล้วแต่เข้าถึงไม่ได้จากภายนอก | ลืมเปิด FORWARD chain ให้ traffic ที่ถูก DNAT ผ่านได้ | เพิ่มกฎ ACCEPT ใน FORWARD chain สำหรับปลายทางที่ระบุใน DNAT |
| ip_forward ปิดอยู่ | ตั้งกฎ NAT/FORWARD ครบแล้วแต่ router ไม่ส่งต่อ traffic เลย | net.ipv4.ip_forward ยังเป็น 0 |
echo 1 > /proc/sys/net/ipv4/ip_forward และตั้งถาวรใน sysctl.conf |
เทคนิคความปลอดภัยเมื่อทดลองแก้กฎ default policy จากระยะไกล: ใช้คำสั่งที่มี timeout กลับคืนอัตโนมัติ เพื่อป้องกันการล็อกตัวเองออก:
# ตั้ง policy DROP ชั่วคราว 60 วินาที แล้วคืนค่าเดิมอัตโนมัติหากไม่ได้ยกเลิกคำสั่งนี้
# (เทคนิคนี้ใช้ at command เพื่อ schedule การ rollback)
echo "iptables -P INPUT ACCEPT" | at now + 1 minute
iptables -P INPUT DROP
# หากทดสอบแล้วเชื่อมต่อได้ปกติ ให้ยกเลิก job ที่ schedule ไว้: atq แล้ว atrm <job_id>
firewalld เป็น daemon ที่ทำงานอยู่เบื้องหลัง (background service) และให้ abstraction แบบ zone-based โดยแต่ละ zone (เช่น public, internal, trusted) มีชุดกฎของตัวเอง ทำให้จัดการ policy ตามระดับความน่าเชื่อถือของเครือข่ายได้ง่าย โดยไม่ต้อง restart service เมื่อแก้กฎ (dynamic rule reload)
# ตัวอย่างคำสั่ง firewalld: เปิด service http ใน zone public แบบถาวร
firewall-cmd --zone=public --add-service=http --permanent
firewall-cmd --reload
ufw เป็น wrapper ที่ออกแบบมาเพื่อความง่ายในการใช้งาน เหมาะกับผู้เริ่มต้นหรือเซิร์ฟเวอร์ที่มีความต้องการไม่ซับซ้อน ซ่อนความซับซ้อนของ syntax iptables ไว้เบื้องหลังคำสั่งที่อ่านง่าย
# ตัวอย่างคำสั่ง ufw: อนุญาต SSH และเปิดใช้งาน firewall
ufw allow ssh
ufw enable
nftables คือตัวสืบทอด (successor) อย่างเป็นทางการของ iptables ตั้งแต่ kernel 3.13 มี syntax ที่กระชับกว่า รองรับ IPv4/IPv6 ในกฎเดียวกัน และใช้โครงสร้างข้อมูล set/map ที่ค้นหาได้เร็วกว่า
# ตัวอย่างคำสั่ง nftables ที่เทียบเท่ากับ iptables ACCEPT SSH
nft add rule inet filter input tcp dport 22 accept
ตารางเปรียบเทียบสรุป:
| คุณสมบัติ | iptables | ip6tables | firewalld | ufw | nftables |
|---|---|---|---|---|---|
| ระดับ (Layer) | Kernel API โดยตรง | Kernel API โดยตรง | Daemon ครอบ iptables/nftables | Wrapper ครอบ iptables | Kernel API รุ่นใหม่ |
| รองรับ IPv4+IPv6 พร้อมกัน | ไม่ (ต้องแยก ip6tables) | ไม่ (ต้องใช้คู่กับ iptables) | ได้ | ได้ (ผ่าน iptables คู่) | ได้ในกฎเดียว |
| ความง่ายสำหรับผู้เริ่มต้น | ปานกลาง-ยาก | ปานกลาง-ยาก | ปานกลาง | ง่ายมาก | ปานกลาง |
| Dynamic reload (ไม่ flush ทั้งหมด) | ไม่ (ต้อง flush/restore) | ไม่ | ได้ | ไม่ | ได้ (atomic rule replace) |
| ประสิทธิภาพกับกฎจำนวนมาก | ต่ำ (linear scan) | ต่ำ | ขึ้นกับ backend | ขึ้นกับ backend | สูง (set/map based) |
| สถานะปัจจุบัน | Maintenance mode (ผ่าน iptables-nft) | Maintenance mode | ใช้งานอยู่ทั่วไป (RHEL default) | ใช้งานอยู่ทั่วไป (Ubuntu default) | มาตรฐานใหม่ (kernel native) |
เป้าหมาย: ตั้งค่าเซิร์ฟเวอร์ Linux ให้เปิดเฉพาะ SSH, HTTP, HTTPS และปฏิเสธทุกอย่างที่เหลือ
อุปกรณ์/สภาพแวดล้อมที่ต้องการ: เครื่อง Linux 1 เครื่อง (VM หรือ container ก็ได้) ที่มีสิทธิ์ root
ขั้นตอน:
iptables -L -n -viptables-save > /root/backup-before-lab.rulesnmap -p 1-1000 <server_ip> จากเครื่องอื่น ควรเห็นเฉพาะ port 22, 80, 443 เปิดอยู่ผลลัพธ์ที่คาดหวัง: เซิร์ฟเวอร์ตอบสนองเฉพาะ 3 port ที่กำหนด, port อื่นทั้งหมดถูกทิ้งแบบเงียบ (DROP)
คำถามท้าย Lab: หากต้องการเปิดเพิ่มเติมสำหรับ monitoring agent (เช่น Prometheus node_exporter บน port 9100) แต่จำกัดให้เข้าถึงได้เฉพาะจาก monitoring server เท่านั้น จะต้องเขียนกฎอย่างไร?
เป้าหมาย: ตั้งค่าเครื่อง Linux ที่มี 2 network interface ให้ทำหน้าที่เป็น router แชร์อินเทอร์เน็ต
อุปกรณ์/สภาพแวดล้อมที่ต้องการ: เครื่อง Linux ที่มี interface อย่างน้อย 2 ใบ (เช่น eth0 ต่อ WAN, eth1 ต่อ LAN switch) และเครื่องลูกข่ายอย่างน้อย 1 เครื่องต่อกับ LAN
ขั้นตอน:
192.168.1.1/24echo 1 > /proc/sys/net/ipv4/ip_forward192.168.1.1 เป็น default gateway และ DNS (หรือใช้ DHCP server แยกต่างหาก)ping 8.8.8.8 และ curl -I https://example.comผลลัพธ์ที่คาดหวัง: เครื่องลูกข่ายในวง LAN สามารถออกอินเทอร์เน็ตได้ผ่าน router โดยที่ traffic ขาออกทั้งหมดถูกแปลง source IP เป็น public IP ของ router (ตรวจสอบได้ด้วย tcpdump -i eth0 icmp บน router ระหว่างทดสอบ ping)
คำถามท้าย Lab: หากต้องการให้เครื่องลูกข่ายบางเครื่อง (เช่น IoT device) ไม่สามารถออกอินเทอร์เน็ตได้เลย แต่ยังคุยกับเครื่องอื่นใน LAN ได้ปกติ จะต้องเพิ่มกฎใน FORWARD chain ตรงไหนและอย่างไร?
เป้าหมาย: ตั้งค่ากฎเพื่อบล็อก IP ที่พยายาม login SSH ผิดพลาดซ้ำ ๆ ในเวลาสั้น
อุปกรณ์/สภาพแวดล้อมที่ต้องการ: เครื่อง Linux ที่เปิด SSH service และเครื่องอีกเครื่องสำหรับจำลองการโจมตี
ขั้นตอน:
ssh_attack และ DROP เมื่อเกิน 4 ครั้งใน 60 วินาที)for i in {1..6}; do ssh -o ConnectTimeout=2 -o BatchMode=yes testuser@<server_ip>; donecat /proc/net/xt_recent/ssh_attackผลลัพธ์ที่คาดหวัง: IP ที่พยายามเชื่อมต่อเกิน 4 ครั้งภายใน 60 วินาทีจะถูกบล็อกโดยอัตโนมัติ และปลดบล็อกเองเมื่อพ้นช่วงเวลาที่กำหนด
คำถามท้าย Lab: หากต้องการให้ IP ที่ถูกบล็อกไปแล้ว ถูกบล็อกถาวร (permanent ban) แทนที่จะปลดบล็อกอัตโนมัติ ควรออกแบบระบบอย่างไร (คำใบ้: พิจารณาการเชื่อม recent module เข้ากับ ipset ผ่านสคริปต์ภายนอกที่อ่าน log)
เป้าหมาย: ตั้งค่า DNAT เพื่อ forward port จากเครื่อง host เข้าไปยัง container หรือ VM ที่รันเว็บเซิร์ฟเวอร์อยู่ภายใน
อุปกรณ์/สภาพแวดล้อมที่ต้องการ: เครื่อง Linux host ที่รัน container (Docker/Podman) หรือ VM (KVM/VirtualBox) อย่างน้อย 1 ตัว ที่มี IP ภายใน (เช่น 192.168.122.10 สำหรับ libvirt bridge)
ขั้นตอน:
docker run -d --name web nginx แต่ไม่ต้อง publish port ผ่าน docker เอง หากต้องการทดลองทำ DNAT ด้วยมือ ให้ใช้ --network=host หรือ VM ที่มี bridge interface แยก)docker inspect web | grep IPAddresscurl http://<host_ip>:8888ผลลัพธ์ที่คาดหวัง: เมื่อเข้าถึง <host_ip>:8888 จากภายนอก จะได้รับหน้าเว็บ nginx default page ซึ่งพิสูจน์ว่าแพ็กเก็ตถูกแปลงปลายทางและส่งต่อไปยัง container สำเร็จ
คำถามท้าย Lab: หากต้องการ forward หลาย port ไปยังหลาย container พร้อมกัน (เช่น container A รับ 8081, container B รับ 8082) ควรออกแบบกฎ DNAT อย่างไรให้จัดการง่ายและอ่านง่ายเมื่อจำนวน container เพิ่มขึ้นในอนาคต (คำใบ้: พิจารณาการใช้ custom chain แยกตาม service)
iptables เป็นเครื่องมือที่ทรงพลังสำหรับการควบคุมการไหลของแพ็กเก็ตบน Linux โดยอาศัยสถาปัตยกรรมของ Netfilter framework ที่มี table และ chain เป็นโครงสร้างหลัก การเข้าใจลำดับการไหลของแพ็กเก็ต (packet flow), หลักการ stateful inspection ผ่าน conntrack, และเทคนิคการทำ NAT เป็นพื้นฐานสำคัญที่นำไปประยุกต์ใช้ได้ทั้งกับ iptables เองและเครื่องมือรุ่นใหม่อย่าง nftables
แม้ในระยะยาว nftables จะเข้ามาแทนที่ iptables ในฐานะ engine หลักของ kernel แต่แนวคิดพื้นฐานที่อธิบายไว้ในบทความนี้ — ตั้งแต่ table/chain, connection tracking, NAT, ไปจนถึงเทคนิคการป้องกันการโจมตี — ยังคงเป็นความรู้ที่จำเป็นสำหรับผู้ดูแลระบบเครือข่ายทุกคน
เอกสารนี้จัดทำขึ้นเพื่อการศึกษาและอ้างอิงทางเทคนิค ควรทดสอบกฎทั้งหมดในสภาพแวดล้อมทดสอบ (staging/lab environment) ก่อนนำไปใช้งานจริงบนระบบ production เสมอ