5、SSH隧道

FLy_鵬程萬里發表於2018-06-08

我們遇到的問題

  • 我想在家裡訪問我在公司的機器(寫程式,查資料,下電影)。
  • 公司為了防止我們用XX軟體封鎖了它的埠或者伺服器地址。
  • 公司不讓我們上XX網站,限制了網址甚至IP。
  • 公司不讓我們看關於XX的資訊,甚至花血本買了XX裝置,能夠對內容進行過濾。一看XX內容,連結就中斷了。
  • 我爸是搞電腦的,他在家裡的路由器上動了手腳,我不能看XXX了。

帶著這些問題我們開始SSH隧道的講解

什麼是SSH隧道

首先看下面這張圖,我們所面臨的大部分情況都和它類似。我們的電腦在右上角,通過公司帶有防火牆功能的路由器接入網際網路(當然可能還有交換機什麼的在中間連線著你和路由器,但是在我們的問題中交換機並不起到什麼關鍵性的作用)。右下腳的部分是一個網站的伺服器,它是我們公司防火牆策略的一部分,也就是說公司不希望我們訪問這個伺服器。在右上角還有一臺機器,它也是屬於我們的。但是這臺機器並不在我們公司裡面,換句話說他不受到公司防火牆的限制。最後也是最重要的一點是,我們能夠在公司通過網際網路直接訪問這臺機器。或者說這臺位於公司防火牆外面的機器需要擁有一個獨立的網際網路IP,同時公司的防火牆規則不會遮蔽這臺機器,並且這臺機器執行著一個OpenSSH伺服器。

現在,我們清楚地知道了自己所處的網路環境。並且不難理解我們在公司無法訪問那個伺服器的原因是:線路A-B-C上A-B之間的防火牆遮蔽了對那個伺服器的訪問。與此同時,我們也很快注意到,線路A-B-D之間、D-C之間是不受阻礙的。相信你已經想到了,在A-B之間的防火牆不會遮蔽對機器d的訪問。因此我們可以通過機器d建立一個通道A-B-D-C,從而訪問到機器c上的資料。

這條通道可以用很多技術來建立,這裡我們僅僅介紹如何使用SSH伺服器來建立這樣一個通道-他被稱為SSH隧道。

如何建立本地SSH隧道

在我們計劃建立一個本地SSH隧道之前,我們必須清楚下面這些資料:

  • 中間伺服器d的IP地址
  • 要訪問伺服器c的IP地址
  • 要訪問伺服器c的埠

現在,我們把上面這張圖變得具體一些,給這些機器加上IP地址。並且根據下面這張圖列出我們的計劃:



需要訪問234.234.234.234的FTP服務,也就是埠21

中間伺服器是123.123.123.123

現在我們使用下面這條命令來達成我們的目的

ssh -N -f -L 2121:234.234.234.234:21 123.123.123.123
ftp localhost:2121 # 現在訪問本地2121埠,就能連線234.234.234.234的21埠了

這裡我們用到了SSH客戶端的三個引數,下面我們一一做出解釋:

  • -N 告訴SSH客戶端,這個連線不需要執行任何命令。僅僅做埠轉發
  • -f 告訴SSH客戶端在後臺執行
  • -L 做本地對映埠,被冒號分割的三個部分含義分別是
        需要使用的本地埠號
        需要訪問的目標機器IP地址(IP: 234.234.234.234)
        需要訪問的目標機器埠(埠: 21)
  • 最後一個引數是我們用來建立隧道的中間機器的IP地址(IP: 123.123.123.123)

我們再重複一下-L引數的行為。-L X:Y:Z的含義是,將IP為Y的機器的Z埠通過中間伺服器對映到本地機器的X埠。

在這條命令成功執行之後,我們已經具有繞過公司防火牆的能力,並且成功訪問到了我們喜歡的一個FTP伺服器了。

如何建立遠端SSH隧道

通過建立本地SSH隧道,我們成功地繞過防火牆開始下載FTP上的資源了。那麼當我們在家裡的時候想要察看下載進度怎麼辦呢?大多數公司的網路是通過路由器接入網際網路的,公司內部的機器不會直接與網際網路連線,也就是不能通過網際網路直接訪問。通過線路D-B-A訪問公司裡的機器a便是不可能的。也許你已經注意到了,雖然D-B-A這個方向的連線不通,但是A-B-D這個方向的連線是沒有問題的。那麼,我們能否利用一條已經連線好的A-B-D方向的連線來完成D-B-A方向的訪問呢?答案是肯定的,這就是遠端SSH隧道的用途。

與本地SSH一樣,我們在建立遠端SSH隧道之前要清楚下面幾個引數:

  • 需要訪問內部機器的遠端機器的IP地址(這裡是123.123.123.123)
  • 需要讓遠端機器能訪問的內部機器的IP地址(這裡因為是想把本機對映出去,因此IP是127.0.0.1)
  • 需要讓遠端機器能訪問的內部機器的埠號(埠:22)

