EQVPS

วิธีตรวจสอบพอร์ตที่เปิดอยู่บน VPS: อะไรกำลังรอรับการเชื่อมต่อ อินเทอร์เน็ตเห็นอะไร และเรื่องไม่คาดคิดของ Docker

ดูทุกพอร์ตที่เซิร์ฟเวอร์รอรับการเชื่อมต่อ หาโปรเซสเบื้องหลัง สแกนเซิร์ฟเวอร์จากภายนอกแบบที่ผู้โจมตีทำ แล้วปิดสิ่งที่ไม่ต้องใช้ รวมถึงพอร์ตของ Docker ที่ยังเปิดอยู่แม้เปิดไฟร์วอลล์แล้ว ทุกตัวเลขวัดบนเซิร์ฟเวอร์ Ubuntu 24.04 ใหม่

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

1. อะไรกำลังรอรับการเชื่อมต่อ

คำสั่งคือ ss ตัวอักษรแต่ละตัวหมายถึง TCP, UDP, ซ็อกเก็ตที่รอรับ, หมายเลขพอร์ต และชื่อโปรเซส:

sudo ss -tulpn

บนเซิร์ฟเวอร์ใหม่จะแสดงหกบรรทัด:

Netid State  Local Address:Port  Process
udp   UNCONN    127.0.0.54:53    systemd-resolve
udp   UNCONN 127.0.0.53%lo:53    systemd-resolve
tcp   LISTEN    127.0.0.54:53    systemd-resolve
tcp   LISTEN 127.0.0.53%lo:53    systemd-resolve
tcp   LISTEN       0.0.0.0:22    sshd
tcp   LISTEN          [::]:22    sshd

คอลัมน์ที่สำคัญคือ Local Address ซึ่งบอกว่าใครเชื่อมต่อได้:

ที่อยู่ใครเข้าถึงได้
127.0.0.1:… หรือ 127.0.0.53เฉพาะโปรแกรมบนเซิร์ฟเวอร์นี้
0.0.0.0:…ทุกคนผ่าน IPv4 เว้นแต่ไฟร์วอลล์บล็อก
[::]:…ทุกคนผ่าน IPv6 (และมักรวม IPv4 ด้วย)
10.0.0.5:… (ที่อยู่เฉพาะ)ใครก็ตามที่เข้าถึงที่อยู่นั้นได้

ดังนั้นเซิร์ฟเวอร์ใหม่เปิดสู่ภายนอกเพียงอย่างเดียว คือ SSH ที่พอร์ต 22 ทั้ง IPv4 และ IPv6 ส่วน DNS resolver บน 127.0.0.53 เป็นส่วนตัว

หากต้องการแสดงเฉพาะสิ่งที่อินเทอร์เน็ตอาจมองเห็น ให้กรองที่อยู่ loopback ออก:

sudo ss -tlnp | awk 'NR>1 && $4 !~ /^(127\.|\[::1\])/ {print $4, $6}'

รันคำสั่งนี้ทุกครั้งหลังติดตั้งอะไรใหม่ เราติดตั้ง nginx, Docker และแอป Python เล็ก ๆ หนึ่งตัว รายการเพิ่มจาก 2 บรรทัดเป็น 10 บรรทัด รวมถึง docker-proxy บน 0.0.0.0:8081 และซ็อกเก็ตของ containerd บนพอร์ต localhost แบบสุ่ม

2. โปรเซสไหนใช้พอร์ตอยู่

เมื่อพอร์ตถูกใช้อยู่และอยากรู้ว่าใครใช้:

sudo lsof -i :80 -P -n
sudo fuser 80/tcp
sudo ss -tlnp 'sport = :80'

lsof อ่านง่ายที่สุด สำหรับ nginx มันแสดงโปรเซสหลักที่รันเป็น root และ worker ที่รันเป็น www-data ทั้งคู่อยู่บน *:80 สำหรับ IPv4 และ IPv6 ส่วน fuser แสดงเฉพาะ PID ซึ่งสะดวกในสคริปต์ หากต้องการปล่อยพอร์ต ให้หยุดบริการแทนการฆ่า PID ไม่เช่นนั้น systemd จะเริ่มมันใหม่: sudo systemctl stop nginx

3. อินเทอร์เน็ตเห็นอะไร

