ลดความยุ่งยากในการใช้คลาวด์ พูดคุยกับเจ้าหน้าที่

Home ตรวจสอบพอร์ตที่เปิดอยู่บน Linux ด้วย ss และ lsof

ตรวจสอบพอร์ตที่เปิดอยู่บน Linux ด้วย ss และ lsof

เมื่อเว็บไซต์เปิดไม่ขึ้น Service เริ่มทำงานไม่ได้เพราะแจ้งว่า Address already in use หรือคุณอยากรู้ว่าเซิร์ฟเวอร์เปิดช่องทางอะไรให้คนภายนอกเข้ามาได้บ้าง คำถามแรกที่ต้องตอบคือ "ตอนนี้มีโปรแกรมไหนกำลังรอรับการเชื่อมต่อ (Listen) อยู่ที่พอร์ตไหน และรับจาก IP ใด" บน Linux รุ่นปัจจุบันมีเครื่องมือหลักสองตัวที่ตอบคำถามนี้ได้ คือ ss ซึ่งมากับระบบอยู่แล้ว และ lsof ซึ่งแสดงความสัมพันธ์ระหว่างโปรเซสกับไฟล์และ Socket ได้ละเอียดกว่า

บทความนี้อธิบายวิธีอ่านผลลัพธ์ของทั้งสองคำสั่ง วิธีหาว่าโปรแกรมใดจับพอร์ตไว้ วิธีนับการเชื่อมต่อที่เข้ามา และวิธีแยกให้ออกว่าพอร์ตที่ "เปิดอยู่ในเครื่อง" กับพอร์ตที่ "เข้าถึงได้จากภายนอก" เป็นคนละเรื่องกัน

สิ่งที่ต้องเตรียม

  • เข้าเซิร์ฟเวอร์ผ่าน SSH ด้วยผู้ใช้ที่ใช้ sudo ได้ (หากยังไม่มี ดู สร้างผู้ใช้ใหม่และให้สิทธิ์ sudo บน Linux)
  • Ubuntu 22.04/24.04 หรือ AlmaLinux/Rocky Linux 8-9
  • คำสั่ง ss อยู่ในแพ็กเกจ iproute2 (Ubuntu) หรือ iproute (AlmaLinux/Rocky) ซึ่งติดตั้งมาแล้วเกือบทุกกรณี ส่วน lsof อาจต้องติดตั้งเพิ่ม

ติดตั้ง lsof หากยังไม่มี:

# Ubuntu
sudo apt update && sudo apt install -y lsof

# AlmaLinux / Rocky Linux
sudo dnf install -y lsof

ขั้นตอนที่ 1: ดูพอร์ตที่กำลัง Listen ทั้งหมดด้วย ss

คำสั่งที่ใช้บ่อยที่สุดคือ:

sudo ss -tulpn

ความหมายของแต่ละตัวเลือก:

ตัวเลือกความหมาย
-tแสดง Socket แบบ TCP
-uแสดง Socket แบบ UDP
-lแสดงเฉพาะ Socket ที่อยู่ในสถานะ Listen (รอรับการเชื่อมต่อ)
-pแสดงชื่อโปรเซสและ PID ที่เป็นเจ้าของ Socket (ต้องใช้ sudo จึงจะเห็นโปรเซสของผู้ใช้อื่น)
-nแสดงตัวเลขพอร์ตและ IP แทนการแปลงเป็นชื่อ เช่น แสดง 443 แทน https

ตัวอย่างผลลัพธ์ (ตัดบางคอลัมน์ให้สั้นลง):

Netid State  Local Address:Port  Peer Address:Port Process
udp   UNCONN 127.0.0.53%lo:53    0.0.0.0:*         users:(("systemd-resolve",pid=612,fd=13))
tcp   LISTEN 0.0.0.0:22          0.0.0.0:*         users:(("sshd",pid=901,fd=3))
tcp   LISTEN 127.0.0.1:3306      0.0.0.0:*         users:(("mariadbd",pid=1204,fd=21))
tcp   LISTEN 0.0.0.0:80          0.0.0.0:*         users:(("nginx",pid=1320,fd=6))
tcp   LISTEN [::]:80             [::]:*            users:(("nginx",pid=1320,fd=7))
tcp   LISTEN *:6379              *:*               users:(("redis-server",pid=1411,fd=6))

ขั้นตอนที่ 2: อ่านคอลัมน์ Local Address ให้ถูก

คอลัมน์ที่สำคัญที่สุดคือ Local Address:Port เพราะบอกว่าโปรแกรมรับการเชื่อมต่อจากที่ใด:

ค่าที่เห็นความหมาย
127.0.0.1:3306 หรือ [::1]:3306รับเฉพาะจากภายในเครื่องเอง คนภายนอกเชื่อมต่อไม่ได้แม้ Firewall จะเปิด
0.0.0.0:80รับจากทุก IPv4 ของเครื่อง
[::]:80รับจากทุก IPv6 ของเครื่อง (และบางระบบรวม IPv4 ด้วย)
*:6379รับจากทุกที่อยู่ทั้ง IPv4 และ IPv6
203.0.113.10:8080รับเฉพาะที่ IP นี้ของเครื่อง