在清楚了上面的引數後,我們使用下面的命令來建立一個遠端SSH隧道

ssh -N -f -R 2222:127.0.0.1:22 123.123.123.123

現在,在IP是123.123.123.123的機器上我們用下面的命令就可以登陸公司的IP是192.168.0.100的機器了。

ssh -p 2222 localhost

引數說明:

-N,-f 這兩個引數我們已經在本地SSH隧道中介紹過了。我們現在重點說說引數-R。該引數的三個部分的含義分別是:

  • 遠端機器使用的埠(2222)
  • 需要對映的內部機器的IP地址(127.0.0.1)
  • 需要對映的內部機器的埠(22)

例如:-R X:Y:Z 就是把我們內部的Y機器的Z埠對映到遠端機器的X埠上。

建立SSH隧道的幾個技巧

自動重連

隧道可能因為某些原因斷開,例如:機器重啟,長時間沒有資料通訊而被路由器切斷等等。因此我們可以用程式控制隧道的重新連線,例如一個簡單的迴圈或者使用 djb’s daemontools . 不管用哪種方法,重連時都應避免因輸入密碼而卡死程式。關於如何安全的避免輸入密碼的方法,請參考我的 如何實現安全的免密碼ssh登入 。這裡請注意,如果通過其他程式控制隧道連線,應當避免將SSH客戶端放到後臺執行,也就是去掉-f引數。

保持產時間連結

有些路由器會把長時間沒有通訊的連線斷開。SSH客戶端的TCPKeepAlive選項可以避免這個問題的發生,預設情況下它是被開啟的。如果它被關閉了,可以在ssh的命令上加上-o TCPKeepAlive=yes來開啟。

另一種方法是,去掉-N引數,加入一個定期能產生輸出的命令。例如: top或者vmstat。下面給出一個這種方法的例子:

ssh -R 2222:localhost:22 123.123.123.123 "vmstat 30"

檢查隧道狀態

有些時候隧道會因為一些原因通訊不暢而卡死,例如:由於傳輸資料量太大,被路由器帶入stalled狀態。這種時候,往往SSH客戶端並不退出,而是卡死在那裡。一種應對方法是,使用SSH客戶端的ServerAliveInterval和ServerAliveCountMax選項。ServerAliveInterval會在隧道無通訊後的一段設定好的時間後傳送一個請求給伺服器要求伺服器響應。如果伺服器在ServerAliveCountMax次請求後都沒能響應,那麼SSH客戶端就自動斷開連線並退出,將控制權交給你的監控程式。這兩個選項的設定方法分別是在ssh時加入-o ServerAliveInterval=n和-o ServerAliveCountMax=m。其中n, m可以自行定義。

如何將埠繫結到外部地址上

使用上面的方法,對映的埠只能繫結在127.0.0.1這個介面上。也就是說,只能被本機自己訪問到。如何才能讓其他機器訪問這個埠呢?我們可以把這個對映的埠繫結在0.0.0.0的介面上,方法是加上引數-b 0.0.0.0。同時還需要開啟SSH伺服器端的一個選項-GatewayPorts。預設情況下它應當是被開啟的。如果被關閉的話,可以在/etc/sshd_config中修改GatewayPorts no為GatewayPorts yes來開啟它。

如何尋找中間伺服器

如果你家裡使用ADSL上網,多半你會比較幸運。一般的ADSL(例如 聯通 的ADSL)都是有網際網路地址的。你只需要在家裡的路由器上一臺裝有OpenSSH server機器的SSH埠對映出去即可。同時一些提供SSH訪問的虛擬主機也可以用於這一用途。例如: Hostmonser 或者 Dreamhost .

通過SSH隧道建立SOCKETS伺服器

如果我們需要藉助一臺中間伺服器訪問很多資源,一個個對映顯然不是高明的辦法(事實上,高明確實沒有用這個方法)。幸好,SSH客戶端為我們提供了通過SSH隧道建立SOCKS伺服器的功能。

通過下面的命令我們可以建立一個通過123.123.123.123的SOCKS伺服器。

ssh -N -f -D 1080 123.123.123             # 將埠繫結在127.0.0.1上
ssh -N -f -D 0.0.0.0:1080 123.123.123.123 # 將埠繫結在0.0.0.0上

通過SSH建立的SOCKS伺服器使用的是SOCKS5協議,在為應用程式設定SOCKS代理的時候要特別注意。

總結

至此,我們已經對如何利用SSH隧道有一個基本的認識了。現在,文章開始時的那些問題應該迎刃而解了吧。這裡要特別說一下,由於SSH隧道也使用了SSH加密協議,因此是不會被防火牆上的內容過濾器監控到的。也就是說一切在隧道中傳輸的資料都是被加密的。當然,離開隧道後的資料還是會保持自己原有的樣子,沒有加密的資料還是會被後續的路由裝置監控到。

參考文獻

OpenSSH網站

相關文章