SSH 端口轉發
簡介
SSH 除了登錄服務器,還有一大用途,就是作為加密通信的中介,充當兩臺服務器之間的通信加密跳板,使得原本不加密的通信變成加密通信。這個功能稱為端口轉發(port forwarding),又稱 SSH 隧道(tunnel)。
端口轉發有兩個主要作用:
(1)將不加密的數據放在 SSH 安全連接里面傳輸,使得原本不安全的網絡服務增加了安全性,比如通過端口轉發訪問 Telnet、FTP 等明文服務,數據傳輸就都會加密。
(2)作為數據通信的加密跳板,繞過網絡防火墻。
端口轉發有三種使用方法:動態轉發,本地轉發,遠程轉發。下面逐一介紹。
動態轉發
動態轉發指的是,本機與 SSH 服務器之間創建了一個加密連接,然后本機內部針對某個端口的通信,都通過這個加密連接轉發。它的一個使用場景就是,訪問所有外部網站,都通過 SSH 轉發。
動態轉發需要把本地端口綁定到 SSH 服務器。至于 SSH 服務器要去訪問哪一個網站,完全是動態的,取決于原始通信,所以叫做動態轉發。
$ ssh -D local-port tunnel-host -N
上面命令中,-D
表示動態轉發,local-port
是本地端口,tunnel-host
是 SSH 服務器,-N
表示這個 SSH 連接只進行端口轉發,不登錄遠程 Shell,不能執行遠程命令,只能充當隧道。
舉例來說,如果本地端口是2121
,那么動態轉發的命令就是下面這樣。
$ ssh -D 2121 tunnel-host -N
注意,這種轉發采用了 SOCKS5 協議。訪問外部網站時,需要把 HTTP 請求轉成 SOCKS5 協議,才能把本地端口的請求轉發出去。
下面是 SSH 隧道建立后的一個使用實例。
$ curl -x socks5://localhost:2121 http://www.example.com
上面命令中,curl 的-x
參數指定代理服務器,即通過 SOCKS5 協議的本地2121
端口,訪問http://www.example.com
。
如果經常使用動態轉發,可以將設置寫入 SSH 客戶端的用戶個人配置文件(~/.ssh/config
)。
DynamicForward tunnel-host:local-port
本地轉發
本地轉發(local forwarding)指的是,創建一個本地端口,將發往該端口的所有通信都通過 SSH 服務器,轉發到指定的遠程服務器的端口。這種情況下,SSH 服務器只是一個作為跳板的中介,用于連接本地計算機無法直接連接的遠程服務器。本地轉發是在本地計算機建立的轉發規則。
它的語法如下,其中會指定本地端口(local-port)、SSH 服務器(tunnel-host)、遠程服務器(target-host)和遠程端口(target-port)。
$ ssh -L -N -f local-port:target-host:target-port tunnel-host
上面命令中,有三個配置參數。
-L
:轉發本地端口。-N
:不發送任何命令,只用來建立連接。沒有這個參數,會在 SSH 服務器打開一個 Shell。-f
:將 SSH 連接放到后臺。沒有這個參數,暫時不用 SSH 連接時,終端會失去響應。
舉例來說,現在有一臺 SSH 服務器tunnel-host
,我們想要通過這臺機器,在本地2121
端口與目標網站www.example.com
的80端口之間建立 SSH 隧道,就可以寫成下面這樣。
$ ssh -L 2121:www.example.com:80 tunnel-host -N
然后,訪問本機的2121
端口,就是訪問www.example.com
的80端口。
$ curl http://localhost:2121
注意,本地端口轉發采用 HTTP 協議,不用轉成 SOCKS5 協議。
另一個例子是加密訪問郵件獲取協議 POP3。
$ ssh -L 1100:mail.example.com:110 mail.example.com
上面命令將本機的1100端口,綁定郵件服務器mail.example.com
的110端口(POP3 協議的默認端口)。端口轉發建立以后,POP3 郵件客戶端只需要訪問本機的1100端口,請求就會通過 SSH 服務器(這里是mail.example.com
),自動轉發到mail.example.com
的110端口。
上面這種情況有一個前提條件,就是mail.example.com
必須運行 SSH 服務器。否則,就必須通過另一臺 SSH 服務器中介,執行的命令要改成下面這樣。
$ ssh -L 1100:mail.example.com:110 other.example.com
上面命令中,本機的1100端口還是綁定mail.example.com
的110端口,但是由于mail.example.com
沒有運行 SSH 服務器,所以必須通過other.example.com
中介。本機的 POP3 請求通過1100端口,先發給other.example.com
的22端口(sshd 默認端口),再由后者轉給mail.example.com
,得到數據以后再原路返回。
注意,采用上面的中介方式,只有本機到other.example.com
的這一段是加密的,other.example.com
到mail.example.com
的這一段并不加密。
這個命令最好加上-N
參數,表示不在 SSH 跳板機執行遠程命令,讓 SSH 只充當隧道。另外還有一個-f
參數表示 SSH 連接在后臺運行。
如果經常使用本地轉發,可以將設置寫入 SSH 客戶端的用戶個人配置文件(~/.ssh/config
)。
Host test.example.com
LocalForward client-IP:client-port server-IP:server-port
遠程轉發
遠程轉發指的是在遠程 SSH 服務器建立的轉發規則。
它跟本地轉發正好反過來。建立本地計算機到遠程 SSH 服務器的隧道以后,本地轉發是通過本地計算機訪問遠程 SSH 服務器,而遠程轉發則是通過遠程 SSH 服務器訪問本地計算機。它的命令格式如下。
$ ssh -R remote-port:target-host:target-port -N remotehost
上面命令中,-R
參數表示遠程端口轉發,remote-port
是遠程 SSH 服務器的端口,target-host
和target-port
是目標服務器及其端口,remotehost
是遠程 SSH 服務器。
遠程轉發主要針對內網的情況。下面舉兩個例子。
第一個例子是內網某臺服務器localhost
在 80 端口開了一個服務,可以通過遠程轉發將這個 80 端口,映射到具有公網 IP 地址的my.public.server
服務器的 8080 端口,使得訪問my.public.server:8080
這個地址,就可以訪問到那臺內網服務器的 80 端口。
$ ssh -R 8080:localhost:80 -N my.public.server
上面命令是在內網localhost
服務器上執行,建立從localhost
到my.public.server
的 SSH 隧道。運行以后,用戶訪問my.public.server:8080
,就會自動映射到localhost:80
。
第二個例子是本地計算機local
在外網,SSH 跳板機和目標服務器my.private.server
都在內網,必須通過 SSH 跳板機才能訪問目標服務器。但是,本地計算機local
無法訪問內網之中的 SSH 跳板機,而 SSH 跳板機可以訪問本機計算機。
由于本機無法訪問內網 SSH 跳板機,就無法從外網發起 SSH 隧道,建立端口轉發。必須反過來,從 SSH 跳板機發起隧道,建立端口轉發,這時就形成了遠程端口轉發。跳板機執行下面的命令,綁定本地計算機local
的2121
端口,去訪問my.private.server:80
。
$ ssh -R 2121:my.private.server:80 -N local
上面命令是在 SSH 跳板機上執行的,建立跳板機到local
的隧道,并且這條隧道的出口映射到my.private.server:80
。
顯然,遠程轉發要求本地計算機local
也安裝了 SSH 服務器,這樣才能接受 SSH 跳板機的遠程登錄。
執行上面的命令以后,跳板機到local
的隧道已經建立了。然后,就可以從本地計算機訪問目標服務器了,即在本機執行下面的命令。
$ curl http://localhost:2121
本機執行上面的命令以后,就會輸出服務器my.private.server
的 80 端口返回的內容。
如果經常執行遠程端口轉發,可以將設置寫入 SSH 客戶端的用戶個人配置文件(~/.ssh/config
)。
Host remote-forwardHostName test.example.comRemoteForward remote-port target-host:target-port
完成上面的設置后,執行下面的命令就會建立遠程轉發。
$ ssh -N remote-forward# 等同于
$ ssh -R remote-port:target-host:target-port -N test.example.com
實例
下面看兩個端口轉發的實例。
簡易 VPN
VPN 用來在外網與內網之間建立一條加密通道。內網的服務器不能從外網直接訪問,必須通過一個跳板機,如果本機可以訪問跳板機,就可以使用 SSH 本地轉發,簡單實現一個 VPN。
$ ssh -L 2080:corp-server:80 -L 2443:corp-server:443 tunnel-host -N
上面命令通過 SSH 跳板機,將本機的2080
端口綁定內網服務器的80
端口,本機的2443
端口綁定內網服務器的443
端口。
兩級跳板
端口轉發可以有多級,比如新建兩個 SSH 隧道,第一個隧道轉發給第二個隧道,第二個隧道才能訪問目標服務器。
首先,在本機新建第一級隧道。
$ ssh -L 7999:localhost:2999 tunnel1-host
上面命令在本地7999
端口與tunnel1-host
之間建立一條隧道,隧道的出口是tunnel1-host
的localhost:2999
,也就是tunnel1-host
收到本機的請求以后,轉發給自己的2999
端口。
然后,在第一臺跳板機(tunnel1-host
)執行下面的命令,新建第二級隧道。
$ ssh -L 2999:target-host:7999 tunnel2-host -N
上面命令將第一臺跳板機tunnel1-host
的2999
端口,通過第二臺跳板機tunnel2-host
,連接到目標服務器target-host
的7999
端口。
最終效果就是,訪問本機的7999
端口,就會轉發到target-host
的7999
端口。
參考鏈接
- An Illustrated Guide to SSH Tunnels, Scott Wiersdorf
- An Excruciatingly Detailed Guide To SSH, Graham Helton