從零開始搭建簡易遠端伺服器(三)-- SSH埠轉發
SSH 埠轉發(port forwarding)是一種 將其他 TCP 埠的網路資料通過 SSH 連結來轉發 的技術, 也被稱作“隧道”(tunneling)。SSH 埠轉發能夠提供兩大有用的功能:
- 加密 SSH 客戶端至服務端之間的通訊連線(因為SSH連線自動提供了相應的加密和解密服務)
- 繞過防火牆的限制完成一些之前無法建立的 TCP 連線
之後將會看到,我們這個例子屬於其中的第二項功能,因為我們希望與遠端機器建立一個 TCP 連結無法實現的連線。有關 SSH 埠轉發的介紹,下文是一個非常好的材料(並且是中文的):
- ofollow,noindex" target="_blank">實戰SSH埠轉發
英文材料可以看:
本文從實際例子出發介紹埠轉發的應用。其中很多引用到實戰SSH埠轉發 的內容,非常推薦大家去讀讀這篇文章。
為什麼需要埠轉發?
讓我們先來回顧一下我們已經實現了什麼:我們利用 python flask 搭建起了一個簡單的伺服器,啟動之後能夠監聽本地主機(localhost)的某個埠然後提供服務,顯示我們準備好的網頁檔案。
然而我們注意到,一直以來我們只是在本地機器 上對某個埠進行訪問。那麼,這個埠是否能夠被遠端機器所訪問呢?換句話說,一個遠端的客戶端是否能夠通過連線這個TCP埠實現對伺服器的訪問呢?通常在以下兩種情況下, 答案是否定的:
- 伺服器上的配置限制了只有通過本地主機才能連線此伺服器。
- 由於防火牆的限制,無法用SSH直接從外部連線到此伺服器(比如說伺服器處於內網之中)。
這時候我們就需要用到埠轉發技術來實現對伺服器的遠端訪問了。
例項環境
假設我們是在一個“公家”機器上搭建的伺服器(比如說你工作所在單位配備的機器),該機器能夠被外部網路連線(SSH)。但是公司一般會對該機器的外部訪問許可權進行限制,即只允許外部網路通過 SSH 埠連線。比如說我們可以嘗試在遠端客戶端的瀏覽器視窗輸入伺服器的 IP 地址和相應的監聽埠,會發現伺服器並沒有反應,說明我們並沒有辦法從外部網路通過 TCP 埠來連線。
本地埠轉發
雖然“公家”機器對外部連線進行了限制,我們可以使用本地埠轉發 (local port forwarding)實現從遠端機器連線到伺服器。
假設伺服器監聽的埠是5000,IP地址是 ServerHost。那麼我們可以從遠端機器 (客戶端)輸入以下命令實現本地埠轉發:
ssh -L 7001:localhost:5000 ServerHost
建立起該 SSH 連線之後,我們只要在遠端機器(客戶端)上訪問本機的7001埠(localhost:7001),便實現連線到伺服器5000埠的效果。 That’s all!(很神奇有木有?)
用法解析
首先,該命令建立了一個從遠端機器到 ServerHost 的SSH連線,而-L 選項指定了我們需要建立一個本地埠轉發。
其中,7001是我們希望在遠端機器上配置的埠(一般選用一個1024-65535之間並尚未被使用的埠即可)。5000是伺服器在服務端監聽的埠。localhost 指的就是 ServerHost 的本地主機,因為我們限制了只允許(服務端)本機上部署的應用才能連線到此伺服器。因此,本地埠轉發的完整命令是這樣的:
ssh -L <local port>:<remote host>:<remote port> <SSH hostname>
在我們的例子中,local 指的就是遠端的客戶端機器,remote指的是服務端的伺服器機器,填入對應的內容就可以得到我們前面輸入的命令了。
我們來捋一下建立起本地埠轉發之後,資料是怎麼通過 SSH 埠流動的:
- 首先,我們在遠端機器(客戶端)上訪問本機的7001埠
- 此時本機會將這個從7001埠接收到的訪問資料加密並通過建立起的 SSH 通道轉發到服務端機器 ServerHost上
- 由於資料是通過 SSH 通道傳送的,所以資料實際傳送和接受的埠是25埠(一般SSH都用這個)
- 服務端機器接收到該資料之後,再將資料轉發到伺服器監聽的5000埠上
- 伺服器對資料請求進行迴應,然後按照原路返回要回應的資料以完成整個流程
簡而言之,本地埠轉發的作用是:將遠端伺服器監聽的埠對映到本機的某個埠上,以致訪問本機的該埠就可以實現訪問遠端伺服器埠的效果 (這裡遠端是對於發起請求的客戶端機器而言)。而整個資料連線過程實際上是通過 SSH 通道及其埠實現的(是不是有種暗度陳倉的感覺)。
進階版
利用本地埠轉發,我們實現了從遠端的客戶端機器訪問服務端的伺服器。可是這個這個埠轉發只能夠被該客戶端機器這一臺機器 使用。這是因為在主流 SSH 實現中,本地埠轉發繫結的是 lookback 介面,這意味著只有 localhost 或者 127.0.0.1 才能使用本機的埠轉發 , 其他機器發起的連線只會得到“connection refused.”。
那我們能不能實現讓其他遠端機器能夠共享 該客戶端機器的本地埠轉發,從而都能訪問到這個伺服器呢?答案是肯定的,SSH 提供了 GatewayPorts 的關鍵字來實現這個“共享”的功能,只需要加上-g 選項就行了:
ssh -g -L 7001:localhost:5000 ServerHost
假設客戶端機器的IP地址為 ClientHost,這樣其他機器就可以通過訪問 ClientHost:7001 來訪問伺服器了。
後記
利用 SSH 埠轉發,我們可以方便地實現對遠端伺服器進行 TCP 連線的效果。這裡我們還要注意一個問題:SSH 埠轉發是通過 SSH 連線建立起來的,我們必須保持這個 SSH 連線以使埠轉發保持生效。一旦關閉了此連線,相應的埠轉發也會隨之關閉。
另外,本文只介紹了埠轉發當中的一種 – 本地埠轉發。其實還有遠端埠轉發和動態埠轉發等有趣的技術。由於沒有在這個專案中實踐過,就不提及了。感興趣的朋友可以去看這篇介紹:實戰SSH埠轉發
最後,感謝白俄羅斯友人 Vasili 為我指出了這個使用埠轉發技術實現遠端訪問伺服器 TCP 連線的思路!