你在家裡執行著一臺 Linux 伺服器,它放在一個 NAT 路由器或者限制性防火牆後面。現在你想在外出時用 SSH 登入到這臺伺服器。你如何才能做到呢?SSH 埠轉發當然是一種選擇。但是,如果你需要處理多級巢狀的 NAT 環境,埠轉發可能會變得非常棘手。另外,在多種 ISP 特定條件下可能會受到干擾,例如阻塞轉發埠的限制性 ISP 防火牆、或者在使用者間共享 IPv4 地址的運營商級 NAT。
什麼是反向 SSH 隧道?
SSH 埠轉發的一種替代方案是 反向 SSH 隧道。反向 SSH 隧道的概念非常簡單。使用這種方案,在你的受限的家庭網路之外你需要另一臺主機(所謂的“中繼主機”),你能從當前所在地通過 SSH 登入到它。你可以用有公網 IP 地址的 VPS 例項 配置一箇中繼主機。然後要做的就是從你的家庭網路伺服器中建立一個到公網中繼主機的永久 SSH 隧道。有了這個隧道,你就可以從中繼主機中連線“回”家庭伺服器(這就是為什麼稱之為 “反向” 隧道)。不管你在哪裡、你的家庭網路中的 NAT 或 防火牆限制多麼嚴格,只要你可以訪問中繼主機,你就可以連線到家庭伺服器。
在 Linux 上設定反向 SSH 隧道
讓我們來看看怎樣建立和使用反向 SSH 隧道。我們做如下假設:我們會設定一個從家庭伺服器(homeserver)到中繼伺服器(relayserver)的反向 SSH 隧道,然後我們可以通過中繼伺服器從客戶端計算機(clientcomputer) SSH 登入到家庭伺服器。本例中的中繼伺服器 的公網 IP 地址是 1.1.1.1。
在家庭伺服器上,按照以下方式開啟一個到中繼伺服器的 SSH 連線。
1 |
homeserver~$ ssh -fN -R 10022:localhost:22 relayserver_user@1.1.1.1 |
這裡埠 10022 是任何你可以使用的埠數字。只需要確保中繼伺服器上不會有其它程式使用這個埠。
“-R 10022:localhost:22” 選項定義了一個反向隧道。它轉發中繼伺服器 10022 埠的流量到家庭伺服器的 22 號埠。
用 “-fN” 選項,當你成功通過 SSH 伺服器驗證時 SSH 會進入後臺執行。當你不想在遠端 SSH 伺服器執行任何命令,就像我們的例子中只想轉發埠的時候非常有用。
執行上面的命令之後,你就會回到家庭主機的命令列提示框中。
登入到中繼伺服器,確認其 127.0.0.1:10022 繫結到了 sshd。如果是的話就表示已經正確設定了反向隧道。
1 |
relayserver~$ sudo netstat -nap | grep 10022 |
1 |
tcp 0 0 127.0.0.1:10022 0.0.0.0:* LISTEN 8493/sshd |
現在就可以從任何其它計算機(客戶端計算機)登入到中繼伺服器,然後按照下面的方法訪問家庭伺服器。
1 |
relayserver~$ ssh -p 10022 homeserver_user@localhost |
需要注意的一點是你在上面為localhost輸入的 SSH 登入/密碼應該是家庭伺服器的,而不是中繼伺服器的,因為你是通過隧道的本地端點登入到家庭伺服器,因此不要錯誤輸入中繼伺服器的登入/密碼。成功登入後,你就在家庭伺服器上了。
通過反向 SSH 隧道直接連線到網路地址變換後的伺服器
上面的方法允許你訪問 NAT 後面的 家庭伺服器,但你需要登入兩次:首先登入到 中繼伺服器,然後再登入到家庭伺服器。這是因為中繼伺服器上 SSH 隧道的端點繫結到了迴環地址(127.0.0.1)。
事實上,有一種方法可以只需要登入到中繼伺服器就能直接訪問NAT之後的家庭伺服器。要做到這點,你需要讓中繼伺服器上的 sshd 不僅轉發迴環地址上的埠,還要轉發外部主機的埠。這通過指定中繼伺服器上執行的 sshd 的 GatewayPorts 實現。
開啟中繼伺服器的 /etc/ssh/sshd_conf 並新增下面的行。
1 |
relayserver~$ vi /etc/ssh/sshd_conf |
1 |
GatewayPorts clientspecified |
重啟 sshd。
基於 Debian 的系統:
1 |
relayserver~$ sudo /etc/init.d/ssh restart |
基於紅帽的系統:
1 |
relayserver~$ sudo systemctl restart sshd |
現在在家庭伺服器中按照下面方式初始化一個反向 SSH 隧道。
1 |
homeserver~$ ssh -fN -R 1.1.1.1:10022:localhost:22 relayserver_user@1.1.1.1 |
登入到中繼伺服器然後用 netstat 命令確認成功建立的一個反向 SSH 隧道。
1 |
relayserver~$ sudo netstat -nap | grep 10022 |
1 |
tcp 0 0 1.1.1.1:10022 0.0.0.0:* LISTEN 1538/sshd: dev |
不像之前的情況,現在隧道的端點是 1.1.1.1:10022(中繼伺服器的公網 IP 地址),而不是 127.0.0.1:10022。這就意味著從外部主機可以訪問隧道的另一端。
現在在任何其它計算機(客戶端計算機),輸入以下命令訪問網路地址變換之後的家庭伺服器。
1 |
clientcomputer~$ ssh -p 10022 homeserver_user@1.1.1.1 |
在上面的命令中,1.1.1.1 是中繼伺服器的公共 IP 地址,homeserver_user必須是家庭伺服器上的使用者賬戶。這是因為你真正登入到的主機是家庭伺服器,而不是中繼伺服器。後者只是中繼你的 SSH 流量到家庭伺服器。
在 Linux 上設定一個永久反向 SSH 隧道
現在你已經明白了怎樣建立一個反向 SSH 隧道,然後把隧道設定為 “永久”,這樣隧道啟動後就會一直執行(不管臨時的網路擁塞、SSH 超時、中繼主機重啟,等等)。畢竟,如果隧道不是一直有效,你就不能可靠的登入到你的家庭伺服器。
對於永久隧道,我打算使用一個叫 autossh 的工具。正如名字暗示的,這個程式可以讓你的 SSH 會話無論因為什麼原因中斷都會自動重連。因此對於保持一個反向 SSH 隧道非常有用。
第一步,我們要設定從家庭伺服器到中繼伺服器的無密碼 SSH 登入。這樣的話,autossh 可以不需要使用者干預就能重啟一個損壞的反向 SSH 隧道。
下一步,在建立隧道的家庭伺服器上安裝 autossh。
在家庭伺服器上,用下面的引數執行 autossh 來建立一個連線到中繼伺服器的永久 SSH 隧道。
1 |
homeserver~$ autossh -M 10900 -fN -o "PubkeyAuthentication=yes" -o "StrictHostKeyChecking=false" -o "PasswordAuthentication=no" -o "ServerAliveInterval 60" -o "ServerAliveCountMax 3" -R 1.1.1.1:10022:localhost:22 relayserver_user@1.1.1.1 |
“-M 10900” 選項指定中繼伺服器上的監視埠,用於交換監視 SSH 會話的測試資料。中繼伺服器上的其它程式不能使用這個埠。
“-fN” 選項傳遞給 ssh 命令,讓 SSH 隧道在後臺執行。
“-o XXXX” 選項讓 ssh:
- 使用金鑰驗證,而不是密碼驗證。
- 自動接受(未知)SSH 主機金鑰。
- 每 60 秒交換 keep-alive 訊息。
- 沒有收到任何響應時最多傳送 3 條 keep-alive 訊息。
其餘 SSH 隧道相關的選項和之前介紹的一樣。
如果你想系統啟動時自動執行 SSH 隧道,你可以將上面的 autossh 命令新增到 /etc/rc.local。
總結
在這篇博文中,我介紹了你如何能從外部通過反向 SSH 隧道訪問限制性防火牆或 NAT 閘道器之後的 Linux 伺服器。這裡我介紹了家庭網路中的一個使用事例,但在企業網路中使用時你尤其要小心。這樣的一個隧道可能被視為違反公司政策,因為它繞過了企業的防火牆並把企業網路暴露給外部攻擊。這很可能被誤用或者濫用。因此在使用之前一定要記住它的作用。