ss แสดงมุมมองของเซิร์ฟเวอร์ มุมมองของอินเทอร์เน็ตอาจต่างออกไป เพราะมีไฟร์วอลล์คั่นอยู่ หรือบริการผูกไว้กับ 127.0.0.1 หากต้องการเห็นภาพจริง ให้สแกนเซิร์ฟเวอร์จากเครื่องอื่น เช่นโน้ตบุ๊กหรือเซิร์ฟเวอร์เครื่องที่สอง เครื่องมือคือ nmap (apt install nmap หรือตัวติดตั้งจาก nmap.org บน Windows และ macOS)

nmap your-server-ip
nmap -p- your-server-ip
nmap -p 22,80,443,3306 your-server-ip

รูปแบบแรกตรวจ 1,000 พอร์ตที่ใช้บ่อยที่สุด รูปแบบที่สองตรวจทั้ง 65,535 พอร์ต รูปแบบที่สามตรวจตามรายการ บนเซิร์ฟเวอร์ใหม่ของเราก่อนมีไฟร์วอลล์:

Not shown: 999 closed tcp ports (reset)
PORT   STATE SERVICE
22/tcp open  ssh
Nmap done: 1 IP address (1 host up) scanned in 6.83 seconds

การสแกนเต็มรูปแบบทั้ง 65,535 พอร์ตใช้เวลา 8.9 วินาที และพบพอร์ตเดียวเดิม พอร์ตที่ปิดจะตอบกลับด้วย reset ทันที การสแกนจึงเร็ว

มีสามสถานะที่สำคัญ:

  • open: มีโปรแกรมรับการเชื่อมต่อ นี่คือพื้นผิวการโจมตีของคุณ
  • closed: เซิร์ฟเวอร์ตอบว่าไม่มีอะไรรอรับอยู่ตรงนั้น ปลอดภัย แต่ยืนยันว่าเซิร์ฟเวอร์มีอยู่ และบอกว่ามันไม่ได้รันอะไร
  • filtered: ไม่มีการตอบกลับ ไฟร์วอลล์ทิ้งแพ็กเก็ตไป ตัวสแกนไม่รู้ว่ามีบริการอยู่เบื้องหลังหรือไม่

สำหรับพอร์ตเดียวโดยไม่ต้องติดตั้งอะไร nc -zv ไอพีเซิร์ฟเวอร์ 443 จะตอบ succeeded หรือ refused และจะค้างจนหมดเวลาหากพอร์ตถูกกรอง สำหรับ UDP ให้เพิ่ม -sU ให้ nmap และเตรียมรอนาน บนเซิร์ฟเวอร์ของเรา พอร์ต UDP ที่พบบ่อยสามพอร์ต คือ 53, 123 และ 161 อยู่ในสถานะ closed ทั้งหมด

หมายเหตุสำหรับแพ็กเกจ NAT: จากอินเทอร์เน็ตจะเข้าถึงได้เฉพาะพอร์ต SSH ที่เราส่งต่อให้คุณ การสแกนที่อยู่ที่ใช้ร่วมกันจะเห็นขอบเครือข่ายของเรา ไม่ใช่เซิร์ฟเวอร์ของคุณ และพอร์ตอื่นเข้าถึงไม่ได้ไม่ว่าจะมีอะไรรอรับอยู่ บนแพ็กเกจที่มีที่อยู่ IPv4 ของตัวเอง ทุกพอร์ตที่เปิดอยู่คุณจัดการเอง

4. ปิดสิ่งที่ไม่ต้องใช้

สามวิธี จากดีที่สุดไปจนถึงกว้างที่สุด

ผูกกับ localhost ถ้ามีแต่โปรแกรมบนเซิร์ฟเวอร์ที่ใช้บริการนั้น ให้มันรอรับบน 127.0.0.1 เรารันเว็บแอปเล็ก ๆ ด้วย --bind 127.0.0.1 8080: ss แสดงว่าอยู่บน 127.0.0.1:8080 และ nmap จากภายนอกรายงาน 8080/tcp closed สำหรับฐานข้อมูลนี่คือค่าเริ่มต้นที่ถูกต้อง: Redis, MySQL และ PostgreSQL บน Ubuntu รอรับบน localhost ตั้งแต่ติดตั้ง และคู่มือ Redis กับ MySQL ของเราก็คงไว้เช่นนั้น

