2007年11月20日

好孩子不要學:SSH Reverse Tunnel

有時候不得以還是得在下班時間處理公務,最麻煩的一點莫過於多數的操作行為必須仰賴於公司所提供的網路環境設定。如果公司有提供寬頻 VPN 環境時倒也罷了,不過通常而言公司不會特地費心去建置一個可能很少使用的 VPN 環境。

沒得接觸內部網路就無法處理公務,在此狀況下就會有三種不同應變作為:

  1. 特地回公司一趟:長時間的作業倒也罷了,如果只是為了重新啟動一個 service 而特地跑回公司則顯的愚蠢和浪費時間。
  2. 裝死,上班後再說:是的,即然覺得為了按下一個 enter 鍵而特地回公司是件愚蠢的行為,那麼就裝死吧。
  3. 自行建立專屬 VPN 環境:通常這會違反公司網路使用規定的,但… 您知道的,網管部門不一定會特地去追蹤偶一為之的非法連線,再者透過多段轉接方式也可以避免對目標主機建立一個長期連線而被網管捕捉到。本篇將針對此法進行簡要說明。
自行建立專屬 VPN 環境的方式應該有很多種,此篇以透過 SSH Server 方式簡單加以介紹。

SSH 提供了 Port Forwarding 功能而得以建立連線通道,分別是:

  1. Dynamic SOCKS-based port forwarding:簡單而言就是一個 Sockets 5 proxy。連線方向為 本機:portSSH Server任意遠端主機
  2. Forward local port to remote address:將本機上的某個 port 對應到遠端主機所在的某個 port。連線方向為 本機:portSSH Server指定遠端主機:port
  3. Forward remote port to local address:將 SSH Server 上的某個 port 對應到遠端主機上的某個 port 。連線方向為 SSH Server:port本機指定遠端主機:port
通常為了突破網管部門所設的防火牆限制,透過 Sockets 5 proxy 是最簡單的方式,以下範例說明如何在本機的 port 1080 上建立一個 Sockets 5 proxy:
# Linux/Unix 平台
$ ssh -N -D 1080 user@SSH_SERVER

% Windows 平台,使用 plink 
C:\> plink -N -D 1080 user@SSH_SERVER

建立起 Tunnel 後可檢查 port 1080 是否進入 LISTENING 狀態:

% Windows 平台之 netstat -a 輸出段落
TCP    WS405:1080             WS405.*.*.*:0   LISTENING

如果想在本機假裝提供某項服務,那就可以採用 Forward local port to remote address 模式將遠端主機的指定 port 對應到本機上,製造出本機有提供某某服務的假象。以下範例可以達到將 Google 查詢服務搬到本機上來的錯覺:

# Linux/Unix 平台
$ ssh -L 80:www.google.com.tw:80 user@SSH_SERVER

% Windows 平台
C:\> plink -L 80:www.google.com.tw:80 user@SSH_SERVER
成果如下:

最後是重頭戲,也是本文原本的重點:如果想要建立由家裡連結到公司電腦的網路連線,那麼 Forward remote port to local address 模式可以符合這樣的需求,此方式為 SSH Tunnel 的特例,通稱為 SSH Reverse Tunnel 。

假設企業內部某主機 svn.mycompany.com 提供了 Subversion 服務,而員工 A 希望在假期時仍然可以由家裡透過 port 36900 存取到 svn.mycompany.com 主機上的 SVN 檔案庫(在 port 3690 上)時,他可以在公司的個人電腦上建立一個 SSH Reverse Tunnel,如下例:

## Linux/Unix 平台
$ ssh -R 36900:svn.mycompany.com:3690 user@SSH_SERVER
% Windows 平台
C:\> plink -R 36900:svn.mycompany.com:3690 user@SSH_SERVER
完成以上設定後員工 A 即可由家裡透過以下指令連接上 svn.mycompany.com 上的 SVN 檔案庫:
$ svn log svn://localhost:36900/projects/myProject1

最後簡單整理各種狀態的使用時機:

  1. 透過 SSH Server 連接到不定對象的遠端主機上:請使用 Dynamic SOCKS-based port forwarding 方式在本機與 SSH Server 間建立 Sockets 5 proxy 通道。
  2. 透過 SSH Server 把遠端的服務移轉到本機上:請使用 Forward local port to remote address 方式。這裡可以把 -L 的意思想成希望在 local host 上進行連線,所以 -L 後接的 port 編號指的是要在本機的哪個 port 上提供連線服務。
  3. 希望在 SSH Server 上透過 SSH 連線連接到遠端主機上:請使用 Forward remote port to local address 方式。這裡可以把 -R 的意思想成希望在 remote host (也就是 SSH Server)進行連線,因此 -R 後所接的 port 編號指的是在 remote host 上的某個 port 提供連線服務。
  4. 通常而言為了安全起見,SSH Reverse Tunnel 不應該建立在 SSH Server 的 public ip 上,所以前述的指令可以將 listening port 掛在 127.0.0.1 這個 private IP 下,如:
    ssh -R 127.0.0.1:36900:svn.mycompany.com:3690 user@SSH_SERVER
    其實經多次實驗確認,我的 OpenSSH_4.7p1 只接受在 127.0.0.1 上建立 Reverse Tunnel ,即使我想指定到 public ip 上都不行…