工程筆記

用 SSH 反向通道讓雲端 Mac 存取本機 API

遠端 Mac ·約 7 分鐘閱讀

用 SSH 反向通道讓雲端 Mac 存取本機 API

你在本機電腦啟動了一個尚未部署的 API,但 Xcode 專案與建置腳本都在雲端 Mac 上執行。直接開放路由器連接埠不但耗時,也會擴大攻擊面;若暫時把程式碼儲存庫裡的位址改成公網網域,也很容易留下不必要的設定變更。更穩妥的方式,是由開發電腦主動建立 SSH 連線,再透過反向通道把本機連接埠映射到雲端 Mac 的回送位址。

先確認流量方向

反向通道解決的是「雲端存取本機」的需求。假設 API 在開發電腦的 127.0.0.1:8080 上執行,並希望雲端 Mac 能透過 127.0.0.1:18080 呼叫它。SSH 連線由開發電腦發起並連入雲端 Mac,再將雲端收到的流量帶回本機連接埠。

本機轉送的方向則相反:開發電腦需要存取雲端 Mac 上僅監聽回送位址的除錯服務。例如雲端服務位於 127.0.0.1:9000,便可將它映射成開發電腦上的 127.0.0.1:19000

目標 SSH 參數 發起連線的位置 存取入口
雲端 Mac 存取本機 API -R 開發電腦 雲端 127.0.0.1:18080
本機存取雲端除錯服務 -L 開發電腦 本機 127.0.0.1:19000

先用實際用戶端確認來源服務可用,不要一開始就把問題歸咎於通道:

curl -i http://127.0.0.1:8080/health
lsof -nP -iTCP:8080 -sTCP:LISTEN

如果應用程式沒有健康檢查路徑,也可以暫時啟動一個只監聽本機的測試服務:

python3 -m http.server 8080 --bind 127.0.0.1

建立最小暴露範圍的反向通道

在開發電腦設定雲端位址後執行:

export CLOUD_MAC_IP="你的节点地址"
ssh -N \
  -o ExitOnForwardFailure=yes \
  -o ServerAliveInterval=30 \
  -o ServerAliveCountMax=3 \
  -R 127.0.0.1:18080:127.0.0.1:8080 \
  dev@"$CLOUD_MAC_IP"

-N 代表不執行遠端命令,這條連線只負責轉送流量。ExitOnForwardFailure 可避免連接埠綁定失敗後,仍留下看似正常的 SSH 工作階段。兩個連線保活參數可用來識別失效連線,但不保證網路中斷後會自動重建通道。

在雲端 Mac 開啟另一個終端機進行驗證:

lsof -nP -iTCP:18080 -sTCP:LISTEN
curl -i http://127.0.0.1:18080/health

檢查 lsof 輸出時,監聽位址應為 127.0.0.1:18080,而不是 *:18080。應用程式也應透過開發環境變數設定聯調位址,不要直接寫死在原始碼中:

export DEV_API_BASE_URL="http://127.0.0.1:18080"
xcodebuild -scheme DemoApp -configuration Debug test

通道只會加密傳輸並限制入口,不能取代 API 本身的身分驗證。即使連接埠只監聽回送位址,也應保留測試權杖、權限檢查與去識別化日誌。

用 SSH 設定降低誤操作風險

反覆輸入長命令,很容易把本機連接埠、遠端連接埠與目標位址寫反。你可以在開發電腦的 ~/.ssh/config 中建立專用項目:

Host minid-api-tunnel
    HostName CLOUD_MAC_IP
    User dev
    RemoteForward 127.0.0.1:18080 127.0.0.1:8080
    ExitOnForwardFailure yes
    ServerAliveInterval 30
    ServerAliveCountMax 3

CLOUD_MAC_IP 替換成實際節點位址,並確認設定檔權限正確:

chmod 700 ~/.ssh
chmod 600 ~/.ssh/config
ssh -N minid-api-tunnel

不要在同一個項目裡放入大量互不相關的轉送規則。每個專案使用一個別名,連接埠則依用途分段,例如 API 使用 18080,除錯面板使用 19000。如此一來,看到監聽中的連接埠時就能反推出所屬專案,也能降低多人共用節點時發生衝突的機率。

