上一篇有提到 SSH 可以在使用者和客戶端之間建立一條安全的隧道 (tunnel) 來進行資料的傳輸,或者是存取到特定的服務。透過 SSH Tunnel 可以達到傳輸的加密或是網路和防火牆的穿透。
舉例來說,我有一台電腦 A 不能對外連線,有一台伺服器 B 可以對外連線,而電腦 A 是可以連線到伺服器 B 的,這時就可以在 A 和 B 之間建立 Tunnel 並將 A 要做的事情轉傳給 B,讓 B 幫忙連到外網完成。這樣的概念類似於 Port Forwarding
或是 Proxy
。
SSH Tunnel 可以分為以下幾種 :
- Local Tunnel (Local Forwarding)
- Remote Tunnel (Remote Forwarding)
- Dynamic Tunnel (Dynamic Forwarding)
Local Tunnel
Local Tunnel 會從 Client 端將特定服務的流量透過 SSH 傳送給 Server 端 (Remote),再透過 Server 端將流量導向到 特定
的 Host 上的 Port。
舉例來說,電腦 A 會先打開一個 port,伺服器 B 也會打開一個 port (也就是 SSH 預設的 port 22),電腦 A 會打通一條隧道到伺服器 B 的 port,並且在建立隧道時也會和伺服器 B 約定好當我傳送 Request 過去時,你要幫我把這個 Request 再傳給誰,這個誰有可能是伺服器 B 本身的 port 或是另一個伺服器 C 上的 port。
再說的生活化一點,當我的電腦因為受到防火牆的限制而不能連到 google 時且 SSH 連線並沒有被禁止,我就可以從我的電腦上送出一個 Request 到另一台可以連到 google 的伺服器,這台伺服器就可以幫我連到 google 再回傳結果給我。
下面我們來看一下建立 Local Tunnel 的指令,這個指令要在自己的電腦上執行 :
1 | $ ssh -NfL <local ip>:<local port>:<host ip>:<host port> <user>@<remote server ip> |
- -N : 不要另外開啟一個 Shell。
- -f : 在背景執行。
- -L : 代表要建立 Local Tunnel。
- local ip : 選填,通常是可以不填,就是綁定本機。
- local port : 本機的 port,盡量選大於 5000。
- host ip : 要連接的服務的 ip 或是 domain name,若是遠端 SSH Server 上的服務可以用
localhost
或127.0.0.1
。 - host port : 要連接的服務的 port。
範例
1. 連線到被防火牆擋下的外部服務
這個範例是本機要連到 facebook.com
,但是可能是公司網路的防火牆將 facebook.com
擋住了,這時候就可以就建立一個 Local Tunnel 來讓外部的一台 Server 幫忙連線。
在建立 tunnel 之前要先在 外部 Server
上打開對應的 port 打開,這裡我們用 6588
port。
1 | $ sudo firewall-cmd --add-port=6588/tcp --permanent |
接著要在 自己的電腦
上執行以下指令,這個例子是當透過本機的 6588
port 將 Request 傳送到 server.foo.com 這個 Server 時,Server 會把 Request 導向到 facebook.com
的 80
port,最後將結果回傳到本機的 6588
port。
1 | # 建立 Local Tunnel |
這時候打開瀏覽器輸入 http://localhost:6588
就可以連到 facebook 了。
2. 連線到被防火牆擋下的遠端伺服器上的服務
這個範例是要連到遠端 Server 上的服務,這個概念就比較簡單了。連到了遠端的 Server 上後,Server 會將 Request 導向到對應的 port,接著再回傳結果。
接著要在 自己的電腦
上執行以下指令,下面這個例子是當透過本機的 6588
port 將 Request 傳送到 server.foo.com 這個 Server 時,Server 會把 Request 導向到 7520
這個 port。這個 port 可能是對應到一個網頁或是一支 API 等等,接著再將結果回傳。
1 | # 建立 Local Tunnel |
這時候同樣打開瀏覽器輸入 http://localhost:6588
就可以連到這項服務了。
Remote Tunnel
Remote Tunnel 會從 Server 端 (Remote) 將特定服務的流量透過 SSH 傳送給 Client 端 (Local)。
舉例來說,我有一台電腦 A 放在家裡沒有固定開放的 IP,但我有一台 AWS 雲端伺服器 B 有固定開放的 IP。如果我想要從外面連回到家裡的電腦,在沒有開放的 IP 的情況下通常是不行的,因為你並沒有辦法明確的指出你要連到誰。
這時候 SSH Tunnel 就可以透過建立 反向隧道
的方式來突破這個限制以達到內網穿透。Remote Tunnel 的概念是,既然外網機器不知道內網機器的 IP,那就反過來由內網機器主動連線到外網機器,並且建立這條隧道,所以稱為 反向隧道
。
透過建立反向隧道,外網機器和內網機器就有一條連通的溝通管道了,這時就可以透過另一台電腦先連線到外網機器,再讓外網機器利用反向隧道將訊息傳送到內網的機器。
下面我們來看一下建立 Remote Tunnel 的指令,這個指令要在內網的機器上執行 :
1 | $ ssh -NfR <host ip>:<host port>:<local ip>:<local port> <user>@<remote server ip> |
- -N : 不要另外開啟一個 Shell。
- -f : 在背景執行。
- -R : 代表要建立 Remote Tunnel。
- host ip : 外網機器的 ip 或是 domain name,可以不填,因為 ssh 本身就需要指定連線對象的 ip,也就是指令中的
remote server ip
。 - host port : 外網機器要開放的 port,也就是當連到外網機器的這個 port,Request 就會透過這條反向隧道送到內網的機器。
- local ip : 可以用
localhost
或127.0.0.1
,就是綁定內網這台機器。 - local port : 內網這台機器的 port,對應到各項服務,SSH 預設值就是 22。
範例
同樣的在建立 tunnel 前要先在 外網機器
上打開對應的 port,這裡我們用 7020
port。作法請參考 Local Tunnel 的介紹。
接著要在 內網機器
上執行以下指令,這個範例是要建立一個 Remote Tunnel 在一台外網機器和一台內網機器之間,可以看到這裡指定的主機是 server.foo.com
,而外網機器開放了 7020
這個 port,所以當連線到 server.foo.com
的 7020
port 時,就會自動將 Request 導向到內網的機器。而這裡導向到的是內網機器的 22
port,也就是 SSH 的 port。
1 | $ ssh -NfR 7020:localhost:22 user@server.foo.com |
建立完成後在 外網機器
上執行以下指令,可以看到外網機器上的 7020
port 已經開始監聽了。
1 | $ sudo netstat -antp | grep 7020 |
如此一來我們就可以使用第三方的電腦,透過 server.foo.com
的 7020
port 來和內網的機器建立 SSH 的連線了。
1 | $ ssh user@server.foo.com -p 7020 |
Dynamic Tunnel
Dynamic Tunnel 會從 Client 端將特定服務的流量透過 SSH 傳送給 Server 端 (Remote),再透過 Server 端將流量導向到 非特定
的 Host 上的 Port。
從前面的定義可以發現 Dynamic Tunnel 和 Local Tunnel 流量導向的方向是一樣的,差別在於 Dynamic Tunnel 會導向到 非特定
的 Host 上的 Port,而 Local Tunnel 是導向到 特定
的 Host 上的 Port。
首先,什麼是 特定
的 Host 上的 Port ? Local Tunnel 會指定當流量通過這條隧道後最終要去的目的地,所以不管傳進來的 Request 是什麼,通通都會導向到一樣的目的地。
而 非特定
的意思是 Dynamic Tunnel 並不會指定流量通過隧道後的目的地,這條隧道的用意只是要讓 Request 可以流向到遠端的 Server 上,再讓遠端的 Server 作為一個代理伺服器 (SOCKS proxy) 將 Request 送出去,透過這樣的作法來突破防火牆的限制。
Dynamic Tunnel 的概念類似於 VPN,先連到另一台可以對外連線的機器,再讓這台機器幫我對外傳送和接收訊息。
下面我們來看一下建立 Dynamic Tunnel 的指令,這個指令要在本機上執行 :
1 | $ ssh -NfD <local ip>:<local port> <user>@<remote server ip> |
- -N : 不要另外開啟一個 Shell。
- -f : 在背景執行。
- -D : 代表要建立 Dynamic Tunnel。
- local ip : 可以用
localhost
或127.0.0.1
或是不填,就是綁定這台機器。 - local port : 本機上的 port,當本機的流量導向到這個 port 時就會通過這條隧道送到 Server。
範例
首先在本機上執行以下指令,這個指令代表當本機上有服務將流量導向到 8500
port 時,就會將這個流量送進隧道中導向到 server.foo.com
這台 Server 上。接著這台 Server 就會再將接收到的 Request 傳送出去,並將結果送回到來。
1 | $ ssh -NfD 8500 user@server.foo.com |
當 Dynamic Tunnel 建好後,我們就可以透過 SOCKS 代理伺服器來執行了。這裡以 Chrome 舉例。
- Linux/Windows 的用戶在 Chrome 的捷徑上按右鍵選擇內容,接著將以下設定加在目標的後面。
1 | --proxy-server="socks5://localhost:2323" |
- macOS 的用戶使用 terminal 輸入以下指令方式開啟 Chrome。
1 | open -a "Google Chrome" --args --proxy-server="socks5://localhost:2323" |
macOS 的用戶可以詳見參考 8 這篇文章,直接設定將所有流量導向到代理伺服器上。
autossh
SSH Tunnel 建立一段時間之後可能會突然斷線,這時就可以安裝 autossh
來取代 ssh
。autossh
會在斷線時自動重新進行連線,以確保連線正常。
而 autossh
的指令承襲了 ssh
,所以可以直接將指令中的 ssh
替換成 autossh
就好了。不過 autossh
還提供了一個參數 -M
來建立讓本機上的一個 port 來監聽連線狀況,如下 :
1 | $ autossh -M 6020 ... |
Summary
SSH 除了上一篇所介紹大家最常拿來連線到伺服器的操作外,Tunnel 才是他最強大的功能,在 Tunnel 中所有的訊息都是加密的。除非真的發生上一篇所介紹的 中間人攻擊
,不然基本上完全可以不用擔心。
不過上述的前提都是本機上要可以執行 ssh 並且連的出去,有些人想要在公司建 SSH Tunnel 出去來突破公司的防火牆,只要 SSH 的 Port 沒有被禁用就可以,如果被禁用了,那你還是乖乖的使用內網吧。
參考
[1] SSH Tunnel
[2] [教學] 透過 SSH Tunnel 將伺服器內部服務綁定到本機電腦上
[3] ssh-tunnel原理及其使用
[4] SSH Tunnel介紹
[5] The power of SSH tunnelling – Simple guide with drawings and examples
[6] Differences between ssh -L to -D
[7] What is the difference between Local/Remote/Dynamic SSH tunneling?
[8] 利用遠端 SSH 伺服器架設 SOCKS 代理伺服器(Proxy Server)保護網路傳輸內容
[9] 鑿一個反向 ssh 隧道, 對朋友或世界展示筆電或家裡的某個服務
[10] Multiple SSH Tunnels