SSH 埠轉發 - 你不讓我看,我也能看

以終為始發表於2020-08-13

在之前 GRE 的文章中,我們知道隧道技術可以解決異種網路的通訊問題。在今天這篇文章中,將認識隧道技術的另一應用 - SSH 埠轉發。

首先我們對 SSH 並不陌生,是應該非常普遍的加密協議,用於在不安全的網路中提供安全的傳輸環境,常見的用途是用於遠端登入。而今天要介紹的埠轉發,原理類似。

埠轉發解決的問題:

  1. 增加安全性,加密 Client 到 Server 端的通訊資料
  2. 突破防火牆限制,完成一些被禁止的 TCP 連線

埠轉發的型別

在此之前,我們先了解一下埠轉發的概念,埠轉發是 SSH 的一種應用,可以理解成在本地 SSH Client 和遠端 SSH Server 建立了一條安全的隧道,而這條隧道傳輸的內容可以是執行的其他 TCP 埠的流量。在隧道傳輸這些流量時,會進行加密,常見的 Telnet, SMTP, LDAP, HTTP 等等使用 TCP 作為傳輸層的協議都可以進行埠轉發。

通常來說,埠轉發分為三種型別,本地埠轉發,遠端埠轉發,動態不定埠轉發。會依次從應用場景和如何實現來介紹。

本地埠轉發

在給客戶部署應用時,通過來說訪問的都是客戶的內網環境,一般需要我們掛上客戶的 VPN 然後通過提供的跳板機來訪問目標伺服器。但這樣的配置,就給在一些 debug 場景帶來很大的不便。

來看這樣一個場景:

這裡 Client A 需要依賴於 Server B 上一個 TCP 應用才能進行開發,但是由於防火牆的設定,Client A 的流量會被 Deny 掉。但提供了一臺 Server A,而 Server A 到 Server B 卻是可達的。這時我們就可以利用埠轉發技術,通過 Server A 間接地實現和 Server B 的通訊。這時 Server A 也就是常說的 Jump Server.

可以將 Client A 和 Server A 之間看成一條隧道,而隧道傳輸的內容就是 Server A 訪問 Server B 的流量。

下面來配置一下:

ssh -L <local port>:<remote host>:<remote port> <SSH hostname>

# local port 相當於 ssh client 埠
# remote host 是目標主機 Server B 的地址
# remote port 是目標主機 Server B 的埠
# ssh hostname 是跳板機,也就是 Server A 的地址

# 在 Client A 上
ssh -NfL 65001:10.124.207.152:8000 root@10.124.207.155

-f 表示後臺執行
-N 表示不輸入命令
-L 表示本地埠轉發

需要注意的內容:

  • Server A 到 Server B 一定是可達的
  • 另外在繫結埠時,要選擇沒有被使用的埠
  • 選用 1024-65535 之間的埠,之前的埠是給管理員使用的。

如何關閉:

like-unix: ps 找到程式 kill 掉

windows:
netstat -ano | findstr "65001"
taskkill /f /t /im  19036

遠端埠轉發

遠端埠轉發和本地埠轉發很像,只不過調換了 ssh server 和 ssh client 的位置。

考慮下面的場景,還是 Client A 無法訪問 Server B,但是 Server A 可以訪問 Server B, 與之前不同的是,Client A 也不能訪問 Server A 了,但 Server A 可以訪問 Client.

這時我們可以在 Server A 進行配置,把 Client A 作為 SSH Server 而自己作為 SSH Client 建立隧道。和之前本地一樣,在 Client A 上訪問配置的埠,就會把流量轉發給 SSH Client 也就是 SSH Server A,這時 Sever A 再將流量轉發至 Server B.

  $ ssh -R <ssh client port>:<destnation host>:<destnation port> <ssh client host>
  
  # Server B 進行配置
  ssh -NfR 65001:10.124.207.152:8000 root@10.124.207.154

這時我們就可以達到和本地埠轉發相同的效果,在 Client A 上訪問 Server B.

如果分不清本地還是遠端埠轉發,可以通過 SSH Client 和 SSH server 來在加上訪問應用的順序加以區分

比如這裡 Client A 想要訪問 Server B 的 HTTP。

那麼 Client A 上的瀏覽器作為 HTTP 的客戶端,Server B 作為 HTTP 的服務端。也就是在 HTTP 應用視角,是從 Client A 給 Server B 傳送請求。這個無論是本地還是遠端,方向都一樣。

接著再看 SSH 的客戶端和服務端。

如果 SSH 的客戶端在 Client A,則說明是本地埠轉發。和 HTTP client 和 server 同向。由於建立的轉發埠在本地,所以叫本地轉發。

否則如果是 SSH 的客戶端在 Server A,則說明是遠端埠轉發。和 HTTP client 和 server 反向。由於建立的轉發埠在遠端,所以叫遠端埠轉發。

其實無論是同向和反向,都是建立了一條隧道,只不過發起者不一樣,實現的目的都是一樣的。

本地和遠端埠轉發的共享

有時我們會希望共享 SSH Client 和 SSH Server 建立的隧道,提供給其他主機同時使用。這時我們可以通過 -g 引數來實現。

比如對本地埠轉發進行共享:

ssh -g -NfL 65001:10.124.207.152:8000 root@10.124.207.155

原來僅對 localhost 和 127.0.0.1 生效,現在對當前主機的所有地址都會生效,也就是 0.0.0.0 的範圍。在其他主機上可以通過訪問 SSH Client IP:65001 來進行服務的訪問。

遠端埠轉發同理:

 ssh -g -NfR 65001:10.124.207.152:8000 root@10.124.207.154

動態埠轉發

動態埠轉發的原理和本地及遠端沒有任何的區別,唯一的不同是,對於本地和遠端來說我們需要固定訪問應用的埠號,比如 80,443 等等。

但假設應用的埠不固定或者需要很多的埠號怎麼辦?這時就需要動態埠轉發。

ssh -D <local port> <SSH Server>
# local port - ssh client 開啟的代理轉發埠
# ssh server - ssh server 的ip及埠

ssh -g -Nf -D 65001 root@10.124.207.155

可以用 curl 進行測試:

curl  --socks5 127.0.0.1:6500  http://10.124.207.152:8000/api-token-auth/

總結

最後我們完成了本地,遠端,共享,動態的埠轉發配置。

從整體來看,其實實現的都是相同的功能。在 SSH Client 和 SSH Server 中建立隧道。只不過有時隧道連線的方向是不同的。

相關文章