SSD Nodes Learn Hosting plans →
คู่มือ Matt Connorโดย Matt Connor · อัปเดตเมื่อ 2026-08-28

วิธีตั้งค่า UFW ป้องกันช่องโหว่ IPv6 บน VPS ให้ปลอดภัย

หลายคนตั้งค่า UFW เฉพาะ IPv4 จนลืมว่า VPS มี IPv6 สาธารณะเปิดอยู่ เรียนรู้วิธีตรวจสอบพอร์ตที่รั่วไหลและตั้งค่าไฟร์วอลล์ให้ครอบคลุมทั้งสองโปรโตคอลเพื่อปิดช่องโหว่บน Ubuntu 24.04

กับดักของไฟร์วอลล์ IPv6 ในประโยคเดียว

ไฟร์วอลล์ของคุณทำหน้าที่ปกป้อง IPv4 แต่ VPS ของคุณเกือบทั้งหมดจะมีที่อยู่ IPv6 สาธารณะมาให้ด้วย และบริการจำนวนมากก็เปิดรับการเชื่อมต่อผ่าน IPv6 เป็นค่าเริ่มต้น หากไฟร์วอลล์ของคุณครอบคลุมเฉพาะ IPv4 หรือคุณพึ่งพาไฟร์วอลล์บนคลาวด์ที่กรองเฉพาะ IPv4 บริการเหล่านั้นทั้งหมดจะสามารถเข้าถึงได้จากอินเทอร์เน็ตผ่าน IPv6 ในขณะที่ฝั่ง IPv4 ของคุณดูเหมือนจะถูกล็อกไว้อย่างแน่นหนา คุณอาจทดสอบพอร์ตด้วย curl แล้วพบว่าการเชื่อมต่อถูกปฏิเสธจึงรู้สึกปลอดภัย แต่ผู้โจมตีสามารถเชื่อมต่อเข้าสู่พอร์ตเดียวกันผ่าน IPv6 และเข้าถึงระบบของคุณได้โดยตรง

คู่มือนี้จะแสดงให้เห็นว่าช่องโหว่นี้เกิดขึ้นได้อย่างไรบน Ubuntu 24.04 VPS ทั่วไป วิธีตรวจสอบว่าคุณกำลังเปิดเผยข้อมูลอะไรออกไปบ้าง และวิธีปิดช่องโหว่ดังกล่าว UFW ไม่ใช่ตัวการในเรื่องนี้ บนการติดตั้ง Ubuntu สมัยใหม่ UFW จัดการ IPv6 ไว้ให้อยู่แล้ว แต่การเปิดเผยข้อมูลเกิดขึ้นจากเลเยอร์รอบข้างและจากบริการที่คุณไม่ทราบว่ากำลังเปิดรับการเชื่อมต่ออยู่

เหตุผลที่ VPS ของคุณมี IPv6 ตั้งแต่แรก

VPS เกือบทุกแห่งในปัจจุบันมาพร้อมกับ public IPv6 address ซึ่งมักจะเป็น /64 ทั้งชุด ควบคู่ไปกับ IPv4 address ของคุณ ตรวจสอบของคุณได้ดังนี้:

ip -6 addr show scope global
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> ...
    inet6 2001:db8:2a::1/64 scope global

2001:db8:2a::1 นั้นสามารถเข้าถึงได้จากทุกที่บนอินเทอร์เน็ต เช่นเดียวกับ IPv4 address ของคุณ ตอนนี้ลองดูว่ามีอะไรกำลังทำงานอยู่บ้าง:

sudo ss -tlnp
State   Recv-Q  Local Address:Port   Process
LISTEN  0       0.0.0.0:22           sshd
LISTEN  0       [::]:22              sshd
LISTEN  0       127.0.0.1:5432       postgres
LISTEN  0       [::]:8080            docker-proxy

อ่านคอลัมน์ Local Address ให้ละเอียด 0.0.0.0:22 หมายถึง "รอรับการเชื่อมต่อบนทุก IPv4 address" [::]:22 หมายถึง "รอรับการเชื่อมต่อบนทุก IPv6 address" 127.0.0.1:5432 ถูกผูกไว้กับ loopback และไม่ได้เป็นสาธารณะ ดังนั้นบรรทัดของ Postgres จึงปลอดภัย สองบรรทัด [::] ตอบสนองต่ออินเทอร์เน็ตทั้งหมดผ่าน IPv6 และบรรทัด docker-proxy คือประเภทที่คุณลืมไปว่าได้เริ่มการทำงานไว้

