SSH Tunneling
ssh端口轉(zhuǎn)發(fā),用來(lái)實(shí)現(xiàn)翻墻的操作。
它有幾種不同的稱(chēng)呼:ssh端口映射,端口轉(zhuǎn)發(fā),ssh隧道,英文(SSH port forward, SSH tunneling )實(shí)際上是指同樣的意思。
動(dòng)態(tài)轉(zhuǎn)發(fā)
動(dòng)態(tài)轉(zhuǎn)發(fā)指的是,本機(jī)與 SSH 服務(wù)器之間創(chuàng)建了一個(gè)加密連接,然后本機(jī)內(nèi)部針對(duì)某個(gè)端口的通信,都通過(guò)這個(gè)加密連接轉(zhuǎn)發(fā)。它的一個(gè)使用場(chǎng)景就是,訪問(wèn)所有外部網(wǎng)站,都通過(guò) SSH 轉(zhuǎn)發(fā)。
動(dòng)態(tài)轉(zhuǎn)發(fā)需要把本地端口綁定到 SSH 服務(wù)器。至于 SSH 服務(wù)器要去訪問(wèn)哪一個(gè)網(wǎng)站,完全是動(dòng)態(tài)的,取決于原始通信,所以叫做動(dòng)態(tài)轉(zhuǎn)發(fā)。
$ ssh -D local-port tunnel-host -N
上面命令中,-D表示動(dòng)態(tài)轉(zhuǎn)發(fā),local-port是本地端口,tunnel-host是 SSH 服務(wù)器,-N表示這個(gè) SSH 連接只進(jìn)行端口轉(zhuǎn)發(fā),不登錄遠(yuǎn)程 Shell,不能執(zhí)行遠(yuǎn)程命令,只能充當(dāng)隧道。
舉例來(lái)說(shuō),如果本地端口是2121,那么動(dòng)態(tài)轉(zhuǎn)發(fā)的命令就是下面這樣。
$ ssh -D 2121 tunnel-host -N
注意,這種轉(zhuǎn)發(fā)采用了 SOCKS5 協(xié)議。訪問(wèn)外部網(wǎng)站時(shí),需要把 HTTP 請(qǐng)求轉(zhuǎn)成 SOCKS5 協(xié)議,才能把本地端口的請(qǐng)求轉(zhuǎn)發(fā)出去。
下面是 SSH 隧道建立后的一個(gè)使用實(shí)例。
$ curl -x socks5://localhost:2121 http://www.example.com
上面命令中,curl 的-x參數(shù)指定代理服務(wù)器,即通過(guò) SOCKS5 協(xié)議的本地2121端口,訪問(wèn)http://www.example.com。
Test tunnel
curl --socks5-hostname "socks5://localhost:2121" ifconfig.me
如果經(jīng)常使用動(dòng)態(tài)轉(zhuǎn)發(fā),可以將設(shè)置寫(xiě)入 SSH 客戶端的用戶個(gè)人配置文件(~/.ssh/config)。
DynamicForward tunnel-host:local-port
建立本地ssh隧道
典型場(chǎng)景

我想要從自己的機(jī)器直接訪問(wèn)遠(yuǎn)程服務(wù)的8080端口,可以建立本機(jī)的隧道監(jiān)聽(tīng)端口,實(shí)現(xiàn)方法:
在MyPC操作(192.168.0.111)
前提:通過(guò)ssh 可以直接從MyPC連接Jump Server
ssh -N -f -L 1111:10.0.1.4:8080 redhat@40.74.67.218
檢查本地新出現(xiàn)的監(jiān)聽(tīng)端口
$ netstat -an | grep LISTEN | grep 1111
tcp4 0 0 127.0.0.1.1111 *.* LISTEN
tcp6 0 0 ::1.1111 *.* LISTEN
本機(jī)打開(kāi)瀏覽器,地址欄輸入 http://localhost:1111 ,把本地 1111端口通過(guò)一臺(tái)機(jī)器做跳板映射到了遠(yuǎn)程8080端口,可以直接訪問(wèn)。
連續(xù)跳轉(zhuǎn)
當(dāng)訪問(wèn)內(nèi)部服務(wù)器需要跳過(guò)兩個(gè)jump server,可以在jump機(jī)器上建立跳轉(zhuǎn),然后本機(jī)再做一次。