需要從本機存取雲端服務時,請另外建立本機轉送:

ssh -N \
  -o ExitOnForwardFailure=yes \
  -L 127.0.0.1:19000:127.0.0.1:9000 \
  dev@"$CLOUD_MAC_IP"

之後只在開發電腦存取 http://127.0.0.1:19000。這兩種轉送方式都不需要讓應用服務監聽公網網路介面。

分層排查連線失敗

通道問題應依序檢查「來源服務、SSH 轉送、目標呼叫」三個層次,避免同時修改防火牆、應用程式設定與專案程式碼。

來源服務層

在開發電腦執行 curllsof。如果 API 只監聽 IPv6 的 ::1,但轉送目標填寫的是 127.0.0.1,連線就會遭到拒絕。此時應統一監聽協定,或將轉送目標改成實際位址。另外也要確認本機 Proxy 沒有接管回送流量,可暫時使用:

curl --noproxy '*' -i http://127.0.0.1:8080/health

SSH 轉送層

加入詳細輸出,以確認連接埠申請結果:

ssh -vv -N \
  -o ExitOnForwardFailure=yes \
  -R 127.0.0.1:18080:127.0.0.1:8080 \
  dev@"$CLOUD_MAC_IP"

如果訊息顯示遠端連接埠轉送失敗,請先改用尚未占用的連接埠。若仍然失敗,再確認雲端 SSH 服務是否允許 TCP 轉送。不要為了省事改成監聽公網位址,也不要直接放寬成允許所有來源。

目標呼叫層

雲端終端機可以成功執行 curl,不代表所有執行目標都會自動沿用相同的網路條件。建置腳本、圖形化應用程式與模擬器可能會讀取不同的環境設定;HTTP 請求也可能受到專案安全政策影響。應分別記錄發出呼叫的程序、目標 URL、回傳碼與逾時時間,再判斷問題出在網路,還是應用層拒絕了請求。

上線前檢查與收尾

聯調完成後,先停止所有依賴通道的測試工作,再結束 SSH 工作階段。使用 lsof 確認映射連接埠已經消失,並移除專案中的臨時位址、測試權杖與除錯日誌。

提交程式碼前,至少檢查以下項目:

  • API 仍然只監聽開發電腦的回送位址。
  • 雲端映射連接埠只監聽 127.0.0.1
  • SSH 私密金鑰權限為 600,設定目錄權限為 700
  • 專案透過環境變數選擇聯調位址,發布設定沒有引用該連接埠。
  • 日誌中沒有請求權杖、金鑰、完整使用者資料與驗證標頭。
  • 多人共用專案時,已記錄連接埠編號、負責人與結束時間。

這套方案適合臨時 API、回呼驗證與遠端建置聯調。如果服務需要長期供多個節點呼叫,應改用具備獨立身分驗證、存取控制與稽核記錄的正式部署,不要把臨時通道當成正式環境入口。

常見問題

SSH 反向通道會讓本機 API 直接暴露在網際網路嗎?

只要將遠端監聽位址固定為 127.0.0.1,該連接埠便只能由雲端 Mac 本機存取。應避免使用 0.0.0.0,並以 lsof 核對實際監聽結果。

SSH 已連線,但雲端 Mac 仍無法存取映射連接埠怎麼辦?

先確認開發電腦上的來源服務正確監聽,再到雲端 Mac 使用 lsof 檢查目標連接埠。常見原因是連接埠衝突,或 SSH 伺服端禁止 TCP 轉送。

從 Xcode 執行的 App 可以直接使用通道位址嗎?

雲端 Mac 上的建置腳本可直接使用 127.0.0.1 與映射連接埠。模擬器或 App 還需要依其網路範圍及專案的 HTTP 安全規則個別驗證。

MiniD 雲端 Mac

按日、週或月租用獨享實體 Mac mini

整機獨享的實體 Mac mini,可透過遠端桌面與 SSH 連線;實際機型、區域與租期以訂購頁目前資訊為準。

查看訂購選項