除錯利器-SSH隧道

浩麟發表於2017-11-03

在開發微信公眾號或小程式的時候,由於微信平臺規則的限制,部分介面需要通過線上域名才能正常訪問。但我們一般都會在本地開發,因為這能快速的看到原始碼修改後的執行結果。但當涉及到需要呼叫微信介面時,由於不和你在同一個區域網中的使用者是無法訪問你的本地開發機的,就必須把修改後的程式碼重新發布到線上域名所在的伺服器才能去驗證結果。每次修改都重新發布很繁瑣也很浪費時間。

本文將教你如何通過 SSH 隧道把本地服務對映到外網,以方便除錯,通常把這種方法叫內網穿透。

閱讀完本文後,你能解決以下常見問題:

  • 開發微信公眾號等應用時把本地服務對映到外網,加速除錯流程;
  • 把你正在開發的本地服務分享給網際網路上其它人訪問體驗;
  • 在任何地方通過網際網路控制你家中在區域網裡的電腦;

最終目的

把執行在本地開發機上的 HTTP 服務對映到外網,讓全世界都能通過外網 IP 服務到你本地開發機上的 HTTP 服務。例如你本地的 HTTP 服務監聽在 127.0.0.1:8080,你有一臺公網 IP 為 12.34.56.78 的伺服器,通過本文介紹的方法,可以讓全世界的使用者通過 http://12.34.56.78:8080 訪問到你本地開發機上的 HTTP 服務。

總結成一句話就是:把內網埠對映到外網。

前提條件

為了把內網服務對映到外網,以下資源為必須的:

  1. 一臺有外網 IP 的伺服器;
  2. 能在本地開發機上通過 ssh 登入到外網伺服器。

要滿足以上條件很簡單:

  • 對於條件1:購買一臺低配 Linux 伺服器,推薦國外的 DigitalOcean
  • 對於條件2:對於 Mac、Linux 開發機是內建了 ssh 客戶端的,對於 Windows 可以安裝 Cygwin

實現原理

要實現把內網埠對映到外網,最簡單的方式就是通過 SSH 隧道。

SSH 隧道就像一根管道,能把任何2臺機器連線在一起,把傳送到其中一臺機器的資料通過管道傳輸到另一臺機器。假如已經通過 SSH 隧道把本地開發機和外網伺服器連線在了一起,外網伺服器端監聽在 12.34.56.78:8080,那麼所有發給 12.34.56.78:8080 的資料都會通過 SSH 隧道原封不動地傳輸給本地開發機的 127.0.0.1:8080,如圖所示:

SSH隧道

也就是說,去訪問 12.34.56.78:8080 就像是訪問本地開發機的 127.0.0.1:8080,本地開發機上的 8080 埠被對映到了外網伺服器上的 8080 埠。

如果你的外網伺服器 IP 配置了域名解析,例如 yourdomin.com 會通過 DNS 解析為 12.34.56.78,那麼也可以通過 yourdomin.com:8080 去訪問本地開發機上的服務。 這樣就做到了訪問外網地址時其實是本地服務返回的結果。

通過 SSH 隧道傳輸資料時,資料會被加密,就算中間被劫持,黑客也無法得到資料的原內容。 所以 SSH 隧道還有一個功能就是保證資料傳輸的安全性。

實現步驟

把本地開機和外網伺服器通過 SSH 隧道連線起來就和在本地開發機 SSH 登入遠端登入到外網伺服器一樣簡單。

先來回顧以下 SSH 遠端登入命令,假如想在本地遠端登入到 12.34.56.78,可以在本地開發機上執行以下命令:

ssh username@12.34.56.78
複製程式碼

而實現 SSH 隧道只需在本地開發機上執行:

ssh -R 8080:127.0.0.1:8080 username@12.34.56.78
複製程式碼

可以看出實現 SSH 隧道的命令相對於 SSH 登入多出來 -R 8080:127.0.0.1:8080,多出的這部分的含義是: 在遠端機器(12.34.56.78)上啟動 TCP 8080埠監聽著,再把遠端機器(12.34.56.78)上8080埠對映到本地的127.0.0.1:8080。 執行完以上命令後,就可以通過 12.34.56.78:8080 去訪問本地的 127.0.0.1:8080 了。

