SSH 端口轉發功能可以將其餘 TCP 端口的網絡數據經過 SSH 連接來轉發,而且自動提供了相應的加密及解密服務。其實這一技術就是咱們經常據說的隧道(tunnel)技術,緣由是 SSH 爲其餘 TCP 連接提供了一個安全的通道來進行傳輸。
咱們知道,FTP 協議是以明文來傳遞數據的。可是咱們可讓 FTP 客戶端和服務器經過 SSH 隧道傳輸數據,從而實現安全的 FTP 數據傳輸。
更常見的狀況是咱們的應用常常被各類防火牆限制。常見的有禁止訪問某些網站、禁用某類軟件,同時你的全部網絡行爲都被監控並分析!一樣的經過 SSH 隧道技術咱們徹底能夠規避這些限制。web
如上圖所示,經過 SSH 的端口轉發, 應用程序的客戶端和應用程序的服務器端再也不直接通信,而是轉發到了 SSH 客戶端及 SSH 服務端來通信。這樣就能夠同時實現兩個目的:數據的加密傳輸和穿透防火牆!
在具體的使用場景中,端口轉發又被細分爲本地端口轉發、遠程端口轉發、動態端口轉發等。本文將詳細的介紹其技術原理及使用方法。瀏覽器
假設咱們有一臺主機 B,上面運行着 smtp 服務器,監聽的端口號爲 25,可是隻監聽了 localhost 網絡接口。也就是說只有運行在主機 B 上的郵件客戶端才能與 smtp 服務器創建鏈接。此時另一臺主機 A 上的郵件客戶端若是想要經過主機 B 上的 smtp 服務器收發郵件該怎麼設置呢?經過 SSH 的本地端口轉發功能能夠輕鬆的搞定這樣的場景!
假設兩臺主機上都安裝了 SSH,咱們可使用主機 A 上的 SSH 客戶端向主機 B 上的 SSH 服務器發起請求,創建一條執行端口轉發的隧道:安全
$ ssh -L 10025:localhost:25 HostB
此命令的運行原理以下圖所示(此圖來自互聯網):服務器
運行上面的命令後,SSH 客戶端程序在主機 A 上監聽了 localhost:10025(你能夠用 1024 - 65535 之間的任意端口代替 10025,只要不與已有端口衝突就行)。全部在主機 A 上發往 10025 端口的消息都會經過 SSH 隧道轉發到主機 B 上的 25 端口。接下來須要配置主機 A 上的郵件客戶端程序,讓它把消息發送到 localhost:10025。完成以後主機 A 上的郵件客戶端就能夠經過主機 B 上的 smtp 服務器收發郵件了。具體的數據包的流向爲:網絡
1 郵件客戶端把數據包發送到 localhost(主機 A) 的 10025 端口
2 SSH 客戶端把數據包加密並從主機 A 發送到主機 B 的 SSH 服務器
3 SSH 服務器把數據包解密併發送到 localhost(主機 B) 的 25 端口
從 smtp 服務器返回的數據包則是沿着原路返回以完成數據的雙向傳遞。併發
至此咱們已經完成了一個最基本的本地端口轉發 demo 的介紹。接下來讓咱們來聊一下究竟什麼叫本地端口轉發?
在上面的 demo 中咱們注意到一共有兩對的客戶端與服務器程序,分別是 smtp 應用的客戶端和服務器與 SSH 的客戶端和服務器。若是應用程序的客戶端和 SSH 的客戶端位於 SSH 隧道的同一側,而應用程序的服務器和 SSH 服務器位於 SSH 隧道的另外一側,那麼這種端口轉發類型就是本地端口轉發。須要使用 -L 選項來建立。ssh
前面的 demo 中應用程序的客戶端和 SSH 客戶端位於同一臺主機上,應用程序的服務器端和 SSH 的服務器端也位於同一臺主機上,真實的狀況每每不是這樣的:網站
上圖中的場景可能更符合真實狀況(此圖來自互聯網)。應用程序的客戶端和 SSH 客戶端分別位於 SSH 隧道同一側的兩臺不一樣的主機上,而應用的服務器端和 SSH 服務器分別位於 SSH 隧道另外一側的兩臺不一樣的主機上。此時咱們須要使用下面的命令:ui
$ ssh -g -L P:HostS:W HostB
應用 -g 選項後主機 A 不只會監聽 localhost 的 P 端口,還可以監聽全部網絡接口的 P 端口,因此主機 C 上的應用客戶端就能夠把消息發送到主機 A 的 P 端口。
接下來咱們必需要介紹本地端口轉發的命令格式了:加密
ssh -L <local port>:<remote host>:<remote port> <SSH server host>
SSH server host 是 SSH 服務器所在的主機, remote host 和 remote port 則分別指應用程序服務器所在主機和監聽端口。若是 remote host 指定爲 localhost 則認爲應用程序服務器和 SSH 服務器在同一臺主機上。
在結束本地端口轉發以前還須要介紹另外兩個選項,它們是 f 和 N。上面的命令在建立隧道的同時登陸到遠程主機,通常狀況下咱們不須要這個登陸。何況一旦這個登陸退出,隧道也會隨之關閉。咱們更指望的是可以建立在後臺運行的隧道,這時就須要添加 f 和 N 選項。
咱們必須區別遠程端口轉發和本地端口轉發,由於它們對應了不一樣的應用場景,固然使用的命令行選項也是不同的。若是應用程序的客戶端和 SSH 的服務器位於 SSH 隧道的同一側,而應用程序的服務器和 SSH 的客戶端位於 SSH 隧道的另外一側,那麼這種端口轉發類型就是遠程端口轉發。遠程端口轉發的結構以下圖所示(此圖來自互聯網):
因此,區分本地端口轉發和遠程端口轉發主要是看 SSH 客戶端與應用程序的哪一部分在 SSH 隧道的同一側!遠程端口轉發的命令格式爲:
ssh -R <local port>:<remote host>:<remote port> <SSH server host>
其它的細節二者基本也是同樣的。可是遠程端口轉發不支持 -g 參數,這讓咱們很難實現相似下面的用例:
內網中主機 A 上運行 Jenkins 服務器監聽本機 8080 端口,並運行 SSH 客戶端。
外網中的主機 B 上運行 SSH 服務器。
但願能夠經過遠程端口轉發的方式在主機 A 和 B 之間創建隧道,
而後外網的 Bitbucket 等代碼管理服務能夠經過 Webhook 的方式訪問主機 B 從而觸發 Jenkins 服務器中的 Build。
這個問題的根源在於咱們執行下面的遠程端口轉發命令後:
$ ssh -R 18080:localhost:8080 HostB
主機 B 只能監聽 localhost 的 18080 端口:
如何讓 HostB 監聽本機全部網絡接口的 18080 端口呢? 須要經過修改 SSH 服務器的配置來實現這個功能!在 SSH 服務器的配置文件 /etc/ssh/sshd_config 中添加一行:
GatewayPorts yes
保存後重啓 SSH 服務器,而後從新創建隧道:
此時主機 B 已經能夠接受外部 webhook 的調用了。
相對於動態端口轉發,前面介紹的端口轉發類型都叫靜態端口轉發。所謂的 "靜態" 是指應用程序服務器端的 IP 地址和監聽的端口是固定的。試想另一類應用場景:設置瀏覽器經過端口轉發訪問不一樣網絡中的網站(好比在家裏鏈接公司內網中的站點,哈哈)。這類應用的特色是目標服務器的 IP 和端口是未知的而且老是在變化,建立端口轉發時不可能知道這些信息。只有在發送 HTTP 請求時才能肯定目標服務器的 IP 和端口。在這種場景下靜態端口轉發的方式是搞不定的,於是須要一種專門的端口轉發方式支持即 "動態端口轉發"。SSH 動態端口轉發是經過 Socks 協議實現的,建立動態端口轉發時 SSH 服務器就相似一個 Socks 代理服務器,因此這種轉發方式也叫 Socks 轉發。
動態端口轉發的命令格式爲:
$ ssh -D <local port> <SSH Server Host>
例如:
$ ssh -D 11080 nick@xxx.xxx.xxx.xxx
注意,命令中不須要指定目標服務器和端口號。執行上面的命令後 SSH 客戶端就開始監聽本機 localhost 的 11080 端口。你能夠把本機上瀏覽器網絡配置中的 Socks 服務器指定爲 localhost:11080。而後瀏覽器中的請求會被轉發到 SSH 服務器端,並從SSH 服務器端與目標站點創建鏈接進行通訊。
SSH 端口轉發是一項很是實用的技術,靈活的使用它不只能夠解決工程項目中繁雜的網絡問題,還可以給咱們的生活添加樂趣!