盤點使 macOS 應用流量通過代理的多種方式

Wi1dcard發表於2020-04-26

在開發過程中,我們經常需要使用到國外的資源,例如各種包依賴等。國內目前比較普遍的做法是使用由知名第三方維護的國內映象。雖然方便,但也存在一些無法避免的問題,例如:

  • 映象可靠性未知,出問題時我們無能為力,只能等待第三方修復。
  • 映象同步時間未知,可能存在資料滯後。
  • 存在安全隱患(儘管可能性較小)。

因此,我個人更加推薦依賴代理來實現快速訪問所需資源的目的。本文不會涉及任何違反法律法規的內容,只來談談 macOS 下如何讓不同應用的流量通過 本地的 代理服務。

在開始前,我將假設你已經在本地配置好了 HTTP 和 SOCKS5 代理服務,分別能夠通過 http://127.0.0.1:8080socks5://127.0.0.1:1080 訪問。

系統代理

作為 macOS 內建的功能,你可以在 System Preferences -> Network -> Advanced -> Proxies 內配置系統代理。

盤點使 macOS 應用流量通過代理的多種方式

另外也可以使用命令列配置系統代理:

networksetup -setwebproxy "Wi-Fi" 127.0.0.1 8080 # HTTP proxy
networksetup -setsocksfirewallproxy "Wi-Fi" 127.0.0.1 1080 # SOCKS5 proxy

networksetup -setwebproxystate "Wi-Fi" on
networksetup -setsocksfirewallproxystate "Wi-Fi" on

其中 "Wi-Fi" 是你的網路介面名,請根據實際情況填寫。

不過,macOS 的系統代理僅對部分 GUI 或做了 macOS 適配的應用生效,例如 Chrome 等 Web 瀏覽器、系統更新等。在開發時我們經常需要接觸 CLI,應該怎麼做呢?

http_proxyall_proxy 環境變數

絕大多數使用 HTTP 協議的 CLI 應用都支援這兩個環境變數,這幾乎已經成為了「行業標準」:

export http_proxy=http://127.0.0.1:8080 https_proxy=$http_proxy all_proxy=socks5://127.0.0.1:1080

典型支援的命令包括 curlgo getcomposer install 等。使用 curl -v ipinfo.io 檢視輸出可發現代理配置已經生效:

* Uses proxy env variable http_proxy == 'http://127.0.0.1:8080'
*   Trying 127.0.0.1...

...

如果你本地的代理服務常駐後臺執行,可將 export ... 命令寫入 .zshrc,這樣每次啟動 Zsh(Shell)時就自動配置好了。

SSH Config ProxyCommand 指令

像 Git over SSH、SCP、SFTP 這類基於 SSH 協議的應用,並不支援以上兩個環境變數。不過我們可以修改 SSH 配置檔案 ~/.ssh/config 來實現,例如:

Host github.com
    ProxyCommand /usr/bin/nc -x 127.0.0.1:1080 %h %p

以上配置中的 ProxyCommand 只在連線到 github.com 時生效,你可以將 Host github.com 替換成其它 Git 服務商的主機名,也可以替換為 Host *,表示 SSH 到任意主機均生效。但我並不推薦這麼做,除非你想 SSH 到自己的伺服器時也通過代理。

Proxifier 和 Surge

根據我自己的經驗,以上方法已經能夠覆蓋絕大多數開發者的使用場景。如果有些應用還是不走代理,你可以試試 Proxifier 和 Surge 這兩款產品。其中 Surge 是 macOS 專有應用,不可用於 Windows 或 Linux 系統。這類應用的思路大致是:

  1. 偽裝成 TUN 裝置(可理解為虛擬網路卡)。
  2. 修改或覆蓋系統預設路由,把所有 TCP、UDP、ICMP 等三層及以上的流量全部發到虛擬 TUN 裝置。
  3. 經過內部處理後把流量導向代理服務。
  4. 代理伺服器流出的流量使用 SO_MARK 等方式進行特殊標記,使用系統策略路由導向到真實網路卡,避免死迴圈。

例如啟動 Surge 增強模式後,可使用 netstat -nr 看出路由表的變化:

Routing tables

Internet:
Destination        Gateway            Flags        Netif Expire
default            192.168.1.254      UGSc           en0             <-- 預設路由
1                  198.18.0.1         UGSc         utun7             <---
2/7                198.18.0.1         UGSc         utun7              |
4/6                198.18.0.1         UGSc         utun7              |  為了覆蓋預設路由
8/5                198.18.0.1         UGSc         utun7              |  Surge 新增的路由
16/4               198.18.0.1         UGSc         utun7              |
32/3               198.18.0.1         UGSc         utun7              |
64/2               198.18.0.1         UGSc         utun7             <---
...

虛擬 TUN 裝置介面名為 utun7,閘道器 IP 為 198.18.0.1,這個 IP 段還被 Surge 用來做一些特殊用途,例如反查 IP 對應的域名以支援策略代理功能,在此不再詳述。

這類實現雖然理論上能夠將系統所有流量都導向這個虛擬的 TUN 裝置,但仍有一些侷限。目前根據我的觀察,FaceTime 似乎不會遵循路由表的配置,我猜是 Apple 為了優化通訊協議直接繞過了自定義路由表以便於發起點對點通訊吧。

另外,由於 SOCKS 和 HTTP 代理協議的限制,雖然修改路由表後 ICMP 流量也會到達虛擬 TUN 裝置,但它不能代理 ICMP 協議,因此如何「優雅地」處理這些流量,仍然是 Surge 這類產品需要優化的問題。

綜上,我個人不特別常用 Surge 的增強模式,但有時不確定某個應用是否支援通常的代理配置、時間又比較緊張時,魔改路由的確是個簡單粗暴、快刀斬亂麻的解決方案。

通過 LD_PRELOAD 劫持共享庫

利用這種方式最好的當屬 proxychains-ng 專案了,它是上古世紀 proxychains 的繼任者。

主要思路是通過修改 LD_PRELOAD 環境變數,劫持 connect() 等系統呼叫,從而將流量轉發到代理伺服器。由於我對這類產品使用不多,因此不多作評論。但比較肯定的是,像 Golang 靜態編譯後的應用,由於它們不會載入系統共享庫,所以 proxychains-ng 對其無效。

通過 ptrace 跟蹤系統呼叫

與上一種方法類似,該方法的思路也是對 connect()「做手腳」,只是不通過劫持共享庫實現。只可惜目前 macOS 上由於系統安全性等原因,這類實現仍然處在探索階段。另外,我個人比較擔心(猜測)通過 ptrace 監視系統呼叫,效能是否會受到明顯影響。因此目前暫時不推薦。如果你有興趣的話,可以看看 graftcp 專案。

總結

以上是我知道的幾乎所有在 macOS 上配置代理的方法。個人比較推薦前幾種,既能夠覆蓋大多數場景,也不需要安裝額外的應用。如果你對網路方面感興趣,也不妨探索性地嘗試下其它方案。

本作品採用《CC 協議》,轉載必須註明作者和本文連結

Former WinForm and PHP engineer. Now prefer Golang and Rust, and mainly working on DevSecOps and Kubernetes.

相關文章