daemon ส่วนใหญ่ผูกกับ :: โดยค่าเริ่มต้น เพราะบน Linux นั้น :: socket มักจะยอมรับ IPv4 ด้วยเช่นกัน ดังนั้นสถานะเริ่มต้นของเซิร์ฟเวอร์ใหม่คือ "ตอบสนองบนทั้งสอง stack จากทุกที่" firewall ของคุณเป็นสิ่งเดียวที่ขวางกั้นอยู่ นั่นคือเหตุผลว่าทำไม firewall ที่มองเห็นเพียง stack เดียวจึงเป็นปัญหาที่แท้จริง

ที่มาของช่องโหว่ IPv6 จริงๆ แล้วมาจากไหน

มีแหล่งที่มาทั่วไป 4 ประการ บนเซิร์ฟเวอร์หนึ่งเครื่องคุณอาจพบปัญหาเหล่านี้เพียงข้อเดียวหรือหลายข้อพร้อมกัน

1. Cloud firewall ที่กรองเฉพาะ IPv4 ไฟร์วอลล์และผลิตภัณฑ์กลุ่มความปลอดภัยของผู้ให้บริการหลายรายถูกพัฒนาขึ้นโดยเน้น IPv4 เป็นหลัก ซึ่งมักจะละเลย IPv6 หรือต้องการให้คุณเพิ่มกฎ IPv6 แยกต่างหากด้วยตนเอง หากไฟร์วอลล์เดียวที่คุณมีคือไฟร์วอลล์ในแดชบอร์ดของผู้ให้บริการและมันไม่ครอบคลุม IPv6 บริการ [::] ของคุณจะเปิดเผยต่อสาธารณะไม่ว่าแดชบอร์ดจะระบุว่าพอร์ต 22 บน IPv4 ถูกปิดไว้อย่างไรก็ตาม โปรดอ่านเอกสารประกอบไฟร์วอลล์ของผู้ให้บริการของคุณและมองหาคำว่า IPv6 โดยเฉพาะ

2. การเขียน iptables ด้วยตนเองโดยไม่มี ip6tables คำสั่ง iptables จะจัดการเฉพาะตารางของ IPv4 เท่านั้น IPv6 มีคำสั่งแยกต่างหากคือ ip6tables ซึ่งมีกฎของตัวเอง หากคุณเขียนสคริปต์ไฟร์วอลล์ที่เต็มไปด้วยบรรทัด iptables -A INPUT ... แต่ไม่เคยเขียนกฎ ip6tables ที่สอดคล้องกัน ไฟร์วอลล์ IPv6 ของคุณจะว่างเปล่า และ chain INPUT ที่ว่างเปล่าพร้อมนโยบายเริ่มต้นเป็น ACCEPT จะอนุญาตให้ทุกอย่างผ่านเข้ามาได้:

sudo ip6tables -L INPUT -n
Chain INPUT (policy ACCEPT)
target     prot opt source               destination

ผลลัพธ์นั้นคือกับดักทั้งหมดที่ปรากฏบนหน้าจอเดียว IPv4 ถูกกรอง แต่ IPv6 ยอมรับการเชื่อมต่อจากทั่วโลก

3. Docker เผยแพร่พอร์ตโดยข้ามไฟร์วอลล์ของคุณไปโดยตรง เมื่อคุณรัน docker run -p 8080:80 Docker จะแทรกกฎของตัวเองไว้หน้ากฎของ UFW ดังนั้นพอร์ตที่ถูกเผยแพร่จึงสามารถเข้าถึงได้แม้ว่า ufw status จะระบุว่าพอร์ตนั้นถูกปฏิเสธก็ตาม และใน Docker เวอร์ชันปัจจุบัน สิ่งนี้ยังคงมีผลกับ IPv6 เช่นกัน เหตุใด Docker จึงข้าม UFW และวิธีจัดการพอร์ตคอนเทนเนอร์อย่างถูกต้อง อธิบายถึงกลไกและวิธีแก้ไข ดู พื้นฐานของ Docker Compose บน VPS เพื่อดูว่าพอร์ตที่เผยแพร่เหล่านี้ถูกประกาศอย่างไร

4. UFW ที่ปิดการใช้งาน IPv6 ไว้ UFW สามารถจัดการ IPv6 ได้ แต่ต้องได้รับคำสั่งให้ทำเช่นนั้น ตรวจสอบสวิตช์การตั้งค่า:

grep IPV6 /etc/default/ufw

