上回书说道,我们通过 SSH 隧道 + gost 的组合,成功让内网机器A借助跳板机B访问了外网。但访问的是普通外网,遇到需要科学上网的场景(比如访问 Google、GitHub 下载资源等)还是会失败。本文在上篇基础上,进一步让机器A借助机器B上已有的代理服务实现科学上网。

前置条件: 本文方案依赖机器B上已配置好科学上网代理。如果机器B还没有配置,请先参考:【Linux】使用 Clash 科学上网

环境信息:

  • 机器A:内网机器,无外网,无 root 权限
  • 机器B(跳板机):172.22.26.185,用户名 tianlejin,本地 7890 端口运行着科学上网代理(HTTP 协议)

整体思路

两种模式对应两条链路,需要同时保持三个后台进程运行:

1
普通外网:机器A → gost(8118) → SSH隧道(1080, SOCKS5) → 机器B → 外网
1
科学上网:机器A → SSH隧道(7891) → 机器B的7890代理 → 科学上网

通过切换环境变量来选择走哪条链路,无需重启任何进程。

为什么普通外网需要 gost 而科学上网不需要? SSH -D 建立的是 SOCKS5 隧道,大多数工具不认 SOCKS5,需要 gost 转成 HTTP。而机器B的 7890 本身就是 HTTP 代理,直接映射过来就能用,不需要 gost 再转一次。


第一步:确认机器B上 7890 端口的代理协议

不同的科学上网工具(Clash、V2Ray 等)监听的协议可能不同,先确认是 SOCKS5 还是 HTTP:

1
2
3
4
5
# 测试 SOCKS5
ssh [email protected] "curl -x socks5://127.0.0.1:7890 https://www.google.com -o /dev/null -w '%{http_code}' 2>/dev/null"

# 测试 HTTP
ssh [email protected] "curl -x http://127.0.0.1:7890 https://www.google.com -o /dev/null -w '%{http_code}' 2>/dev/null"

返回 200302 说明协议正确(Google 返回 302 是正常的重定向行为,不是失败)。返回 000 说明协议不对,换另一个试。

本文场景中 7890 是 HTTP 代理协议。


第二步:启动三个后台进程

1
2
3
4
5
6
7
8
# 1. 普通外网隧道(SOCKS5)
ssh -D 1080 -N -f [email protected]

# 2. gost:将 SOCKS5(1080) 转为 HTTP(8118),供普通外网使用
~/software/gost -L http://:8118 -F socks5://127.0.0.1:1080 > /dev/null 2>&1 &

# 3. 科学上网隧道(把机器B的7890映射到本地7891)
ssh -L 7891:127.0.0.1:7890 -N -f [email protected]

验证三个进程都在跑:

1
2
3
ps aux | grep "ssh -D 1080" | grep -v grep
ps aux | grep gost | grep -v grep
ps aux | grep "ssh -L 7891" | grep -v grep

第三步:配置切换函数

创建脚本文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
mkdir -p ~/bash_scripts
cat > ~/bash_scripts/proxy.sh << 'EOF'
# 普通外网
proxy_normal() {
export ALL_PROXY=http://127.0.0.1:8118
export http_proxy=http://127.0.0.1:8118
export https_proxy=http://127.0.0.1:8118
export HTTP_PROXY=http://127.0.0.1:8118
export HTTPS_PROXY=http://127.0.0.1:8118
echo "普通外网已开启,走 8118"
}

# 科学上网
proxy_vpn() {
export ALL_PROXY=http://127.0.0.1:7891
export http_proxy=http://127.0.0.1:7891
export https_proxy=http://127.0.0.1:7891
export HTTP_PROXY=http://127.0.0.1:7891
export HTTPS_PROXY=http://127.0.0.1:7891
echo "科学上网已开启,走 7891"
}

# 完全关闭(机器A将无法访问外网)
proxy_off() {
unset ALL_PROXY http_proxy https_proxy HTTP_PROXY HTTPS_PROXY
echo "代理已关闭,无法访问外网"
}
EOF

然后在 ~/.bashrc 末尾添加一行 source:

1
echo 'source ~/bash_scripts/proxy.sh' >> ~/.bashrc

执行生效:

1
source ~/.bashrc

之后随时切换:

1
2
3
proxy_normal  # 切换到普通外网
proxy_vpn # 切换到科学上网
proxy_off # 关闭所有代理

验证

1
2
3
4
5
6
7
8
# 验证普通外网
proxy_normal
curl https://ifconfig.me # 返回机器B的公网IP

# 验证科学上网
proxy_vpn
curl https://ifconfig.me # 返回代理服务器的IP(与上面不同)
curl https://www.google.com -o /dev/null -w '%{http_code}' # 返回 200 或 302

用完清理

1
2
3
4
pkill -f "ssh -D 1080"
pkill -f "ssh -L 7891"
pkill -f gost
proxy_off

下次打开新终端时

三个后台进程只要没有被 kill 或机器重启,跨终端共享,不需要重复启动。每次新终端只需要检查进程是否存活,然后设置环境变量即可。

第一步:检查并恢复后台进程

1
2
3
4
5
6
7
8
# 检查普通外网隧道
ps aux | grep "ssh -D 1080" | grep -v grep || ssh -D 1080 -N -f [email protected]

# 检查科学上网隧道
ps aux | grep "ssh -L 7891" | grep -v grep || ssh -L 7891:127.0.0.1:7890 -N -f [email protected]

# 检查 gost
ps aux | grep gost | grep -v grep || ~/software/gost -L http://:8118 -F socks5://127.0.0.1:1080 > /dev/null 2>&1 &

第二步:选择模式(每个新终端必须执行)

1
proxy_normal  # 或 proxy_vpn

Q&A

Q:为什么普通外网需要 gost 而科学上网不需要?

SSH -D 建立的是 SOCKS5 隧道,但大多数工具只认 HTTP 代理格式的环境变量,不认 SOCKS5,所以需要 gost 在中间做一次协议转换。而机器B的 7890 本身就是 HTTP 代理,通过 SSH -L 映射到本地后可以直接使用,不需要 gost 再转一次。

Q:SSH 为什么不能直接建立 HTTP 隧道,只能建立 SOCKS5?

SSH 协议设计上只支持 -L(静态端口转发)和 -D(动态 SOCKS5 转发)两种模式,没有 HTTP 代理模式。原因是 SOCKS5 比 HTTP 代理更底层、更简单,只需要把数据从A搬到B,不需要理解数据内容。而 HTTP 代理需要解析请求头、处理 CONNECT 方法、理解 HTTP 语义,实现复杂得多。SSH 的核心职责是安全的远程登录和文件传输,内置一个完整的 HTTP 代理实现没有必要,所以干脆没做。这也是为什么需要 gost 来补这个缺口。


扩展阅读


作者:Claude Sonnet 4.6