從外面連回家裡的內網服務

在家裡的電腦上跑了一個 web 服務,想從外面(或另一台裝置)連回去看。 這篇記錄先用 SSH tunnel 踩到的雷、為什麼會卡,以及最後為什麼改用 Tailscale。


一、SSH tunnel:ssh -L 到底在做什麼

最直覺的做法是 SSH 埠轉發:

ssh -L 8000:localhost:8000 使用者@遠端主機

這三個參數最常被誤解,拆開來看:

參數是誰的視角意思
8000(第一個)本機(你現在坐的這台)在本機監聽這個埠
localhost:8000(後兩個)SSH server連線送到對面之後,對面要往哪裡轉

換句話說:你在本機瀏覽器打 http://localhost:8000,流量走加密隧道到遠端主機,然後由遠端主機自己去連 localhost:8000

關鍵理解是後面那個 localhost 指的是遠端的 localhost,不是你的。


二、失敗症狀與診斷

實際跑起來,瀏覽器一直 ERR_EMPTY_RESPONSE

診斷過程中真正有價值的是這三條指令的對比

在哪裡跑指令結果
本機瀏覽器http://localhost:8000❌ ERR_EMPTY_RESPONSE
遠端主機curl http://localhost:8000/...❌ 連線立刻被斷,完全沒輸出
遠端主機curl http://<內層 IP>:8000/...✅ 200 OK

第三條是診斷關鍵:服務本身是好的、遠端主機也連得到它,唯獨遠端主機的 localhost:8000 沒有通。

問題不在 SSH,在最後一哩。

除錯順序:從內往外一段一段確認

網路鏈路有好幾段的時候,不要從頭猜。先確認最裡面那層服務活著(在那台機器上直接 curl),再往外一段一段驗,最後才懷疑 SSH。這樣每次都能把問題縮小到某一段,而不是在整條鏈路上亂試。


三、為什麼會卡:WSL2 的 localhost relay

如果你的服務是跑在 Windows 底下的 WSL2 裡,這裡有個隱藏機制。

WSL2 預設用 NAT networking,它其實是一台獨立的虛擬機、有自己的 IP。Windows 之所以能用 localhost:某個埠 連到 WSL2 裡的服務,靠的是一個叫 localhost relay 的背景機制——由 WSL service 偷偷把流量導進去。

這個 relay 有兩個很有事的特性:

  1. 它不一定總是在工作。版本、網路模式、服務啟動順序都可能讓它失效,而失效的時候沒有任何錯誤訊息,就只是連不上
  2. WSL2 的 IP 每次重開機都會變,所以「繞過 relay、直接打 IP」這個 workaround 撐不過下一次重開機
# 治標:手動指定 WSL2 當下的 IP
ssh -L 8000:172.x.x.x:8000 使用者@遠端主機
#            ^^^^^^^^^^ 重開機就要重查一次

能動,但每次重開機都要重來一遍。


四、正解:讓服務自己上 VPN

與其層層轉發,不如讓跑服務的那台機器自己有一個穩定位址。把 Tailscale(或其他 WireGuard 系的 mesh VPN)直接裝進跑服務的那一層——以上面的例子來說,就是裝進 WSL2 裡面,而不是只裝在 Windows 上。

前後對比:

之前: 本機 ─ssh -L─→ Windows ─localhost relay─→ WSL2 服務
                                      ↑ 這層會壞、IP 會變

之後: 本機 ─────Tailscale 加密────→ WSL2 服務

差別在哪:

維度SSH tunnel服務層直接上 VPN
連線前置動作每次都要先建 tunnel不用,直接開瀏覽器
中間層必須維持 SSH 連線開著不需要中轉
IP 變動重開機要重查、改指令hostname 永久穩定
訪問方式http://localhost:8000http://<主機名>:8000(Magic DNS)
多裝置每台裝置各建一條 tunnel手機、平板、筆電全部直接通
認證SSH 金鑰VPN 帳號(WireGuard 加密)

兩個沒預期到的好處

  1. 跟特定服務解耦。之後在同一台機器上再跑別的東西(Home Assistant、Jupyter、任何 dev server),全部自動可從所有裝置連到,不用每個服務各建一條隧道
  2. 「永遠連著」的體感。用起來就像那台機器跟你在同一個區網,不管你人在哪裡

代價

項目麻煩程度
裝 VPN client一行安裝指令,兩分鐘
跑 daemon需要 systemd。WSL2 預設沒開,在 /etc/wsl.conf[boot] systemd=true 即可
沒有 systemd 的環境改用 userspace 模式(tailscale up --tun=userspace-networking),自用完全夠

五、什麼時候還是用 SSH tunnel 就好

不是每個情境都值得裝 VPN:

  • 一次性的、臨時的:偶爾連一次,ssh -L 打完就關,不用為它裝東西
  • 對方機器你不能亂裝軟體:公司主機、別人的伺服器
  • 本來就只從一台裝置連:VPN 的多裝置優勢用不到

反過來說,只要你發現自己每次開機都在重建同一條 tunnel,那就是該換方案的訊號了。


相關