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