如何使用远程服务器作为本机的代理
这两年代理软件管得比较严,但 ChatGPT 对科研和学习又不可或缺。通常的解决办法是找更稳定的代理节点,也就是俗称的“机场”。不过,如果你手上有一台本身已经能访问外网的远程服务器(别管是怎么来的),也可以换一种思路:让本机通过 SSH 连接这台服务器,再把它作为本机的网络出口。这样一来,本机访问网站时,流量会先走到服务器,再由服务器访问目标网站。
如何使用远程服务器作为本机的代理
本文介绍的方法依赖一个前提:远程服务器本身已经可以访问目标网站,而本机可以通过 SSH 登录这台服务器。
这种服务器在企业、学校或实验室环境中并不少见,我就不详细讲了。
如果服务器已经配置好了外网访问能力,那么我们就可以利用 SSH 动态端口转发,在本机创建一个 SOCKS 代理入口,让本机流量经由这台服务器发出。
直观地说:
1 | 本机 |
因此,只要服务器能访问某个网站,本机也可以通过这个隧道,借由服务器访问它。
使用方法
假设远程服务器信息如下:
1 | 服务器 IP: 1.2.3.4 |
1. 在本机创建 SOCKS5 代理入口
在本机运行:
1 | ssh -N -D 127.0.0.1:1080 -p 2010 [email protected] |
这条命令会在本机创建一个 SOCKS5 代理入口:
1 | 127.0.0.1:1080 |
只要这个 SSH 连接保持不断,本机就可以通过 127.0.0.1:1080 把网络流量转发到远程服务器,再由服务器访问目标网站。
如果服务器已经写入 ~/.ssh/config:
1 | Host Tsinghua |
那么命令可以简化为:
1 | ssh -N -D 127.0.0.1:1080 Tsinghua |
2. 让 GUI 应用使用系统代理
对于 Chrome、Safari 等 GUI 应用,通常可以直接使用 macOS 系统代理。
打开 macOS 设置:
1 | System Settings |
开启:
1 | SOCKS Proxy |
填写:
1 | Server: 127.0.0.1 |
保存后,Chrome、Safari 等支持系统代理的应用通常就会使用这个代理。此时访问 ChatGPT、YouTube 等网站时,流量路径大致是:
1 | Chrome |
目标网站看到的出口 IP 是远程服务器的 IP,而不是本机的 IP。
3. 让终端程序使用代理
需要注意的是,macOS 系统代理通常不会自动作用于终端程序。例如:
codexbrewgit clonecurlpipnpm
这些程序更常见的代理配置方式是环境变量。
如果要让 Codex CLI 使用该 SOCKS 代理,可以这样启动:
1 | ALL_PROXY="socks5h://127.0.0.1:1080" \ |
例如,如果要启动 Codex App 并让它使用代理:
1 | ALL_PROXY="socks5h://127.0.0.1:1080" \ |
这里推荐使用 socks5h://,而不是 socks5://。区别是:socks5h 会把域名解析也交给代理侧处理,避免本机 DNS 解析失败或泄漏。
对于其他终端程序,也可以用同样的方式。例如:
1 | ALL_PROXY="socks5h://127.0.0.1:1080" git clone <repo-url> |
或者:
1 | ALL_PROXY="socks5h://127.0.0.1:1080" brew update |
4. 写成脚本
如果经常使用,可以把启动命令和环境变量写成脚本。例如可以写一个 codex-proxy,每次自动带代理启动 Codex。
这类脚本很简单,不必手写很久。把当前代理端口和目标程序告诉 AI,就可以直接生成。
不建议把代理环境变量无脑写进全局 ~/.zshrc,因为这样会影响所有终端程序,包括 ssh、git、conda、pip 等。更推荐为特定程序写单独的 wrapper。
5. 测试是否生效
测试 SOCKS 代理是否能访问目标网站:
1 | curl --socks5-hostname 127.0.0.1:1080 https://www.google.com |
测试当前出口 IP:
1 | curl --socks5-hostname 127.0.0.1:1080 https://ifconfig.me |
如果输出的是远程服务器的出口 IP,说明代理已经生效。
或者,你还可以用 https://whatismyipaddress.com/ 来查看在互联网眼中你的 IP。
原理
1. SSH 动态端口转发
这套方法的名字是 SSH Dynamic Port Forwarding,即 SSH 动态端口转发。核心命令是:
1 | ssh -N -D 127.0.0.1:1080 -p 2010 [email protected] |
其中,-D 127.0.0.1:1080 会:
- 把本机回环地址
127.0.0.1的1080端口实现为一个 SOCKS 代理入口。 - 让本机的 SSH client 监听
127.0.0.1:1080。
注意,ssh -D 只是创建了一个本地 SOCKS 代理,并不会自动接管所有网络流量。接下来,还需要让应用主动使用这个 SOCKS 入口。
2. 让应用使用代理
接下来,还需要让应用主动使用这个 SOCKS 入口。ssh -D 只是创建了一个本地 SOCKS 代理,并不会自动接管所有网络流量。实际使用时,可以把应用分成两类。
第一类:读取 macOS 系统代理的应用
这类应用会读取 macOS 的系统代理设置。Chrome、Safari 等 GUI 应用通常属于这一类。
因此,只要在 macOS 系统代理里设置:
1
SOCKS5 127.0.0.1:1080
这些应用访问 HTTP/HTTPS 网站时,通常就会把流量交给这个 SOCKS 代理,再经由 SSH 隧道转发到远程服务器。
第二类:不读取 macOS 系统代理的命令行工具
很多命令行工具不会自动读取 macOS 系统代理。它们更常使用 shell 环境变量,例如:
1
2
3ALL_PROXY
HTTP_PROXY
HTTPS_PROXY因此,对于
codex、brew、git clone、curl等终端程序,通常需要显式设置代理环境变量,例如:1
2
3
4ALL_PROXY="socks5h://127.0.0.1:1080" \
HTTP_PROXY="socks5h://127.0.0.1:1080" \
HTTPS_PROXY="socks5h://127.0.0.1:1080" \
codex这里的
socks5h表示把域名解析也交给 SOCKS 代理处理,通常比socks5更稳。
当某个应用(经过配置)实际使用 127.0.0.1:1080 作为 SOCKS5 代理后,它访问网站时不会直接连接目标网站,而是先连接本机的 SOCKS 入口。随后,应用会通过 SOCKS5 协议告诉 SSH client 自己想连接的目标地址,例如:
1 | chatgpt.com:443 |
SSH client 收到 SOCKS5 请求后,会在已有的 SSH 连接中打开一个 direct-tcpip channel,并把目标地址发送给远程服务器上的 sshd。随后,远程服务器上的 sshd 会从服务器侧发起到目标网站的 TCP 连接。
实际链路是:
1 | 本机应用 |
目标网站看到的连接来源是远程服务器,而不是本机。
3. 何为动态转发?
所谓“动态”,指的是 127.0.0.1:1080 这个本地入口是固定的,但每次 SOCKS5 请求中的目标地址可以不同。访问 ChatGPT 时,SSH 隧道转发到 ChatGPT;访问 YouTube 时,转发到 YouTube;访问 GitHub 时,转发到 GitHub。
这和普通 SSH 本地端口转发不同。普通 -L 转发需要提前写死目标地址,例如:
1 | ssh -L 8080:example.com:80 user@server |
而 -D 不需要提前指定目标网站,它会根据每次 SOCKS5 请求中的目标地址动态建立远程连接。
如果目标网站使用 HTTPS,那么数据传输中会有两层加密:
1 | 本机应用 <-> 目标网站:HTTPS/TLS 加密 |
远程服务器上的 sshd 只负责建立连接和转发字节流。它能看到要连接的目标地址和端口,但不会解密 HTTPS 内容。
1 |