จากตัวอย่างข้างต้น MariaDB ที่ 127.0.0.1:3306 ปลอดภัยตามค่าเริ่มต้น แต่ Redis ที่ *:6379 เป็นสัญญาณอันตราย เพราะ Redis ไม่ควรเปิดรับจากภายนอก ควรแก้ให้ Bind ที่ 127.0.0.1 (ดู ติดตั้งและตั้งค่า Redis บน Linux Server)

หมายเหตุ: UDP ไม่มีสถานะ Listen แบบ TCP คำสั่ง ss จึงแสดง UDP ที่รอรับข้อมูลเป็นสถานะ UNCONN ซึ่งถือว่าเปิดรับอยู่เช่นกัน

ขั้นตอนที่ 3: หาว่าโปรแกรมไหนใช้พอร์ตที่ต้องการ

เมื่อ Nginx หรือ Apache เริ่มไม่ได้เพราะพอร์ต 80 ถูกใช้อยู่ ให้กรองดูเฉพาะพอร์ตนั้น:

sudo ss -ltnp 'sport = :80'

หรือใช้ lsof ซึ่งอ่านง่ายกว่าสำหรับคำถามนี้:

sudo lsof -i :80

ผลลัพธ์ตัวอย่าง:

COMMAND  PID     USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
apache2 1102     root    4u  IPv6  23981      0t0  TCP *:http (LISTEN)
apache2 1105 www-data    4u  IPv6  23981      0t0  TCP *:http (LISTEN)

แปลว่า Apache จับพอร์ต 80 อยู่ ถ้าคุณตั้งใจใช้ Nginx แทน ให้หยุดและปิดการเริ่มอัตโนมัติของ Apache ด้วย sudo systemctl disable --now apache2 (บน AlmaLinux/Rocky ชื่อ Service คือ httpd) รายละเอียดการจัดการ Service ดูได้ที่ จัดการ Service บน Linux ด้วย systemctl

ตัวเลือกของ lsof ที่ควรรู้:

# แสดงเฉพาะ TCP ที่ Listen ไม่แปลงชื่อพอร์ตและชื่อโฮสต์
sudo lsof -nP -iTCP -sTCP:LISTEN

# ดูว่าโปรเซส PID 1320 เปิด Socket อะไรอยู่บ้าง
sudo lsof -nP -a -p 1320 -i

# ดูทุก Socket ของผู้ใช้ www-data
sudo lsof -nP -a -u www-data -i

ตัวเลือก -a หมายถึงให้เงื่อนไขทั้งหมดเป็นจริงพร้อมกัน (AND) ถ้าไม่ใส่ lsof จะรวมผลแบบ OR ทำให้ได้รายการยาวเกินจำเป็น

ขั้นตอนที่ 4: ดูการเชื่อมต่อที่กำลังเกิดขึ้น

นอกจากพอร์ตที่รอรับ คุณอาจต้องดูว่ามีใครเชื่อมต่อเข้ามาอยู่บ้าง เช่น เมื่อเว็บช้าผิดปกติและสงสัยว่ามีการเชื่อมต่อจำนวนมาก

# การเชื่อมต่อ TCP ที่ Established ทั้งหมด
ss -tn state established

# นับการเชื่อมต่อที่เข้ามายังพอร์ต 443
ss -Htn state established '( sport = :443 )' | wc -l

# สรุปจำนวน Socket แยกตามสถานะ
ss -s

ตัวเลือก -H ตัดบรรทัดหัวตารางออกเพื่อให้ wc -l นับได้ตรง หากต้องการดูว่า IP ใดเชื่อมต่อเข้ามามากที่สุด ใช้:

ss -Htn state established '( sport = :443 )' \
  | awk '{print $4}' | sed 's/:[0-9]*$//' | sort | uniq -c | sort -rn | head

เมื่อใช้ตัวกรอง state คำสั่ง ss จะไม่พิมพ์คอลัมน์ State ออกมา คอลัมน์ที่ 4 จึงเป็น Peer Address ผลที่ได้คือจำนวนการเชื่อมต่อต่อ IP เรียงจากมากไปน้อย

ขั้นตอนที่ 5: ตรวจจากภายนอกว่าพอร์ตเข้าถึงได้จริงหรือไม่

พอร์ตที่ ss แสดงว่า Listen ที่ 0.0.0.0 อาจยังเข้าไม่ได้จากภายนอก เพราะมีชั้นกั้นอีกสองชั้น คือ Firewall ในเครื่อง (UFW, firewalld) และ Firewall ภายนอก ในทางกลับกัน ถ้า Firewall เปิดแต่โปรแกรม Listen ที่ 127.0.0.1 ก็ยังเข้าไม่ได้ ให้ทดสอบจากเครื่องอื่นที่ไม่ใช่เซิร์ฟเวอร์เอง:

# จากเครื่อง Linux หรือ macOS อื่น
nc -zv 203.0.113.10 443

# หรือใช้ nmap ตรวจหลายพอร์ต (สแกนเฉพาะเซิร์ฟเวอร์ของคุณเท่านั้น)
nmap -Pn -p 22,80,443,3306 203.0.113.10

