通過 SSH 實現 TCP / IP 隧道(埠轉發):使用 OpenSSH 可能的 8 種場景
對於 Secure Shell (SSH) 這樣的網路協議來說,其主要職責就是在終端模式下訪問一個遠程系統。因為 SSH 協議對傳輸數據進行了加密,所以通過它在遠端系統執行命令是安全的。此外,我們還可以在這種加密後的連接上通過創建隧道(埠轉發)的方式,來實現兩個不同終端間的互聯。憑藉這種方式,只要我們能通過 SSH 創建連接,就可以繞開防火牆或者埠禁用的限制。
這個話題在網路領域有大量的應用和討論:
- Wikipedia: SSH Tunneling
- O』Reilly: Using SSH Tunneling
- Ssh.com: Tunneling Explained
- Ssh.com: Port Forwarding
- SecurityFocus: SSH Port Forwarding
- Red Hat Magazine: SSH Port Forwarding
我們在接下來的內容中並不討論埠轉發的細節,而是準備介紹一個如何使用 OpenSSH 來完成 TCP 埠轉發的速查表,其中包含了八種常見的場景。有些 SSH 客戶端,比如 PuTTY,也允許通過界面配置的方式來實現埠轉發。而我們著重關注的是通過 OpenSSH 來實現的的方式。
在下面的例子當中,我們假設環境中的網路劃分為外部網路(network1)和內部網路(network2)兩部分,並且這兩個網路之間,只能在 externo1 與 interno1 之間通過 SSH 連接的方式來互相訪問。外部網路的節點之間和內部網路的節點之間是完全聯通的。
場景 1
在 externo1 節點訪問由 interno1 節點提供的 TCP 服務(本地埠轉發 / 綁定地址 = localhost / 主機 = localhost )
externo1 節點可以通過 OpenSSH 連接到 interno1 節點,之後我們想通過其訪問運行在 5900 埠上的 VNC 服務。
我們可以通過下面的命令來實現:
externo1 $ ssh -L 7900:localhost:5900 user@interno1
現在,我們可以在 externo1 節點上確認下 7900 埠是否處於監聽狀態中:
externo1 $ netstat -ltn
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
...
Tcp 0 0 127.0.0.1:7900 0.0.0.0:* LISTEN
...
我們只需要在 externo1 節點上執行如下命令即可訪問 internal 節點的 VNC 服務:
externo1 $ vncviewer localhost::7900
注意:在 vncviewer 的 man 手冊中並未提及這種修改埠號的方式。在 About VNCViewer configuration of the output TCP port 中可以看到。這也是 the TightVNC vncviewer 所介紹的的。
場景 2
在 externo2 節點上訪問由 interno1 節點提供的 TCP 服務(本地埠轉發 / 綁定地址 = 0.0.0.0 / 主機 = localhost)
這次的場景跟方案 1 的場景的類似,但是我們這次想從 externo2 節點來連接到 interno1 上的 VNC 服務:
正確的命令如下:
externo1 $ ssh -L 0.0.0.0:7900:localhost:5900 user@interno1
看起來跟方案 1 中的命令類似,但是讓我們看看 netstat
命令的輸出上的區別。7900 埠被綁定到了本地(127.0.0.1
),所以只有本地進程可以訪問。這次我們將埠關聯到了 0.0.0.0
,所以系統允許任何 IP 地址的機器訪問 7900 這個埠。
externo1 $ netstat -ltn
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
...
Tcp 0 0 0.0.0.0:7900 0.0.0.0:* LISTEN
...
所以現在在 externo2 節點上,我們可以執行:
externo2 $ vncviewer externo1::7900
來連接到 interno1 節點上的 VNC 服務。
除了將 IP 指定為 0.0.0.0
之外,我們還可以使用參數 -g
(允許遠程機器使用本地埠轉發),完整命令如下:
externo1 $ ssh -g -L 7900:localhost:5900 user@interno1
這條命令與前面的命令能實現相同效果:
externo1 $ ssh -L 0.0.0.0:7900:localhost:5900 user@interno1
換句話說,如果我們想限制只能連接到系統上的某個 IP,可以像下面這樣定義:
externo1 $ ssh -L 192.168.24.80:7900:localhost:5900 user@interno1
externo1 $ netstat -ltn
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
...
Tcp 0 0 192.168.24.80:7900 0.0.0.0:* LISTEN
...
場景 3
在 interno1 上訪問由 externo1 提供的 TCP 服務(遠程埠轉發 / 綁定地址 = localhost / 主機 = localhost)
在場景 1 中 SSH 伺服器與 TCP 服務(VNC)提供者在同一個節點上。現在我們想在 SSH 客戶端所在的節點上,提供一個 TCP 服務(VNC)供 SSH 服務端來訪問:
將方案 1 中的命令參數由 -L
替換為 -R
。
完整命令如下:
externo1 $ ssh -R 7900:localhost:5900 user@interno1
然後我們就能看到 interno1 節點上對 7900 埠正在監聽:
interno1 $ netstat -lnt
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
...
Tcp 0 0 127.0.0.1:7900 0.0.0.0:* LISTEN
...
現在在 interno1 節點上,我們可以使用如下命令來訪問 externo1 上的 VNC 服務:
interno1 $ vncviewer localhost::7900
場景 4
interno2 使用 externo1 上提供的 TCP 服務(遠端埠轉發 / 綁定地址 = 0.0.0.0 / 主機 = localhost)
與場景 3 類似,但是現在我們嘗試指定允許訪問轉發埠的 IP(就像場景 2 中做的一樣)為 0.0.0.0
,這樣其他節點也可以訪問 VNC 服務:
正確的命令是:
externo1 $ ssh -R 0.0.0.0:7900:localhost:5900 user@interno1
但是這裡有個重點需要了解,出於安全的原因,如果我們直接執行該命令的話可能不會生效,因為我們需要修改 SSH 服務端的一個參數值 GatewayPorts
,它的默認值是:no
。
GatewayPorts
該參數指定了遠程主機是否允許客戶端訪問轉發埠。默認情況下,sshd(8) 只允許本機進程訪問轉發埠。這是為了阻止其他主機連接到該轉發埠。GatewayPorts 參數可用於讓 sshd 允許遠程轉發埠綁定到非迴環地址上,從而可以讓遠程主機訪問。當參數值設置為 「no」 的時候只有本機可以訪問轉發埠;「yes」 則表示允許遠程轉發埠綁定到通配地址上;或者設置為 「clientspecified」 則表示由客戶端來選擇哪些主機地址允許訪問轉發埠。默認值是 「no」。
如果我們沒有修改伺服器配置的許可權,我們將不能使用該方案來進行埠轉發。這是因為如果沒有其他的限制,用戶可以開啟一個埠(> 1024)來監聽來自外部的請求並轉發到 localhost:7900
。
參照這個案例:netcat ( Debian # 310431: sshd_config should warn about the GatewayPorts workaround. )
所以我們修改 /etc/ssh/sshd_config
,添加如下內容:
GatewayPorts clientspecified
然後,我們使用如下命令來重載修改後的配置文件(在 Debian 和 Ubuntu 上)。
sudo /etc/init.d/ssh reload
我們確認一下現在 interno1 節點上存在 7900 埠的監聽程序,監聽來自不同 IP 的請求:
interno1 $ netstat -ltn
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
...
Tcp 0 0 0.0.0.0:7900 0.0.0.0:* LISTEN
...
然後我們就可以在 interno2 節點上使用 VNC 服務了:
interno2 $ internal vncviewer1::7900
場景 5
在 externo1 上使用由 interno2 提供的 TCP 服務(本地埠轉發 / 綁定地址 localhost / 主機 = interno2 )
在這種場景下我們使用如下命令:
externo1 $ ssh -L 7900:interno2:5900 user@interno1
然後我們就能在 externo1 節點上,通過執行如下命令來使用 VNC 服務了:
externo1 $ vncviewer localhost::7900
場景 6
在 interno1 上使用由 externo2 提供的 TCP 服務(遠程埠轉發 / 綁定地址 = localhost / host = externo2)
在這種場景下,我們使用如下命令:
externo1 $ ssh -R 7900:externo2:5900 user@interno1
然後我們可以在 interno1 上通過執行如下命令來訪問 VNC 服務:
interno1 $ vncviewer localhost::7900
場景 7
在 externo2 上使用由 interno2 提供的 TCP 服務(本地埠轉發 / 綁定地址 = 0.0.0.0 / 主機 = interno2)
本場景下,我們使用如下命令:
externo1 $ ssh -L 0.0.0.0:7900:interno2:5900 user@interno1
或者:
externo1 $ ssh -g -L 7900:interno2:5900 user@interno1
然後我們就可以在 externo2 上執行如下命令來訪問 vnc 服務:
externo2 $ vncviewer externo1::7900
場景 8
在 interno2 上使用由 externo2 提供的 TCP 服務(遠程埠轉發 / 綁定地址 = 0.0.0.0 / 主機 = externo2)
本場景下我們使用如下命令:
externo1 $ ssh -R 0.0.0.0:7900:externo2:5900 user@interno1
SSH 伺服器需要配置為:
GatewayPorts clientspecified
就像我們在場景 4 中講過的那樣。
然後我們可以在 interno2 節點上執行如下命令來訪問 VNC 服務:
interno2 $ internal vncviewer1::7900
如果我們需要一次性的創建多個隧道,使用配置文件的方式替代一個可能很長的命令是一個更好的選擇。假設我們只能通過 SSH 的方式訪問某個特定網路,同時又需要創建多個隧道來訪問該網路內不同伺服器上的服務,比如 VNC 或者 遠程桌面。此時只需要創建一個如下的配置文件 $HOME/redirects
即可(在 SOCKS 伺服器 上)。
# SOCKS server
DynamicForward 1080
# SSH redirects
LocalForward 2221 serverlinux1: 22
LocalForward 2222 serverlinux2: 22
LocalForward 2223 172.16.23.45:22
LocalForward 2224 172.16.23.48:22
# RDP redirects for Windows systems
LocalForward 3391 serverwindows1: 3389
LocalForward 3392 serverwindows2: 3389
# VNC redirects for systems with "vncserver"
LocalForward 5902 serverlinux1: 5901
LocalForward 5903 172.16.23.45:5901
然後我們只需要執行如下命令:
externo1 $ ssh -F $HOME/redirects user@interno1
本文轉載來自 Linux 中國: https://github.com/Linux-CN/archive