Ubuntu รุ่นใหม่จะตั้งค่า IPV6=yes ไว้เป็นค่าเริ่มต้น ดังนั้น UFW จะนำกฎแต่ละข้อไปใช้กับทั้งสอง stack หากคุณเห็น IPV6=no ซึ่งอาจมาจากอิมเมจเก่าหรือคู่มือเก่า กฎ UFW ทุกข้อที่คุณเขียนไว้จะมีผลเฉพาะ IPv4 เท่านั้น และ IPv6 จะถูกปล่อยทิ้งไว้โดยไม่มีการจัดการ

ตรวจสอบสิ่งที่เปิดเผยอยู่จริง

อย่าคาดเดา ให้วัดผลจากภายนอก เริ่มจากการแสดงรายการพอร์ตที่เปิดใช้งานอยู่และจดบันทึกทุกพอร์ตที่ผูกกับ :::

sudo ss -tlnp | grep '::'

จากนั้น ให้ใช้เครื่องอื่นเชื่อมต่อไปยัง IPv6 address สาธารณะของเซิร์ฟเวอร์ แล้วลองทดสอบพอร์ตที่คุณคิดว่าปิดอยู่:

curl -6 -v http://[2001:db8:2a::1]:8080/

หากการเชื่อมต่อแสดงหน้าเว็บหรือข้อความ banner แสดงว่าพอร์ตนั้นเปิดอยู่บน IPv6 พอร์ตที่ปิดอยู่จะส่งผลเป็น Connection refused หรือการหมดเวลา (timeout) ความล้มเหลวทั้งสองรูปแบบนี้ให้สัญญาณที่ต่างกัน และ ความแตกต่างระหว่างการปฏิเสธและการหมดเวลา จะบอกคุณได้ว่าโฮสต์ตอบกลับและปฏิเสธคุณ หรือไฟร์วอลล์ทิ้งแพ็กเก็ตของคุณไปโดยไม่มีการตอบสนอง เพื่อให้เห็นภาพรวมทั้งหมด ให้สแกน IPv6 address ด้วย nmap จากภายนอกเซิร์ฟเวอร์:

nmap -6 2001:db8:2a::1

ทุกพอร์ตที่ nmap รายงานว่าเปิดอยู่บน IPv6 คือพอร์ตที่อินเทอร์เน็ตทั้งโลกสามารถเข้าถึงได้ ไม่ว่าการสแกน IPv4 ของคุณจะแสดงผลอย่างไร การเปรียบเทียบผลการสแกน IPv4 และ IPv6 ควบคู่กันเป็นวิธีที่เร็วที่สุดในการค้นหาช่องโหว่: สิ่งใดก็ตามที่เปิดอยู่บน -6 แต่ปิดอยู่บน IPv4 คือบริการที่ไฟร์วอลล์ของคุณยังไม่ได้ป้องกันไว้

ปิดช่องโหว่

ทำให้ UFW ครอบคลุมทั้งสอง stack และตั้งค่าเริ่มต้นเป็น deny ยืนยันการสลับโหมด จากนั้นตั้งค่านโยบายขาเข้าเริ่มต้นเป็น deny และอนุญาตเฉพาะสิ่งที่จำเป็นเท่านั้น:

sudo sed -i 's/^IPV6=no/IPV6=yes/' /etc/default/ufw
sudo ufw default deny incoming
sudo ufw allow 22/tcp
sudo ufw enable
sudo ufw status verbose

หาก UFW ทำงานอยู่แล้วในขณะที่คุณสลับ IPV6=yes การเปลี่ยนแปลงจะยังไม่มีผลจนกว่าคุณจะรัน sudo ufw reload

ufw status จะแสดงรายการกฎแต่ละข้อสองครั้ง ครั้งหนึ่งเป็นแบบปกติและอีกครั้งที่มี suffix เป็น (v6) เมื่อคุณเห็นบรรทัด (v6) แสดงว่า UFW กำลังกรอง IPv6 อยู่:

22/tcp                     ALLOW IN    Anywhere
22/tcp (v6)                ALLOW IN    Anywhere (v6)

หากคุณจัดการ iptables ด้วยตนเอง ให้ทำซ้ำกฎทุกข้อใน ip6tables หรือย้ายไปใช้ nftables ซึ่งตาราง inet จะครอบคลุมทั้ง IPv4 และ IPv6 ในที่เดียวและขจัดความผิดพลาดประเภทนี้ทั้งหมด ตารางกรอง inet ของ nftables เพียงตารางเดียวเป็นวิธีแก้ไขที่สะอาดที่สุดเมื่อคุณเขียนกฎด้วยตนเอง หาก VPS ของคุณใช้ Rocky หรือ AlmaLinux แทนที่จะเป็น Ubuntu จะไม่มี UFW ให้กำหนดค่า และ firewalld คือส่วนหน้าที่คุณต้องจัดการแทน ซึ่งจะใช้กฎ zone กับทั้งสอง stack พร้อมกัน

