5900
VNC 講的 RFB 協定的基準連接埠——也是為什麼檢視器死都連不上,而伺服器好端端地坐在 5901。
照你的系統挑一行貼上去執行。它會直接印出占著這個連接埠的行程,以及它綁在哪個位址上——通常看到這兩件事答案就出來了。
sudo lsof -nP -iTCP:5900 -sTCP:LISTEN`-n` 和 `-P` 讓位址和連接埠維持數字,NAME 那一欄才會讀成 `*:5900` 而不是 `*:rfb`;`-iTCP:5900` 選連接埠,`-sTCP:LISTEN` 只留監聽中的 socket。COMMAND 點出持有者,PID 是你要動手的號碼,NAME 是綁定位址——`*:5900` 代表每一張介面,對螢幕共享來說就是任何路由得到這台 Mac 的人都可以來試。這裡的 `sudo` 不是可有可無:螢幕共享的監聽者屬於系統帳號,所以不帶權限的 `lsof` 什麼都不會印,一個忙碌的連接埠看起來會像空的。把 `-sTCP:LISTEN` 拿掉還能看到已經建立的檢視器 session,那是你發現已經有人連進來的方法。
sudo ss -tlnp 'sport >= :5900 and sport <= :5910'掃一段範圍而不是單一連接埠,因為決定用到哪一個的是 display 號碼。`-t` 顯示 TCP socket,`-l` 只留監聽中的,`-n` 跳過服務名稱解析,`-p` 顯示所屬行程;`sport` 比對來源埠,而且接受 `<`、`<=`、`=`、`!=`、`>=`、`>`,所以一段範圍寫成一個運算式就好。5901 那一行是 display `:1`,5902 是 `:2`,依此類推。Local Address:Port 那一欄分得出來:`127.0.0.1:5901` 只綁回送位址,在等一條 SSH 通道;`0.0.0.0:5901` 則是整個網路都碰得到。持有者會出現成 `users:(("Xvnc",pid=2210,fd=8))`。沒有 `ss` 的話,`sudo netstat -tlnp | grep -E ':59[0-9][0-9]'` 給你同一幅畫面,行程那欄是斜線分隔的 pid/program;兩者都需要超級使用者權限才點得出不屬於你的行程。
netstat -ano | findstr ":59"同樣因為 display 號碼的關係,這裡比對的是前綴而不是完整連接埠。`-a` 顯示所有連線和監聽中的連接埠,`-n` 讓位址和連接埠維持數字,`-o` 在最後一欄補上所屬行程 ID。讀 Local Address 那一欄,看 `5900`、`5901` 這些到底是哪一個真的綁上去了,以及它是 `0.0.0.0` 還是 `127.0.0.1`,而且要 State 是 LISTENING 才能把那一行當成伺服器。接著用 `tasklist /FI "PID eq 3320"` 點出行程,它的 PID 過濾條件接受 eq、ne、gt、lt、ge、le;預期會看到 `tvnserver.exe` 或 `winvnc.exe` 這類東西。因為 `findstr` 比的是子字串,`:59` 這種寬鬆樣式也會撈到不相干的高位連接埠——動手之前先把完整號碼看清楚。
Get-NetTCPConnection -LocalPort 5900 | Select-Object LocalAddress,LocalPort,State,OwningProcess上面那種子字串搜尋的精確比對版本,而且每一個你懷疑的 display 號碼都值得跑一次。`State` 是 `Listen` 加上 `LocalAddress` 是 `127.0.0.1`,代表這台伺服器被刻意限制在本機,那是安全的設定,同時也是遠端檢視器被拒絕的原因。把 `OwningProcess` 丟進 `Get-Process -Id` 就能拿到執行檔。整段範圍每一個連接埠都是空結果,代表根本沒有 VNC 伺服器在跑,所以檢視器的拒絕訊息是準確的,不是什麼謎團。
`nmap -Pn -p 5900-5910 host.example.com` 才是值得打的版本,因為只掃單一連接埠只回答了一半的問題——伺服器可能因為 display `:1` 而在 5901。`-Pn` 跳過主機探索,所以不理 ping 的主機照樣掃得到。nmap 的狀態有精確定義:`open` 代表目標上有應用程式在聽;`closed` 代表主機回應了但那裡沒有人在聽;`filtered` 代表防火牆、過濾器或其他網路障礙擋住探測,所以 nmap 分不出是哪一種。對 VNC 來說,一個 `closed` 的 5900 旁邊接著一個 `open` 的 5901,一行就是完整的診斷。加 `--reason` 看是哪個封包決定了每一個狀態,加 `-sV` 讓 nmap 讀 RFB banner 並報出協定版本和實作。只掃你自己負責的主機。
| 你看到的訊息 | 代表什麼 |
|---|---|
The viewer reports connection refused on 5900 while the VNC server is definitely running | 幾乎一定是 display 的算式。RFC 6143 把第 N 台伺服器放在 5900+N,所以 `vncserver :1` 在 5901,而 5900 上根本什麼都沒有。檢視器拿到一個光禿禿的主機名稱就去撥 5900,被拒絕是正確的。依你的檢視器語法輸入 `host:1` 或 `host::5901`,並且用範圍掃描而不是單埠掃描來確認。 |
The viewer hangs and eventually times out instead of failing immediately | 什麼都沒回應,所以封包是被丟掉而不是被拒絕:主機防火牆、雲端安全群組,或你和那台機器之間的某條 ACL。被拒絕是瞬間的,被丟掉要等滿整個連線逾時。`nmap --reason` 會替它命名——被丟掉是 `filtered`,被拒絕是 `closed`——而伺服器本來就正確地綁在回送位址、你卻忘了開 SSH 通道時,看到的也正是這個結果。 |
The server will not start: the display or port is already in use, or bind fails with Address already in use | 5900+N 上的 `EADDRINUSE`。要嘛那個 display 之前的 `Xvnc` 還活著,要嘛是被砍掉的那一個留下了過時的鎖檔,讓 display 看起來被占用、其實連接埠是空的——光看錯誤訊息這兩種一模一樣,所以先用你系統對應的探測指令查連接埠。換一個 display 號碼啟動可以繞過去;砍掉對的那個行程才是修好它。 |
No matching security types | TCP 連線成功了,RFB 交握也走到了安全協商那一步,然後伺服器的清單和檢視器的清單沒有任何交集。典型情況是伺服器只提供 VNC Authentication,而檢視器被設定成一定要加密的廠商擴充;也可能是伺服器提供了某個這個檢視器從來沒實作過的私有類型。這是兩個程式之間的設定不合,不是網路故障——連接埠一根寒毛都不用動。 |
A long password is accepted, and so are its first eight characters | 這不是伺服器的 bug。VNC Authentication 產生 DES 金鑰的方式,是把密碼截成八個字元、不足的話右邊補 null 位元組,所以第八個字元之後的東西在兩端都會被丟掉。攻擊這個 session 的人永遠只需要搜尋八個字元,這正是 RFC 6143 自己說這套機制在密碼學上脆弱、不適合用在不受信任網路上的原因。 |
lsof or ss prints nothing, yet the port is clearly busy | 這是權限的情況:兩個工具在沒有 `sudo` 的時候都只顯示你自己擁有的行程,而由別的使用者、由 macOS 螢幕共享這類系統服務,或在容器裡啟動的 VNC 伺服器,不加權限就是看不到。斷定連接埠是空的之前,先用 `sudo` 再跑一次——而如果查出來持有者是 Docker 的行程,要停的是容器不是行程,因為監督者會直接再發布一次。 |
The session connects, but the screen is blank or shows only a grey background and an X cursor | 連接埠和認證都成功了,所以這完全不是網路問題。獨立的 `Xvnc` display 在有人替它啟動視窗管理員之前,是沒有任何桌面 session 掛上去的,而那件事本來該由伺服器的啟動腳本負責。去看 VNC 伺服器自己針對那個 display 的紀錄檔,不要看連接埠。 |
5900 是 Remote Framebuffer 協定的基準連接埠,標準化為 RFC 6143,在 IANA 登記為 `rfb`。這份 RFC 對那個絆倒所有人的算式講得很明白:「RFB 用戶端連到伺服器的 TCP 連接埠 5900。在有多個 RFB 伺服器的系統上,第 N 台伺服器通常監聽 5900+N,就像 X Window 伺服器監聽 6000+N 一樣。」所以一個啟動了 display `:1` 的 Unix `vncserver` 聽的是 5901 而不是 5900——而且 5901 根本不在 IANA 登記表裡,只有基準號碼有指派。在檢視器裡打一個光禿禿的主機名稱,它就會去撥 5900,而那台機器的 5900 上通常什麼都沒有,這就是為什麼「連線被拒」是 VNC 最常見的症狀,卻幾乎從來不是大家以為的那個意思。至於真正占著這個連接埠的是什麼,每個平台不一樣,而你去找的時候這件事很重要:在 macOS 上,內建的螢幕共享和 Apple Remote Desktop 直接用 5900,蘋果自己的連接埠清單也把它列成 `rfb` 並引用 RFC 6143;在 Linux 上是 TigerVNC 或 TightVNC 的 `Xvnc`,再不然就是掛在既有 display 上的 `x11vnc`;在 Windows 上則是 TightVNC 的 `tvnserver` 這類服務。它們講的是同一套交握,這也是為什麼某個專案的檢視器通常連得上另一個專案的伺服器——直到兩邊在安全類型上談不攏為止。
不要把 5900 對外開,也不要把 VNC 的密碼當成保護。RFC 6143 在核心協定裡只定義了三種安全類型——0 Invalid、1 None、2 VNC Authentication——而類型 1 的意思是完全不做認證,這正是現在還暴露在網際網路上的一大堆伺服器提供的東西。類型 2 也只是好一點點:伺服器送出一個隨機的 16 位元組挑戰,用戶端用密碼當金鑰以 DES 加密它,而「密碼會被截成八個字元,不足的話右邊補上 null 位元組」。不管你打了多長的密碼,整個金鑰空間就是八個字元。RFC 自己的安全性考量講得毫不客氣:這套機制「在密碼學上已知是脆弱的,不打算用在不受信任的網路上。許多實作會想使用更強的安全機制,例如把 session 跑在 IPsec 或 SSH 提供的加密通道上。」核心協定裡也沒有任何東西會加密 session,所以整個 framebuffer——以及你在上面打的每一個字——都是明文穿過網路的。把伺服器綁在回送位址上,透過通道連進去,例如 `ssh -L 5901:127.0.0.1:5901 user@host`,然後把檢視器指向 `127.0.0.1:5901`。廠商擴充確實加了 TLS 和更強的認證,但那是擴充:兩端必須實作同一個,而當它們沒有的時候,安全類型協商失敗要告訴你的就是這件事。
這些全都在你的瀏覽器裡跑,不會上傳任何東西。
查 5900 的時候,通常也會順手看一下這幾個。
幾乎都是權限問題。lsof 和 ss 會列出 socket,但別人的行程它不會顯示所有者,所以由系統帳號、容器執行環境或 launchd 啟動的服務,在你補上 sudo 之前就只是一個沒有主人的監聽項。Windows 則是反過來:netstat 什麼都沒有,綁定卻還是失敗,那代表這個號碼落在 Hyper-V、WSL2 或 Docker Desktop 開機時預留的範圍裡。
先看清楚它是什麼。沒收乾淨的開發伺服器砍掉沒事;正在寫入的資料庫不行,其他東西依賴的系統服務也不行。如果持有者是容器,請停容器,不要砍主機看到的那個行程——砍了執行環境只會把它拉起來。遇到真的不能動的,把自己的服務換一個連接埠反而更快。
因為它綁在 127.0.0.1 而不是 0.0.0.0。每一頁的指令之所以會把綁定位址跟行程名一起印出來,就是為了這件事:綁在回送位址上的服務,不管防火牆怎麼設,都只有這台機器自己連得到。現在不少框架是刻意這樣預設的,所以通常是一個參數的問題,不是 bug。如果已經是 0.0.0.0 還是不通,下一個要懷疑的就是主機防火牆或雲端的安全群組。
幾乎沒有幫助。掃全網的機器持續在掃整段範圍,換號碼買到的是幾個小時,不是安全,代價卻是每個同事都得記住它。它確實能讓針對預設埠的無差別掃描少污染一點日誌,這算是實際但有限的好處。真正會改變結果的是認證、限制來源位址的防火牆,以及一開始就不要讓它對外。
連接埠 5900 還是卡住嗎?看完整的連接埠對照表,或是回到上面,從你實際看到的那行錯誤往回追。