ผลที่เป็นไปได้:

  • succeeded หรือ open: เข้าถึงได้
  • Connection refused หรือ closed: แพ็กเก็ตไปถึงเครื่อง แต่ไม่มีโปรแกรม Listen ที่พอร์ตนั้นบน IP ภายนอก
  • timed out หรือ filtered: มี Firewall ทิ้งแพ็กเก็ตระหว่างทาง

หากเป็นกรณี filtered ให้ตรวจ Firewall ในเครื่องตาม ตั้งค่า Firewall ด้วย UFW บน Ubuntu หรือ ตั้งค่า Firewall ด้วย firewalld บน AlmaLinux และ Rocky Linux และหากเซิร์ฟเวอร์มี Security Group หรือ Firewall ภายนอกจากฝั่งผู้ให้บริการ ให้เปิดพอร์ตนั้นเพิ่มด้วย

ตรวจสอบผลลัพธ์

หลังจากแก้ไขค่าของโปรแกรมใดก็ตาม (เช่น เปลี่ยน Bind ของ Redis หรือ MySQL) และรีสตาร์ต Service แล้ว ให้รัน sudo ss -tulpn อีกครั้ง แล้วเทียบกับรายการที่ควรเป็น ตัวอย่างเว็บเซิร์ฟเวอร์ทั่วไปควรเห็นเพียง:

  • SSH (22 หรือพอร์ตที่คุณเปลี่ยนไว้) ที่ 0.0.0.0 และ [::]
  • HTTP 80 และ HTTPS 443 ที่ 0.0.0.0 และ [::]
  • ฐานข้อมูล, Redis, PHP-FPM (ถ้าใช้ TCP) ที่ 127.0.0.1 เท่านั้น

พอร์ตอื่นที่ไม่รู้จักให้ตรวจด้วย lsof ว่าเป็นของโปรแกรมใด แล้วพิจารณาปิด Service ที่ไม่ได้ใช้

ปัญหาที่พบบ่อย

คอลัมน์ Process ว่างเปล่า

เกิดจากรัน ss -p หรือ lsof โดยไม่ใช้ sudo ผู้ใช้ทั่วไปจะเห็นเฉพาะโปรเซสของตัวเอง ให้รันซ้ำด้วย sudo

ปิด Service แล้ว แต่พอร์ตยังถูกใช้อยู่

บน Ubuntu 22.10 ขึ้นไป (รวม 24.04) SSH อาจถูกเปิดผ่าน Socket Activation โดย ssh.socket ของ systemd ซึ่งจะเห็นในคอลัมน์ Process เป็น systemd แทน sshd การหยุด ssh.service อย่างเดียวจึงไม่ปิดพอร์ต ในกรณีทั่วไปให้ใช้ systemctl list-sockets ดูว่ามี Socket Unit ใดจับพอร์ตอยู่

Service เริ่มไม่ได้ แต่ ss ไม่เห็นใครใช้พอร์ตนั้น

ให้อ่านข้อความ Error ให้ละเอียด ถ้าเป็น Cannot assign requested address แปลว่าโปรแกรมพยายาม Bind IP ที่ไม่มีอยู่บนเครื่อง (ตรวจ IP ของเครื่องด้วย ip -br addr) ถ้าเป็น Permission denied กับพอร์ตต่ำกว่า 1024 แปลว่าโปรแกรมไม่ได้รันด้วยสิทธิ์ที่ Bind พอร์ตนั้นได้ ส่วน Nginx อาจแจ้ง a duplicate default server เมื่อมีสองไฟล์ประกาศ listen 80 default_server ซ้ำกัน ให้ตรวจด้วย sudo nginx -t และอ่าน Log ของ Service ด้วย journalctl -u ชื่อservice (ดู อ่าน Log ระบบบน Linux ด้วย journalctl)

ภายในเครื่องเชื่อมต่อได้ แต่ภายนอกไม่ได้

ไล่ตรวจตามลำดับ: โปรแกรม Listen ที่ 0.0.0.0 หรือไม่, Firewall ในเครื่องเปิดพอร์ตหรือยัง, Firewall ภายนอกเปิดหรือยัง และบน AlmaLinux/Rocky ถ้าเป็นพอร์ตที่ไม่ใช่ค่ามาตรฐาน SELinux อาจห้ามโปรแกรม Bind พอร์ตนั้น (ดู ใช้งาน SELinux บน AlmaLinux และ Rocky Linux โดยไม่ต้องปิด)

หากตรวจแล้วยังไม่พบสาเหตุ หรือไม่แน่ใจว่าพอร์ตใดควรเปิด ติดต่อทีมซัพพอร์ต THAI DATA CLOUD ได้ที่ https://thaidata.cloud/contact/

ผู้ให้บริการคลาวด์ไทย
เพื่อธุรกิจของคนไทย

"มุ่งมั่น" และ "มั่นคง"
พร้อมรับมือทุกการเติบโต
Trust Cloud
คลาว์ที่ปลอดภัย
คือรากฐานที่มั่นคง
cloud security