ผูกบริการที่คุณไม่ต้องการให้เป็นสาธารณะไว้กับ loopback ฐานข้อมูล, แผงควบคุมผู้ดูแลระบบ หรือ endpoint สำหรับวัดผลแทบไม่จำเป็นต้องมีที่อยู่สาธารณะเลย ให้ผูกบริการเหล่านั้นไว้กับ 127.0.0.1 และ ::1 เพื่อไม่ให้มันฟังบนที่อยู่ที่สามารถเข้าถึงได้จากภายนอกตั้งแต่แรก สำหรับ Postgres ให้ตั้งค่า listen_addresses = 'localhost' สำหรับแอปเซิร์ฟเวอร์ ให้ผูกไว้กับ 127.0.0.1 และวาง reverse proxy ไว้ด้านหน้า การปิด listener นั้นดีกว่าการใช้ไฟร์วอลล์กั้น เพราะจะไม่มีช่องทางให้เข้าถึงได้เลย

อย่าเชื่อใจ UFW ในการป้องกันพอร์ตที่ Docker เปิดใช้งาน ให้เผยแพร่พอร์ตของ container ไปยังที่อยู่ที่เฉพาะเจาะจงแทนที่จะเป็นทุก interface เช่น -p 127.0.0.1:8080:80 เพื่อให้พอร์ตนั้นสามารถเข้าถึงได้จากโฮสต์และสิ่งที่คุณตั้งใจ proxy ไปหาเท่านั้น เมื่อ container จำเป็นต้องเป็นสาธารณะจริงๆ ให้วางไว้หลัง Traefik reverse proxy และเผยแพร่เฉพาะตัว proxy เท่านั้น ไม่ใช่เผยแพร่ทุกแอปแยกกัน

เพิ่มกฎ IPv6 ลงในไฟร์วอลล์ของผู้ให้บริการของคุณ หรือยอมรับว่านั่นไม่ใช่ไฟร์วอลล์สำหรับ IPv6 ของคุณ และปล่อยให้ UFW หรือ nftables บนโฮสต์ทำหน้าที่นั้นแทน

ตรวจสอบว่าพอร์ตปิดจริง

ให้รันการทดสอบจากภายนอกซ้ำอีกครั้งหลังจากที่คุณได้ทำการเปลี่ยนแปลงแล้ว:

curl -6 -v http://[2001:db8:2a::1]:8080/
nmap -6 2001:db8:2a::1

พอร์ตที่เคยตอบรับก่อนหน้านี้ควรจะปฏิเสธการเชื่อมต่อหรือหมดเวลา (time out) และ nmap ควรรายงานสถานะว่าเป็น filtered หรือ closed หากพอร์ตยังคงเปิดอยู่ ให้ย้อนกลับไปตรวจสอบแหล่งที่มาทั้ง 4 จุดข้างต้น ได้แก่ บริการที่ยังคง bind อยู่กับ :: โดยไม่มีกฎไฟร์วอลล์กั้น, กฎของ Docker ที่แทรกอยู่หน้า UFW หรือไฟร์วอลล์ของผู้ให้บริการที่ไม่ได้ตรวจสอบ IPv6 เลย

การเก็บรักษาบริการที่มีความละเอียดอ่อนไม่ให้เข้าถึงได้จากอินเทอร์เน็ตสาธารณะเลยนั้นมีความปลอดภัยสูงกว่า การนำ SSH และแผงควบคุมผู้ดูแลระบบไปไว้หลัง WireGuard VPN และตั้งค่าไฟร์วอลล์ให้พอร์ตเหล่านั้นตอบรับเฉพาะการเชื่อมต่อผ่านอุโมงค์ (tunnel) จะช่วยให้ประเด็นเรื่องการเปิดเผย IPv6 ไม่ส่งผลกระทบต่อบริการเหล่านี้อีกต่อไป เพื่อชะลอการสแกนแบบ brute-force ที่พุ่งเป้าไปยังบริการที่ยังคงเป็นสาธารณะอยู่ ให้ติดตั้ง Fail2ban หน้า SSH เพิ่มเติมบนไฟร์วอลล์ที่มีนโยบาย default-deny

หากคุณยังไม่คุ้นเคยกับเรื่องพอร์ต พอร์ตคืออะไรและบริการต่างๆ ฟังการเชื่อมต่ออย่างไร คือบทความพื้นฐานที่คุณควรอ่านก่อนเป็นอันดับแรก

FAQ

