在伺服器上架設服務的時候,不見得會將服務的TCP連接埠直接公開在網際網路上給大家任意存取。所以在進行系統管理時,通常會透過SSH來登入伺服器,利用伺服器上的Shell來操作伺服器。但是有時候我們會希望直接在自己的電腦上使用工具或是撰寫程式來連接伺服器防火牆背後的服務,此時就可以使用SSH Tunnel來進行TCP封包的轉遞(Forwarding)。SSH Tunnel可以把要傳給該服務的封包,透過SSH客戶端傳給SSH伺服器,再由SSH伺服器轉遞給該服務,這個服務不一定要運行在SSH伺服器的本地端,只要是SSH伺服器能透過網路連接得到的服務也都可以。



透過SSH Tunnel,我們可以在不開放服務對外連線的情況下,從外部連線到該服務,而且這個連線是透過SSH協定來建立,十分安全。

SSH Tunnel

SSH Tunnel可以將SSH客戶端網路介面的任意TCP連接埠對應到SSH伺服器所能連接得到的TCP服務或是UDS(Unix Domain Socket)服務上,一樣是使用ssh指令來操作,只要在連線指令中加上一個或多個-L參數即可。

連接到TCP服務的-L參數格式如下:

-L [bind_address:]<port>:<host>:<host_port>

連接到UDS服務的-L參數格式如下:

-L [bind_address:]<port>:/path/to/socket_file

bind_address是SSH客戶端網路介面的IP位址,用來設定要使用哪個網路介面的<port>連接埠來對應到SSH伺服器的<host>:<host_port>或是/path/to/socket_file。這是可選的,如果不填的話,等同於*,也就是所有的網路介面。如果設定成所有的網路介面,就表示要監聽來自於這些網路介面的連線,若又沒有設定防火牆來阻擋,將會使得SSH伺服器的服務暴露在外。如果只想要讓SSH客戶端本身能夠連到SSH伺服器的TCP服務,bind_address要填寫為127.0.0.1

還有個小地方要提醒一下,那就是在沒有root權限的情況下,<port>只能設定為1024 ~ 65535範圍的值。

以下舉例說明:

ssh magiclen@192.168.56.101

以上指令,可以透過SSH連線登入到magiclen@192.168.56.101,並用Shell操作主機。這是SSH的基本用法。

ssh -L 127.0.0.1:27017:127.0.0.1:27017 magiclen@192.168.56.101

以上指令,可以透過SSH連線登入到magiclen@192.168.56.101,並用Shell操作主機,同時監聽本地端的27017連接埠。若本地端此時有程式與27017連接埠做連線,TCP封包會轉遞給magiclen@192.168.56.101上的127.0.0.1:27017。登出SSH,SSH Tunnel就會被關閉。

如果我們只是要建立SSH Tunnel,大可不必進入SSH伺服器的Shell。可以加上-N參數,來讓ssh指令不進入Shell。

ssh -N -L 127.0.0.1:27017:127.0.0.1:27017 magiclen@192.168.56.101

以上指令在成功登入SSH伺服器後,終端機就會阻塞(block)在那邊,此時的SSH Tunnel是開啟的。如果要關閉SSH Tunnel,只要回到執行這個指令的終端機,按下Ctrl + c來關閉指令的執行就好。

如果要讓SSH Tunnel的開啟是在背景運作的話,考量到我們在用完服務後會需要去關閉SSH Tunnel,因此必須有個方便關閉SSH Tunnel的機制才行。直接殺掉SSH的行程(process)也不是不行,但還要先把開啟SSH Tunnel的SSH行程查找出來,有點麻煩,在此提供一個比較漂亮的作法。

在使用ssh指令前,先在Shell中設定SOCKET_PATHSSH_SERVER變數。SOCKET_PATH的Socket檔案是等等ssh指令要拿來使用的,把路徑設在/tmp內即可,例如:/tmp/ssh-tunnelSSH_SERVER就是SSH伺服器,在此以magiclen@192.168.56.101為例。

然後執行以下指令:

rm -f "${SOCKET_PATH}" && ssh -fNM -S "${SOCKET_PATH}" -L 127.0.0.1:27017:127.0.0.1:27017 ${SSH_SERVER}

ssh-tunnel

ssh指令的-f參數可以讓SSH在成功登入後於背景執行;-M參數可以開啟SSH的Master Mode。

-S參數在Master Mode下,可以在指定的路徑建立一個Control Socket檔案,如此一來只要使用-S參數指定到一樣的Control Socket檔案的路徑就可以共用同一個SSH連線。也就是說,如果我們之後要關閉已經開啟的SSH Tunnel,只要執行以下的指令,將原本的SSH連線關閉即可:

ssh -S "${SOCKET_PATH}" -O exit ${SSH_SERVER}

ssh-tunnel

ssh指令的-O參數可以在使用-S參數的時候傳遞SSH連線的控制指令,exit表示要關閉這個SSH連線。