Linux 如何檢查連接埠是否開放?ss、nc、nmap
用 ss 確認 Linux 本機哪些服務正在監聽,再以 nc 或 nmap 從外部測試連接埠。了解封鎖時連線卡住、關閉時立即拒絕的差異。
先釐清問題,再在 Linux 上檢查連接埠是否開放
若要在 Linux 上檢查連接埠是否開放,請先判斷自己要確認的是哪個問題,因為站在不同位置時,「開放」代表的意思不同。在伺服器本機上,開放表示某個程序已繫結至該連接埠並等待連線。從另一台機器檢查時,開放表示封包能抵達該程序,且收到回應。若沒有收到回應,真正要確認的是哪個裝置丟棄了封包。sudo ss -ltnp 可回答第一個問題。nc -z 或 nmap 可回答第二個問題。防火牆計數器與 tcpdump 可回答第三個問題。
執行錯誤的檢查方式,往往會讓人耗費整個下午。在伺服器上執行的測試不會經過供應商的網路防火牆,因為該篩選器位於伺服器外部。如果你還不熟悉連接埠編號,Linux 上連接埠與 socket 的運作方式會說明本指南後續所採用的模型。
這台機器上有哪些服務正在監聽?讀懂 ss 輸出
ss 隨 iproute2 一起提供。Debian 將 iproute2 列為重要套件,因此標準 Debian 與 Ubuntu 安裝都包含它。精簡系統可能不會安裝它:Alpine 的基礎系統不包含 iproute2,而 apk add iproute2 會連同 ss 一起安裝。netstat 來自 net-tools。Ubuntu 多年來都不再預設安裝 net-tools,因此 netstat -tulpn 通常會回答 netstat: command not found。先了解 ss,就不會在需要時才發現指令不存在。
sudo ss -ltnp-l 只顯示監聽中的 socket。-t 將清單限制為 TCP。-n 會直接顯示數字,不解析名稱,因此指令會立即回傳。-p 顯示擁有該 socket 的程序,但需要 root 權限:沒有 sudo 時,凡是不屬於你的程序,其 Process 欄位都會留白。將 -t 替換為 -u 即可查看 UDP。
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 4096 127.0.0.53%lo:53 0.0.0.0:* users:(("systemd-resolve",pid=612,fd=14))
LISTEN 0 128 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=921,fd=3))
LISTEN 0 511 127.0.0.1:8080 0.0.0.0:* users:(("node",pid=1442,fd=19))
LISTEN 0 128 [::]:22 [::]:* users:(("sshd",pid=921,fd=4))Local Address 欄位決定所有結果,但人們往往會略過這一欄。
0.0.0.0:22表示這台機器上的所有 IPv4 位址,因此只要防火牆允許,外部就能連線。[::]:22對 IPv6 具有相同意義。127.0.0.1:8080表示僅限 loopback。使用預設 kernel 設定時,這台機器以外的任何位置都無法連線。10.20.0.5:5432表示僅限該介面位址,不包含其他位址,這在私人網路設定中很常見。- Process 欄位留白通常代表缺少
sudo,而不是沒有程序。
若要查詢單一連接埠,請在 ss 內進行篩選,不要對整份清單執行 grep:
sudo ss -ltnp 'sport = :8080'
sudo lsof -nP -iTCP:8080 -sTCP:LISTEN
sudo fuser 8080/tcp三個指令都沒有輸出,表示沒有任何程序佔用該連接埠。服務可能已停止、啟動失敗,或正在其他位置監聽。修改任何防火牆規則前,先查看 systemctl status <unit> 與 journalctl -u <unit> -n 50。
為什麼 Local Address 中的 127.0.0.1 會讓人耗上一個下午
使用預設核心設定時,繫結至 127.0.0.1 的 socket 無法從其他主機連線,任何一般的允許規則都無法改變這點。根據預設,核心只會將 127.0.0.0/8 路由至 loopback 介面;如果攜帶該目的位址的封包從實體網路卡進入,便會被視為 martian 並丟棄。因此,程序正常執行,ss 顯示正在監聽,ufw allow 8080 也回報成功,但從筆記型電腦發起的連線仍然失敗。連線會立即收到 Connection refused,因為封包已抵達你的公開位址,卻找不到繫結至該位址的 socket,核心因此回覆 TCP reset。例外是 route_localnet sysctl;此設定預設為關閉。啟用後,destination NAT 規則可以將外部流量傳送至 loopback socket,無須重新繫結。因此,如果某個 loopback 服務意外變得可連線,請檢查 sudo sysctl -a | grep route_localnet。
許多程式是刻意繫結至 loopback;對資料庫或管理介面而言,這通常是正確的預設設定。你有兩個直接的選擇。修改程式本身的設定,變更繫結位址(listen_addresses 位於 postgresql.conf、bind 位於 redis.conf,或使用應用程式接受的 host 引數),然後開放防火牆。或者讓服務維持在 loopback,改用其他方式連線,例如 nginx reverse proxy,或從筆記型電腦建立 SSH tunnel:
ssh -L 8080:127.0.0.1:8080 user@203.0.113.10Docker 的 publish flag 也有相同的區別。除非已在 daemon 或 network 設定中變更預設繫結位址,否則 -p 8080:8080 會在所有主機位址(0.0.0.0 和 [::])上發布,並使容器暴露於網際網路。-p 127.0.0.1:8080:8080 則會繫結至 loopback,維持本機可用。
如何從另一台機器檢查 Linux 上的埠是否開放
請從不同的網路執行這項測試。從伺服器本機測試,只能證明 loopback 路徑正常。即使從伺服器連線到自己的公開 IP,也會略過供應商的網路防火牆,因為該篩選器在 VPS 外部執行。
nc -zv -w 3 203.0.113.10 443-z 會建立連線後立即關閉,且不傳送資料。-w 3 會在 3 秒後放棄;這個旗標很重要:若未設定 timeout,被丟棄的封包會讓用戶端持續重試 SYN 超過 2 分鐘,直到 kernel 停止重試。成功時會看到以下結果:
Connection to 203.0.113.10 443 port [tcp/https] succeeded!如果缺少這項工具(nc: command not found),請在 Debian 或 Ubuntu 上安裝 netcat-openbsd;也可以使用 bash 內建的網路重新導向功能。只要系統已安裝 bash,就不需要額外套件:
timeout 3 bash -c '</dev/tcp/203.0.113.10/443' && echo open || echo "no answer"這種語法是 bash 的功能,因此請使用 bash 執行。Debian 和 Ubuntu 上的 /bin/sh 是 dash;它不支援 /dev/tcp,並會回報路徑不存在。Alpine 的預設 shell 是 BusyBox ash,bash 不包含在其基礎系統中,因此請先使用 apk add bash 安裝 bash。若要掃描一段埠號範圍,或希望工具直接標示狀態,請對由你負責的主機使用 nmap:
sudo nmap -Pn -p 22,80,443 203.0.113.10
sudo nmap -Pn -p 1-1024 203.0.113.10-Pn 會略過主機探索。多數 VPS 主機會丟棄 ICMP echo,因此若未使用 -Pn,nmap 會判定主機已離線,並略過掃描。nmap 會在某個服務回應並接受連線時輸出 open;若服務回應 reset,則輸出 closed;若完全沒有回應,則輸出 filtered。對 Web 服務而言,curl -sS -o /dev/null -w '%{http_code}\n' https://example.com 可區分網路故障與應用程式故障,因為狀態碼證明整條路徑都正常運作。
為何遭封鎖的連接埠會停滯,而關閉的連接埠會立即拒絕
立即拒絕。 封包已抵達機器,且有元件回應。
nc: connect to 203.0.113.10 port 443 (tcp) failed: Connection refused這行訊息可能由兩種不同原因造成。可能是沒有程序繫結至該位址和連接埠,因此 kernel 回應 TCP reset;也可能是防火牆規則以 reset 或 ICMP port unreachable 訊息拒絕封包。拒絕代表明確的回應,會在一次往返內返回。
先暫停,接著逾時。 某個元件丟棄了封包,沒有傳回任何回應。
nc: connect to 203.0.113.10 port 443 (tcp) timed out: Operation now in progressDROP 規則會產生這種結果,provider firewall 或 cloud security group 也會如此。沉默是封包遭丟棄的特徵,因為傳送端無法分辨封包遭丟棄和主機失效。
這個症狀能告訴你下一步該檢查哪裡。遭拒絕表示封包能正常通過網路,因此回到 ss -ltnp,檢查繫結位址和連接埠號碼。發生逾時表示封包遭到丟棄,因此應由外而內檢查防火牆。SSH 中的遭拒絕與發生逾時針對 port 22 詳細說明這兩種情況,多數人都是在這裡遇到這個問題。
ufw 刻意提供這兩種行為:ufw deny 8080 會丟棄封包,ufw reject 8080 會傳送拒絕回應。在 nftables 中,這兩個目標是 drop 和 reject;在 iptables 中則是 -j DROP 和 -j REJECT。預設政策幾乎一律是丟棄,因此缺少規則時會造成停滯,而不是顯示錯誤訊息。
誰在阻擋連接埠?從外向內排查
sudo ufw status verbose
sudo iptables -L INPUT -n -v --line-numbers
sudo nft list ruleset-v 計數器才是有用的部分。從外部執行 nc 測試,再次執行 iptables 命令,查看哪個計數器有變化:封包計數增加的規則,就是處理該網路流量的規則。這能以證據取代猜測。
關鍵測試要在伺服器上執行。當你從外部連線時,監看網路線上的封包:
sudo tcpdump -ni any tcp port 8080收到 SYN,卻沒有送回 SYN-ACK,表示封包已抵達 VPS,但遭主機丟棄。因此,供應商的防火牆正常,而本機規則有問題。完全沒有輸出,表示封包根本未抵達。原因可能是供應商防火牆、安全性群組或 IP 位址錯誤。只要分辨這兩種情況,大部分排查工作就能省下來。
有兩個網路層會產生看似不可能的結果。第一是 IPv6:如果主機名稱有 AAAA 記錄,客戶端可能透過 IPv6 連線,但你的規則只涵蓋 IPv4。因此,在採信任一結果前,先使用 nc -4 和 nc -6 分別測試兩種通訊協定。VPS 上的 ufw 規則與 IPv6 連接埠說明這種不一致的情況。第二是 Docker:即使 ufw status 將該連接埠列為拒絕,已發布的容器連接埠仍可從網際網路回應,因為這些封包會在 ufw 的 chain 看到它們之前先被處理。Docker 為何能直接繞過 ufw 發布連接埠說明其運作機制與修正方法;新 VPS 應設定的 ufw 規則則是建議先套用的基本規則集。
UDP 回應本來就具有歧義
UDP 沒有交握程序,因此探測沒有可判定成功的階段。nc -zu 203.0.113.10 53 封包送出後就會以 0 結束;這只能證明本機已送出封包,無法證明遠端的任何狀態。UDP 埠關閉時,主機通常會回覆 ICMP port unreachable 訊息,而 kernel 只會在下一次寫入時,將該錯誤回報給已連線的 socket,因此單次封包探測會漏掉這個回應。防火牆通常會丟棄 ICMP,連這項線索也會消失。因此,nmap 對大多數 UDP 埠顯示 open|filtered:沒有回應,正是開啟但不回應的服務與遭過濾的埠都會產生的結果。
請透過實際使用目標協定來測試 UDP。能回應的 DNS server 會將位址回傳給 dig +short @203.0.113.10 example.com。沒有收到回應時,即使使用 +short,目前版本的 dig 也會輸出 ;; communications error to 203.0.113.10#53: timed out 行。WireGuard peer 會在 sudo wg show 顯示最近的 latest handshake 行。接著在 server 端確認封包是否抵達:
sudo tcpdump -ni any udp port 51820client 傳送時若能看到封包,表示封包已抵達,因此問題出在服務或輸入鏈。完全看不到封包,表示封包根本沒有抵達。
依照最快找出故障原因的順序檢查
- 在伺服器上執行
sudo ss -ltnp 'sport = :8080'。沒有輸出表示沒有任何服務在監聽,因此先修復服務。 - 若有輸出,查看 Local Address 欄位。在預設核心設定下,
127.0.0.1表示外部無法存取,除非重新繫結服務,或在前方加上代理。 - 從另一個網路執行
nc -zv -w 3 <public ip> 8080。 - 若連線遭拒,回到第 1 步。位址、連接埠或主機可能不是你以為的那一個。
- 逾時表示封包遭到丟棄。在伺服器上啟動
sudo tcpdump -ni any tcp port 8080,然後重複測試。 - SYN 已抵達,但沒有封包返回:問題出在主機防火牆。找出在
sudo iptables -L INPUT -n -v中計數器有變動的規則。 - 沒有 SYN 抵達:問題出在供應商防火牆、安全性群組或錯誤的 IP 位址。
FAQ
如何檢查自己的 Linux 伺服器開放了哪些連接埠?
執行 sudo ss -ltnp 檢查 TCP,執行 sudo ss -lunp 檢查 UDP。每一行代表一個監聽中的 socket,而 Local Address 欄位會顯示哪些主機可以連線:0.0.0.0 和 [::] 會接受防火牆允許的任何來源,127.0.0.1 則依預設 kernel 設定只接受本機連線。Process 欄位需要 root 權限,因此請搭配 sudo 執行,否則該欄位會是空白。ss 是 iproute2 的一部分,標準 Debian 和 Ubuntu 安裝通常都包含;在 Alpine 上請執行 apk add iproute2。netstat 則屬於 net-tools,通常不會預先安裝。
為什麼 ss 顯示服務正在監聽,但我仍然無法連線?
常見原因有兩個,使用一個指令即可區分。如果 Local Address 是 127.0.0.1,表示服務繫結至 loopback,因此其他主機無法連線;這是因為 kernel 預設只會將該範圍路由至 loopback 介面。如果是 0.0.0.0,但連線仍然失敗,請在伺服器上執行 sudo tcpdump -ni any tcp port <port>,再從外部建立連線。若收到 SYN 卻沒有回應,表示本機防火牆規則正在丟棄封包。若完全沒有封包抵達,表示封包在到達 VPS 之前就被阻擋,通常是 provider firewall 或 security group 所造成。
被拒絕的連線與逾時的連線有何不同?
拒絕代表收到了回應。封包已抵達主機,並收到 TCP reset 或 ICMP port unreachable,這表示該位址和連接埠沒有服務正在監聽,或某項規則拒絕了連線。逾時則代表沒有回應:某項規則丟棄封包且未傳回任何訊息,因此用戶端會持續重試,直到放棄。連線被拒絕時,應檢查服務及其繫結位址。連線逾時時,應檢查防火牆,並先確認最靠近外部網路的防火牆。
如何檢查 UDP 連接埠是否開放?
無法透過一般探測可靠地判斷 UDP 連接埠是否開放,因為 UDP 沒有握手程序,無回應的服務與被丟棄的封包看起來相同。nc -zu 只要送出封包就會回傳成功;基於相同原因,nmap 會回報 open|filtered。請改以實際通訊協定進行測試:DNS 使用 dig +short @<host> example.com,WireGuard peer 則使用具有近期 handshake 的 sudo wg show。若要確認封包確實抵達,請在用戶端傳送封包時於伺服器上執行 sudo tcpdump -ni any udp port <port>。
我仍然可以使用 telnet host port 測試連接埠嗎?
TCP 可以使用此方式測試,而 Escape character is '^]' 表示連線已被接受。請使用 Ctrl+] 離開,然後執行 quit。有兩個原因使 nc -z 成為較佳工具:目前大多數伺服器映像檔都未安裝 telnet,而 nc 可搭配 -w 設定逾時,並設定可在 script 中檢查的結束狀態。如果兩者都無法使用,timeout 3 bash -c '</dev/tcp/<host>/<port>' 不需要額外套件;只要系統已安裝 bash 即可。