UFW บล็อก IPv6 โดยค่าเริ่มต้นหรือไม่?

บน Ubuntu 24.04 เวอร์ชันปัจจุบัน คำตอบคือใช่ UFW จะอ่าน IPV6=yes จาก /etc/default/ufw และนำกฎแต่ละข้อไปใช้กับทั้ง IPv4 และ IPv6 โดย ufw status จะแสดงกฎ IPv6 พร้อมส่วนต่อท้าย (v6) ปัญหาจะเกิดขึ้นเมื่อ IPV6=no (จากอิมเมจเก่าหรือบทความสอนเก่า) เมื่อคุณพึ่งพาไฟร์วอลล์ของผู้ให้บริการที่กรองเฉพาะ IPv4 หรือเมื่อ Docker เปิดพอร์ตโดยข้าม UFW ไป ให้ตรวจสอบการตั้งค่าด้วย grep IPV6 /etc/default/ufw

ฉันจะตรวจสอบได้อย่างไรว่า VPS ของฉันเปิดพอร์ตอะไรบ้างบน IPv6?

รันคำสั่ง sudo ss -tlnp และสังเกตรายการที่ฟังอยู่ (listener) ทั้งหมดที่มี local address ขึ้นต้นด้วย [::] ซึ่งหมายความว่าบริการนั้นตอบสนองบนทุกอินเทอร์เฟซ IPv6 จากนั้นให้ทดสอบจากเครื่องอื่นโดยเชื่อมต่อไปยัง public IPv6 address ของเซิร์ฟเวอร์โดยตรงด้วย curl -6 -v http://[YOUR:IPV6::ADDR]:PORT/ หรือสแกนด้วย nmap -6 YOUR:IPV6::ADDR พอร์ตใดก็ตามที่เปิดอยู่ในการสแกน IPv6 แต่ปิดอยู่บน IPv4 คือช่องโหว่ของคุณ

ทำไมฉันถึงเข้าถึงพอร์ตของ Docker container ได้ ทั้งที่ UFW แจ้งว่าถูกบล็อก?

Docker จะแทรกกฎไฟร์วอลล์ของตนเองไว้หน้ากฎของ UFW เมื่อคุณเปิดพอร์ตด้วย -p ทำให้พอร์ตที่เปิดไว้สามารถเข้าถึงได้แม้ว่า ufw status จะแสดงสถานะว่าถูกปฏิเสธก็ตาม ปัญหานี้เกิดขึ้นบน IPv4 และบน IPv6 ด้วยหากเปิดใช้งานการรองรับ IPv6 ของ Docker ไว้ ให้เปลี่ยนไปเปิดพอร์ตที่ระบุ address เช่น -p 127.0.0.1:8080:80 หรือนำ container ไปไว้หลัง reverse proxy แล้วเปิดพอร์ตเฉพาะตัว proxy เท่านั้น

ฉันยังจำเป็นต้องมีไฟร์วอลล์ IPv6 หรือไม่ หากไฟร์วอลล์ IPv4 ของฉันแน่นหนาแล้ว?

จำเป็น IPv4 และ IPv6 เป็น network stack ที่แยกจากกันและมีกฎไฟร์วอลล์แยกกัน กฎ IPv4 ที่สมบูรณ์แบบไม่มีผลใดๆ ต่อ traffic ของ IPv6 หาก VPS ของคุณมี public IPv6 address (ซึ่งเกือบทุกเครื่องมี) บริการใดก็ตามที่ฟังอยู่บน :: จะยังคงเข้าถึงได้ผ่าน IPv6 จนกว่าจะมีกฎไฟร์วอลล์ IPv6 หรือการผูก (binding) เข้ากับ loopback มาหยุดไว้

ฉันจะตั้งค่าให้บริการฟังเฉพาะ IPv4 หรือเฉพาะ localhost ได้อย่างไร?

ให้กำหนด bind address ของบริการนั้นในไฟล์คอนฟิกของตัวบริการเอง หากต้องการให้ฟังเฉพาะ IPv4 loopback ให้ bind ไปที่ 127.0.0.1 หรือหากต้องการฟังทุก IPv4 address โดยไม่มี IPv6 listener ให้ใช้ 0.0.0.0 สำหรับ Postgres ให้ใช้ listen_addresses, SSH ใช้ ListenAddress และแอปพลิเคชันเซิร์ฟเวอร์ส่วนใหญ่จะมี flag สำหรับกำหนด host หรือ bind ให้ตรวจสอบผลลัพธ์ด้วย sudo ss -tlnp และตรวจสอบว่า Local Address ไม่แสดง [::] อีกต่อไป