หยุดบริการ sudo systemctl disable --now ชื่อ จะเอาตัวรอรับออกถาวร ตรวจด้วย ss ว่ามันหายไปแล้ว

เปิดไฟร์วอลล์ อนุญาต SSH ก่อน แล้วอนุญาตสิ่งที่คุณให้บริการ จากนั้นจึงเปิดใช้งาน:

sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw enable
sudo ufw status verbose

ผลการสแกนจากภายนอกเปลี่ยนไปโดยสิ้นเชิง:

Not shown: 997 filtered tcp ports (no-response)
PORT     STATE SERVICE
22/tcp   open  ssh
80/tcp   open  http
8081/tcp open  blackice-icecap
Nmap done: 1 IP address (1 host up) scanned in 10.18 seconds

ทุกพอร์ตที่ไม่ได้อนุญาตเปลี่ยนจาก closed เป็น filtered และการสแกนเต็มรูปแบบทั้ง 65,535 พอร์ตตอนนี้ใช้ 110 วินาทีแทน 8.9 วินาที ตัวสแกนใช้เวลาต่อพอร์ตมากขึ้น และได้ข้อมูลเกี่ยวกับเซิร์ฟเวอร์ของคุณน้อยลง คู่มือ UFW ของเราอธิบายการจำกัดอัตรา การอนุญาตเฉพาะ IP เดียว และ IPv6

แต่ลองดูบรรทัดที่สาม

5. เรื่องไม่คาดคิดของ Docker

พอร์ต 8081 ยังเปิดอยู่ทั้งที่ไฟร์วอลล์เปิดและไม่มีกฎใดอนุญาต พอร์ตนี้เป็นของคอนเทนเนอร์ที่เรารันด้วย docker run -p 8081:80 nginx:alpine Docker เพิ่มกฎของตัวเองลงในไฟร์วอลล์ของเคอร์เนล และกฎเหล่านี้ถูกประเมินก่อนกฎของ ufw กฎที่มันเขียนลงบนเซิร์ฟเวอร์ของเราคือ:

-A DOCKER ! -i docker0 -p tcp --dport 8081 -j DNAT --to-destination 172.17.0.2:80

ทุกแพ็กเก็ตที่มายังพอร์ต 8081 จะถูกส่งต่อเข้าคอนเทนเนอร์ก่อนที่ ufw จะได้ตัดสิน ทุกคอนเทนเนอร์ที่มี -p หรือรายการ ports: ทำแบบนี้ และ ss ก็แสดงอย่างตรงไปตรงมาว่าเป็น docker-proxy บน 0.0.0.0:8081

วิธีแก้คือเผยแพร่บน localhost เท่านั้น:

docker rm -f web
docker run -d --name web -p 127.0.0.1:8081:80 nginx:alpine

ใน Compose ให้เขียน "127.0.0.1:8081:80" ใต้ ports: หลังเปลี่ยนแล้ว ss แสดง 127.0.0.1:8081 กฎของ Docker มี -d 127.0.0.1/32 เพิ่มเข้ามา และการสแกนจากภายนอกรายงาน 8081/tcp filtered วางnginx เป็น reverse proxy ไว้ด้านหน้าสำหรับฝั่งสาธารณะพร้อม HTTPS เพื่อให้พอร์ตที่เปิดบนเซิร์ฟเวอร์มีเพียง 22, 80 และ 443

เช็กลิสต์หลังการเปลี่ยนแปลงทุกครั้ง

  1. sudo ss -tulpn: ทุกบรรทัดที่มี 0.0.0.0 หรือ [::] เป็นสิ่งที่คุณตั้งใจเปิดหรือไม่?
  2. nmap ไอพีเซิร์ฟเวอร์ จากโน้ตบุ๊ก: พอร์ตที่เปิดตรงกันหรือไม่?
  3. ฐานข้อมูล แคช หรือแผงผู้ดูแลบนที่อยู่สาธารณะ: ย้ายไป 127.0.0.1 หรือไว้หลังไฟร์วอลล์ภายในวันนี้
  4. ทุกรายการ ports: ของ Docker: ถ้าไม่ใช่พอร์ตเว็บสาธารณะ ขึ้นต้นด้วย 127.0.0.1: หรือไม่?
  5. SSH เปิดให้ทุกคน: เพิ่ม fail2ban หรือล็อกอินด้วยคีย์เท่านั้น