第一次在靠近自己的跳板機(jī)(Jump1)上做。
ssh -i mykey jump2 -N -f -L 8443:real-server:8443
第二次在本機(jī)做映射
ssh -N -f -L 1111:localhost:8443 jump1
于是,在本機(jī)就可以訪問(wèn)端口1111了。
建立遠(yuǎn)程ssh隧道
假設(shè)由于網(wǎng)絡(luò)或防火墻的原因我們不能用 SSH 直接從 本機(jī) 連接到 跳板 服務(wù)器(40.74.67.218),但是反向連接卻是被允許的。那此時(shí)我們的選擇自然就是遠(yuǎn)程端口轉(zhuǎn)發(fā)了。
(場(chǎng)景:MyPC -> ssh -> Jump 被禁止;MyPC <- ssh <- Jump 允許)
因?yàn)楸緳C(jī)使用內(nèi)網(wǎng)IP, 不能直接反向直連,我找了另外一臺(tái)服務(wù)器做例子,如下圖:

在 Jump 機(jī)操作(40.74.67.218)
ssh -N -f -R 2222:10.0.1.4:8080 redhat@40.83.72.19
在 Work 機(jī)驗(yàn)證(40.83.72.19)
$ netstat -an | grep 2222
tcp 0 0 127.0.0.1:2222 0.0.0.0:* LISTEN
tcp6 0 0 ::1:2222 :::* LISTEN
$ wget http://localhost:2222
本地轉(zhuǎn)發(fā)與遠(yuǎn)程轉(zhuǎn)發(fā)的對(duì)比
SSH 端口轉(zhuǎn)發(fā)自然需要 SSH 連接,而 SSH 連接是有方向的,從 SSH Client 到 SSH Server 。而我們的應(yīng)用也是有方向的,比如需要連接 Web Server 時(shí),Web Server 自然就是 Server 端,我們應(yīng)用連接的方向也是從應(yīng)用的 Client 端連接到應(yīng)用的 Server 端。如果這兩個(gè)連接的方向一致,那我們就說(shuō)它是本地轉(zhuǎn)發(fā)。而如果兩個(gè)方向不一致,我們就說(shuō)它是遠(yuǎn)程轉(zhuǎn)發(fā)。
本地轉(zhuǎn)發(fā)時(shí):
MyPC 同時(shí)是應(yīng)用的客戶端,也是 SSH Client,這兩個(gè)連接都從它指向 Jump所在網(wǎng)絡(luò)。
遠(yuǎn)程轉(zhuǎn)發(fā)時(shí):
Work 是應(yīng)用的客戶端,但卻是 SSH Server ;而 Jump 所在網(wǎng)絡(luò)是 Web 的服務(wù)端,但卻是 SSH Client 。這樣兩個(gè)連接的方向剛好相反。
另一種簡(jiǎn)單粗暴的區(qū)分:所有操作在同一臺(tái)機(jī)器完成,這就是本地轉(zhuǎn)發(fā);否則就是遠(yuǎn)程轉(zhuǎn)發(fā)。
過(guò)程中使用參數(shù)的解釋
-f Fork into background after authentication.
-N Do not execute a shell or command.
-L port:host:hostport
-R port:host:hostport
中文再解釋一遍,這里我們用到了SSH客戶端的三個(gè)參數(shù):
-N 告訴SSH客戶端,這個(gè)連接不需要執(zhí)行任何命令。僅僅做端口轉(zhuǎn)發(fā)
-f 告訴SSH客戶端在后臺(tái)運(yùn)行
-L 做本地映射端口,被冒號(hào)分割的三個(gè)部分含義分別是
需要使用的本地端口號(hào) (端口:1111)
需要訪問(wèn)的目標(biāo)機(jī)器IP地址(IP: 10.0.1.4)
需要訪問(wèn)的目標(biāo)機(jī)器端口(端口: 8080)
最后一個(gè)參數(shù)是我們用來(lái)建立隧道的中間機(jī)器的IP地址(IP: 40.74.67.218)
我們?cè)僦貜?fù)一下-L參數(shù)的行為。-L X:Y:Z的含義是,將IP為Y的機(jī)器的Z端口通過(guò)中間服務(wù)器映射到本地機(jī)器的X端口。
命令使用格式:
ssh -f -N -L listen_port:DST_Host:DST_port user@Tunnel_Host (本地轉(zhuǎn)發(fā))
ssh -f -N -R listen_port:DST_Host:DST_port user@Tunnel_Host (遠(yuǎn)程轉(zhuǎn)發(fā))
Windows SSH App 端口轉(zhuǎn)發(fā)的方法
以Xshell 為例
