คำตอบสั้น ๆ: เรียนรู้วิธีใช้ ss และ lsof ดูว่าโปรแกรมใดเปิดพอร์ตไหน รับการเชื่อมต่อจาก IP ใด และตรวจว่าพอร์ตเข้าถึงได้จากภายนอกจริงหรือไม่
คู่มือนี้อธิบาย 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 ทำให้ได้รายการยาวเกินจำเป็น