通常把這種技術叫做 SSH 遠端埠轉發(remote forwarding)。

其實不限於只能把本地開發機上執行的服務對映到外網伺服器上去,還可以把任何本地開發機可以訪問的服務對映到外網伺服器上去。例如在本地開發機上能訪問 github.com:80,在本地開發機上執行:

ssh -R 8080:github.com:80 username@12.34.56.78
複製程式碼

就能通過 12.34.56.78:8080 去訪問 github.com:80 了。

保持執行

在執行完上面介紹的 SSH 隧道命令後,你會發現登入到了外網伺服器上去了,如果你登出外網伺服器,就會發現 12.34.56.78:8080 無法訪問了。導致這個問題的原因是你登出外網伺服器時,在外網伺服器上本次操作對應的 SSH 程式也跟著退出了,而這個退出的程式曾負責監聽在 8080 埠進行轉發操作。

為了讓 SSH 隧道一直保持在後臺執行,有以下方法。

通過 SSH 自帶的引數

SSH 還支援這些引數:

  • N引數:表示只連線遠端主機,不開啟遠端shell;
  • T引數:表示不為這個連線分配TTY;
  • f引數:表示連線成功後,轉入後臺執行;

因此要讓 SSH 隧道一直保持在後臺執行,可以通過以下命令:

ssh -NTf -R 8080:127.0.0.1:8080 username@12.34.56.78
複製程式碼

通過 AutoSSH

SSH 隧道是不穩定的,在網路惡劣的情況下可能隨時斷開。如果斷開就需要手動去本地開發機再次向外網伺服器發起連線。 AutoSSH 能讓 SSH 隧道一直保持執行,他會啟動一個 SSH 程式,並監控該程式的健康狀況;當 SSH 程式崩潰或停止通訊時,AutoSSH 將重啟動 SSH 程式。

使用AutoSSH 只需在本地開發機上安裝 AutoSSH ,方法如下:

  • Mac 系統:brew install autossh
  • Linux 系統:apt-get install autossh

安裝成功後,在本地開發機上執行:

autossh -N -R 8080:127.0.0.1:8080 username@12.34.56.78
複製程式碼

就能完成和上面一樣的效果,但本方法能保持 SSH 隧道一直執行。 可以看出這行命令和上面的區別在於把 ssh 換成了 autossh,並且少了 -f 引數,原因是 autossh 預設會轉入後臺執行。

常見問題

如果你遇到通過以上方法成功啟動 SSH 隧道後,還是無法訪問 12.34.56.78:8080,那麼很有可能是外網伺服器上的 SSH 沒有配置對。為此你需要去外網伺服器上修改 /etc/ssh/sshd_config 檔案如下:

GatewayPorts yes
複製程式碼

這個選項的意思是,SSH 隧道監聽的服務的 IP 是對外開放的 0.0.0.0,而不是隻對本機的 127.0.0.1。不開 GatewayPorts 的後果是不能通過 12.34.56.78:8080 訪問,只能在外網伺服器上通過 127.0.0.1:8080 服務到本地開發機的服務。

修改好配置檔案後,你還需要重啟 sshd 服務來載入新的配置,命令如下:

service sshd restart
複製程式碼

如果使用以上方法還是無法訪問 12.34.56.78:8080,請檢查你外網伺服器的防火牆配置,確保 8080 埠是對外開放的。

其它代替方案

除了 SSH 隧道能實現內網穿透外,還有以下常用方法。

frp

frp 是一個可用於內網穿透的高效能的反向代理應用,支援 tcp, udp, http, https 協議。 frp 有以下特性:

  • frp 比 SSH 隧道功能更多,配置項更多;
  • frp 也需要一臺外網伺服器,並且需要在外網伺服器上安裝 frps,在本地開發機上安裝 frpc;

ngrok

ngrok 是一個商用的內網穿透工具,它有以下特點:

  • 不需要有外網伺服器,因為 ngrok 會為你提供;
  • 只需要在本地開發機安裝 ngrok 客戶端,和註冊 ngrok 賬戶;
  • 按照服務收費;

這些代替方案的缺點在於都需要再額外安裝其它工具,沒有 SSH 隧道來的直接。 想了解更多可以訪問它們的主頁。

閱讀原文

相關文章