บน Windows Server การตรวจแบบเดียวกันคือ Get-NetTCPConnection -State Listen และกฎอยู่ใน Windows Firewall ทุกแพ็กเกจ Linux ตั้งแต่ Nano ราคา $3 ขึ้นไป มีรุ่นที่มีที่อยู่ IPv4 ของตัวเอง และที่นั่นสิ่งที่คุณเห็นใน ss ก็คือสิ่งที่ทั้งโลกเห็น ยกเว้นสิ่งที่ไฟร์วอลล์ของคุณกั้นไว้

คำถามที่พบบ่อย

VPS ใหม่มีพอร์ตไหนเปิดอยู่บ้าง?

บนอิมเมจ Ubuntu 24.04 ของเรามีเพียงพอร์ตเดียว คือ SSH ที่พอร์ต 22 นอกจากนั้นมีแค่ DNS resolver ภายในบน 127.0.0.53 และ 127.0.0.54 ที่รอรับอยู่ ซึ่งอินเทอร์เน็ตเข้าไม่ถึง การสแกน TCP ครบทั้ง 65,535 พอร์ตจากอีกเครื่องพบพอร์ตเปิดเพียงพอร์ตเดียวพอดี

พอร์ต closed กับ filtered ต่างกันอย่างไร?

closed หมายถึงเซิร์ฟเวอร์ตอบกลับว่าไม่มีอะไรรอรับอยู่ตรงนั้น ตัวสแกนจึงรู้ทันที filtered หมายถึงไม่มีการตอบกลับเลย ซึ่งเป็นสิ่งที่ไฟร์วอลล์ทำเมื่อทิ้งแพ็กเก็ต ทั้งสองแบบกันผู้บุกรุกไว้ได้ แต่ filtered ยังซ่อนด้วยว่ามีบริการอะไรบ้าง และทำให้การสแกนเต็มรูปแบบบนเซิร์ฟเวอร์ของเราช้าลงราวสิบสองเท่า

ทำไม ss แสดงพอร์ตที่ nmap บอกว่า closed?

บริการนั้นรอรับเฉพาะบน 127.0.0.1 ซึ่งเป็นที่อยู่ loopback โปรแกรมบนเซิร์ฟเวอร์เองเชื่อมต่อได้ แต่จากภายนอกไม่มีใครเชื่อมต่อได้ ใน ss ให้ดูคอลัมน์ Local Address: 127.0.0.1:8080 เป็นส่วนตัว ส่วน 0.0.0.0:8080 หรือ [::]:8080 เข้าถึงได้จากอินเทอร์เน็ตหากไฟร์วอลล์อนุญาต

เปิด ufw แล้ว ทำไมพอร์ต Docker ยังเปิดอยู่?

Docker เขียนกฎไฟร์วอลล์ของตัวเอง และกฎเหล่านี้ถูกประเมินก่อนกฎของ ufw คอนเทนเนอร์ที่รันด้วย -p 8081:80 บนเซิร์ฟเวอร์ของเรายังเข้าถึงได้จากภายนอก แม้ ufw เปิดอยู่และไม่มีกฎสำหรับ 8081 ให้เผยแพร่บน localhost แทน คือ -p 127.0.0.1:8081:80 แล้ววาง nginx หรือ Caddy ไว้ด้านหน้าสำหรับฝั่งสาธารณะ

จะตรวจพอร์ตจากคอมพิวเตอร์ของตัวเองได้อย่างไร?

ตรวจพอร์ตเดียวด้วย nc -zv ไอพีเซิร์ฟเวอร์ 443 หรือติดตั้ง nmap แล้วรัน nmap ไอพีเซิร์ฟเวอร์ สำหรับพันพอร์ตที่ใช้บ่อยที่สุด บนแพ็กเกจ NAT จากอินเทอร์เน็ตจะมีเพียงพอร์ต SSH ที่ส่งต่อให้คุณเท่านั้นที่ตอบ ที่เหลือเข้าไม่ถึงโดยการออกแบบ

ความคิดเห็น

ยังไม่มีความคิดเห็น เป็นคนแรกสิ

แสดงความคิดเห็น

ความคิดเห็นจะถูกตรวจสอบก่อนแสดง