使用 OpenSSH 替沒有固定 IP 的本地主機 (WSL2) 建立反向 TCP 遠端通道 (Ngrok 免費替代)

目錄

若我們希望將本地主機的服務,例如 ssh 伺服器供外部連線,但我們又沒有固定 IP 時,我們可以使用 OpenSSH 建立反向 TCP 遠端通道,讓外部主機可以透過這個通道連線到我們的本地主機。

其實本質上還是透過一個具有固定 IP 的中間主機,將外部主機的請求轉發到本地主機,這個中間主機可以是我們自己的伺服器,也可以是一些提供這種服務的雲端主機,例如 Google CloudAWS 等等。

假設我們使用 Google 雲端主機作為交換的通道,大致上的流程會是:

    sequenceDiagram
	    participant A as 電腦A (內網Linux主機)
	    participant B as 電腦B (Google雲端主機)
	    participant C as 電腦C (任意外部電腦)
	
	    A->>+B: 建立反向通道到電腦B
	    Note over A,B: 電腦A透過反向通道與電腦B保持連線
	
	    C->>+B: 連線到電腦B
	    B->>+A: 透過反向通道轉發請求給電腦A
	    A-->>-B: 回應請求
	    B-->>-C: 轉發回應給電腦C

在 Google 雲端主機上建立反向通道

首先,我們需要在 Google 雲端主機上建立一個 OpenSSH 伺服器,並且允許 GatewayPorts 選項。

# 安裝 OpenSSH 伺服器
sudo apt update && sudo apt install -y openssh-server
# 編輯 sshd_config
# 將 ListenAddress 設定為 0.0.0.0
# 將 GatewayPorts 設定為 yes
sudo vim /etc/ssh/sshd_config
# 重啟 OpenSSH 伺服器
sudo systemctl restart sshd
# 確認 OpenSSH 伺服器是否啟動
sudo systemctl status sshd

在本地主機上建立反向通道

接著,我們需要在本地主機上建立一個反向通道,將本地主機的 22 port 與 Google 雲端主機的 2222 port 進行映射。

# 安裝 OpenSSH 伺服器
sudo apt update && sudo apt install -y openssh-server
# 編輯 sshd_config 並將 GatewayPorts 設定為 yes
# 如果要讓遠端主機可以使用密碼登入,則將 PasswordAuthentication 設定為 yes
sudo vim /etc/ssh/sshd_config
# 啟動 OpenSSH 伺服器
sudo systemctl start ssh

# 啟用 OpenSSH 服務
sudo systemctl enable ssh

# 重新載入 sshd_config
sudo /etc/init.d/ssh reload

# 查看 OpenSSH 服務狀態
sudo systemctl status ssh
# 重啟 OpenSSH 伺服器
sudo systemctl restart sshd

產生 ssh public key 並複製到 Google 雲端主機:

# 產生 ssh key
ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
# 複製 ssh public key 到 Google 雲端主機
ssh-copy-id -i ~/.ssh/id_rsa.pub <Google Cloud User>@<Google Cloud IP>

將遠端主機的連線資訊寫入 ~/.ssh/config 檔案中:

Host google-cloud
    HostName <Google Cloud IP>
    Port 22
    User <Google Cloud User>
    IdentityFile ~/.ssh/id_rsa
    ServerAliveInterval 60 # 每 60 秒發送一次心跳
    ServerAliveCountMax 3 # 最多發送 3 次心跳

建立反向通道:

# 在本地主機上建立反向通道
REMOTE_PORT=2222
LOCAL_PORT=22
ssh -M -S /tmp/ssh_master -f -N -T -R $REMOTE_PORT:localhost:$LOCAL_PORT google-cloud
  • -Mssh 建立 “master” 模式
  • -Sssh 在建立 “master” 模式之後,順便建立一個 control socket 檔案以利後續停止或添加新的通道
  • -fssh 在開始執行之前,就將該程序放入到背景執行
  • -Nssh 不要在遠端主機執行命令,通常建立 SSH 通道時會使用這個參數
  • -Tssh 停用虛擬終端機建立
  • -Rssh 建立一個反向 TCP 通道(Reverse TCP channel)
# 關閉反向通道
ssh -S /tmp/ssh_master -O exit google-cloud

測試反向通道

在 Google 雲端主機上,使用 ssh 連線到本地主機:

ssh user@localhost -p 2222

確認 port 2222 是否允許外部連線:

sudo ss -tuln | grep 2222
# 預期輸出:
# tcp   LISTEN 0      128          0.0.0.0:2222        0.0.0.0:*
# tcp   LISTEN 0      128             [::]:2222           [::]:*

使用 autossh 保持反向通道連線並在開機後自動啟動

由於 ssh 通道可能會因為網路問題或其他原因而中斷,我們可以使用 autossh 來保持通道連線。 並且使用 systemd 服務來在開機後自動啟動 autossh

# 安裝 autossh
sudo apt update && sudo apt install -y autossh

# 建立 systemd 服務
sudo vim /etc/systemd/system/create_reverse_tunnel.service
[Unit]
Description=Create reverse tunnel to Google Cloud
After=network-online.target ssh.service

[Service]
User=<user> # 誰執行這個服務
ExecStart=/usr/bin/autossh -M 0 -N -T -R 2222:localhost:22 google-cloud
Restart=always
RestartSec=20
Environment="AUTOSSH_GATETIME=0"

[Install]
WantedBy=multi-user.target
# 啟動 systemd 服務
sudo systemctl start create_reverse_tunnel

# 啟用 systemd 服務
sudo systemctl enable create_reverse_tunnel

# 查看 systemd 服務狀態
sudo systemctl status create_reverse_tunnel

在外部主機上連線到本地主機

上述步驟完成後,我們就可以在外部主機上使用 ssh 連線到本地主機:

ssh user@<Google Cloud IP> -p 2222 # user 為本地主機的使用者,若需要輸入密碼,則輸入本地主機的密碼

相關連結

分享 :