0%

Linux (九) - SSH Tunnel 突破防火牆限制

Tunnel

上一篇有提到 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 上的服務可以用 localhost127.0.0.1
  • host port : 要連接的服務的 port。

範例

1. 連線到被防火牆擋下的外部服務

這個範例是本機要連到 facebook.com,但是可能是公司網路的防火牆將 facebook.com 擋住了,這時候就可以就建立一個 Local Tunnel 來讓外部的一台 Server 幫忙連線。

在建立 tunnel 之前要先在 外部 Server 上打開對應的 port 打開,這裡我們用 6588 port。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ sudo firewall-cmd --add-port=6588/tcp --permanent
success

$ sudo firewall-cmd --reload
success

$ sudo firewall-cmd --zone=public --list-all
public
target: default
icmp-block-inversion: no
interfaces:
sources:
services: dhcpv6-client ssh
ports: 6588/tcp
protocols:
masquerade: no
forward-ports:
source-ports:
icmp-blocks:
rich rules:

接著要在 自己的電腦 上執行以下指令,這個例子是當透過本機的 6588 port 將 Request 傳送到 server.foo.com 這個 Server 時,Server 會把 Request 導向到 facebook.com80 port,最後將結果回傳到本機的 6588 port。

1
2
# 建立 Local Tunnel
$ ssh -NfL 6588:facebook.com:80 user@server.foo.com

這時候打開瀏覽器輸入 http://localhost:6588 就可以連到 facebook 了。

2. 連線到被防火牆擋下的遠端伺服器上的服務

這個範例是要連到遠端 Server 上的服務,這個概念就比較簡單了。連到了遠端的 Server 上後,Server 會將 Request 導向到對應的 port,接著再回傳結果。

接著要在 自己的電腦 上執行以下指令,下面這個例子是當透過本機的 6588 port 將 Request 傳送到 server.foo.com 這個 Server 時,Server 會把 Request 導向到 7520 這個 port。這個 port 可能是對應到一個網頁或是一支 API 等等,接著再將結果回傳。

1
2
# 建立 Local Tunnel
$ ssh -NfL 6588:localhost:7520 user@server.foo.com

這時候同樣打開瀏覽器輸入 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 : 可以用 localhost127.0.0.1,就是綁定內網這台機器。
  • local port : 內網這台機器的 port,對應到各項服務,SSH 預設值就是 22。

範例

同樣的在建立 tunnel 前要先在 外網機器 上打開對應的 port,這裡我們用 7020 port。作法請參考 Local Tunnel 的介紹。

接著要在 內網機器 上執行以下指令,這個範例是要建立一個 Remote Tunnel 在一台外網機器和一台內網機器之間,可以看到這裡指定的主機是 server.foo.com,而外網機器開放了 7020 這個 port,所以當連線到 server.foo.com7020 port 時,就會自動將 Request 導向到內網的機器。而這裡導向到的是內網機器的 22 port,也就是 SSH 的 port。

1
$ ssh -NfR 7020:localhost:22 user@server.foo.com

建立完成後在 外網機器 上執行以下指令,可以看到外網機器上的 7020 port 已經開始監聽了。

1
2
3
$ sudo netstat -antp | grep 7020
tcp 0 0 0.0.0.0:7020 0.0.0.0:* LISTEN 19102/sshd: opc
tcp6 0 0 :::7020 :::*

如此一來我們就可以使用第三方的電腦,透過 server.foo.com7020 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 : 可以用 localhost127.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 來取代 sshautossh 會在斷線時自動重新進行連線,以確保連線正常。

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