SSH 反向隧道:让服务器通过客户端代理访问互联网
很多服务器不能直接访问互联网。常见原因是服务器处在内网、受到出口策略限制,或者云上环境没有配置公网出口。
常见处理方式包括给服务器装代理、修改防火墙、开公网出口或增加 NAT 网关。
这些办法都会改变服务器所在网络的出口结构。权限不够时往往无法操作,临时排障也显得过重。
如果客户端已经能访问互联网,而且本地有一个代理端口,例如 127.0.0.1:10808,还可以用 SSH 反向端口转发,把这个代理端口提供给服务器。服务器访问自己的 127.0.0.1:18080 时,流量会穿过 SSH 隧道,转到客户端的 127.0.0.1:10808。
下面用这组示例配置说明:
服务器地址:203.0.113.10
服务器 SSH 端口:2222
服务器 SSH 用户:ops
客户端本地代理地址:127.0.0.1
客户端本地代理端口:10808
服务器侧代理入口:127.0.0.1:18080
先从客户端运行:
ssh -p 2222 \
-R 18080:127.0.0.1:10808 \
ops@203.0.113.10
其中 -R 决定反向转发规则。
这条命令真正做了什么
先看完整结构:
ssh -p 2222 \
-R 18080:127.0.0.1:10808 \
ops@203.0.113.10
含义是:
-p 2222:连接服务器203.0.113.10的 SSH 端口2222ops@203.0.113.10:以用户ops登录服务器-R 18080:127.0.0.1:10808:在服务器侧监听18080,并把连接转发到客户端侧的127.0.0.1:10808
连接建立后,链路变成这样:
服务器上的程序
-> 127.0.0.1:18080
-> SSH 反向隧道
-> 客户端 127.0.0.1:10808
-> 客户端本地代理
-> 互联网
连接方向相反:服务器借用客户端的代理端口,客户端不会访问服务器的这个端口。
更准确地说,-R 的格式是:
-R [远端监听地址:]远端端口:本地地址:本地端口
所以:
-R 18080:127.0.0.1:10808
可以理解成:
服务器 127.0.0.1:18080 -> 客户端 127.0.0.1:10808
SSH 只负责转发 TCP 连接,不关心里面传输的是 HTTP 代理、SOCKS5 代理还是其他协议。客户端的 127.0.0.1:10808 上有可用代理时,服务器就能通过隧道使用它。
和 -L 的方向相反
更常见的正向端口转发是:
ssh -L 本地端口:目标地址:目标端口 user@server
它解决的是“当前电脑如何访问远端内网服务”。
比如把服务器内网里的 MySQL 映射到本机,这很常见。
反向端口转发的方向相反:
ssh -R 远端端口:本地地址:本地端口 user@server
它解决的是“远端服务器如何访问客户端能访问的服务”。
因此,-R 可以把客户端能访问的服务临时提供给远端服务器,适合排障或短期访问。
最小可用配置
假设你的客户端已经有一个本地代理:
127.0.0.1:10808
这个代理可能是 SOCKS5,也可能是 HTTP。先确认它在客户端可用。
如果是 SOCKS5 代理,可以在客户端测试:
curl --socks5-hostname 127.0.0.1:10808 https://ifconfig.me
如果是 HTTP 代理,可以测试:
curl -x http://127.0.0.1:10808 https://ifconfig.me
客户端代理可用后,从客户端连接服务器:
ssh -p 2222 \
-R 18080:127.0.0.1:10808 \
ops@203.0.113.10
登录服务器后,在服务器上测试:
curl --socks5-hostname 127.0.0.1:18080 https://ifconfig.me
如果你的本地代理是 HTTP 代理,就用:
curl -x http://127.0.0.1:18080 https://ifconfig.me
如果能返回公网 IP,说明服务器已经通过客户端代理访问互联网。
保持隧道运行
临时验证时,前面的命令已经够用。只建立隧道、不打开远程 shell,可以加上 -N:
ssh -N -p 2222 \
-R 18080:127.0.0.1:10808 \
ops@203.0.113.10
加上 ExitOnForwardFailure 后,隧道创建失败时 SSH 会直接退出:
ssh -N -p 2222 \
-o ExitOnForwardFailure=yes \
-R 18080:127.0.0.1:10808 \
ops@203.0.113.10
再加 keepalive,让 SSH 更快发现断线:
ssh -N -p 2222 \
-o ExitOnForwardFailure=yes \
-o ServerAliveInterval=30 \
-o ServerAliveCountMax=3 \
-R 18080:127.0.0.1:10808 \
ops@203.0.113.10
需要长期运行时,再配合 autossh 或 systemd user service。先验证链路,再配置守护进程。
让命令行工具使用这个代理
隧道建立后,服务器上会出现一个本地代理入口:
127.0.0.1:18080
如果客户端的 10808 是 SOCKS5 代理,服务器上的命令可以通过服务器侧的 18080 入口走代理:
curl --socks5-hostname 127.0.0.1:18080 https://github.com
也可以临时设置环境变量:
export ALL_PROXY=socks5h://127.0.0.1:18080
export HTTP_PROXY=socks5h://127.0.0.1:18080
export HTTPS_PROXY=socks5h://127.0.0.1:18080
这里使用 socks5h,这样域名解析也会交给代理端完成。否则服务器本身 DNS 不通时,请求仍会失败。
如果客户端的 10808 是 HTTP 代理,则在服务器上使用:
export HTTP_PROXY=http://127.0.0.1:18080
export HTTPS_PROXY=http://127.0.0.1:18080
Git 也可以单独配置:
git config --global http.proxy socks5h://127.0.0.1:18080
git config --global https.proxy socks5h://127.0.0.1:18080
用完后取消:
git config --global --unset http.proxy
git config --global --unset https.proxy
常见问题
服务器上访问 127.0.0.1:18080 失败
先确认 SSH 命令还在运行。反向端口转发依赖这条 SSH 连接,连接断了,服务器上的 18080 也就没了。
再检查服务器上端口是否存在:
ss -lntp | grep 18080
如果端口没有监听,可能是远端 SSH 服务不允许 TCP 转发。服务器的 sshd_config 里需要允许:
AllowTcpForwarding yes
修改后需要重载或重启 sshd。
远端端口被占用
如果服务器上已经有程序占用了 18080,可以换一个远端端口:
ssh -N -p 2222 \
-R 28080:127.0.0.1:10808 \
ops@203.0.113.10
这时服务器使用:
curl --socks5-hostname 127.0.0.1:28080 https://ifconfig.me
链路变成:
服务器 127.0.0.1:28080 -> 客户端 127.0.0.1:10808
curl 可以,git 或 npm 不行
这类情况通常说明隧道本身已通,问题在于工具没有使用代理,或者代理协议配置不对。
先用 curl 确认最小链路:
curl -v --socks5-hostname 127.0.0.1:18080 https://github.com
先用 curl 确认最小链路,再配置具体工具。这样更容易定位问题。
DNS 仍然失败
如果使用 SOCKS5,优先使用 socks5h:// 或 --socks5-hostname。这会把域名解析交给代理侧。
如果写成 socks5://,很多工具会先在服务器本地解析域名。服务器 DNS 不通时,代理还没开始工作,请求已经失败。
其他机器能不能访问服务器上的 18080
默认情况下,OpenSSH 会把反向端口绑定在服务器的 loopback 地址上,也就是服务器自己访问:
127.0.0.1:18080
这样只有这台服务器能访问代理,暴露面较小。
如果希望服务器所在网络的其他机器也访问这个反向代理,需要显式绑定地址,并且服务器 SSH 配置还要允许 GatewayPorts。例如:
ssh -N -p 2222 \
-R 0.0.0.0:18080:127.0.0.1:10808 \
ops@203.0.113.10
这会扩大暴露面。除非你非常清楚访问控制边界,否则不建议这么做。
安全边界
这类隧道会让服务器使用客户端的网络出口。
使用时至少要限制以下范围:
- 不要把远端监听地址开放到
0.0.0.0,除非你已经做好访问控制 - 不要在多人共享服务器上随便暴露代理端口
- 长期使用时,应改用具备正式出口、审计和权限控制的网络方案
- 不要忽略客户端代理的访问规则,服务器发出的请求最终会从客户端代理出口出去
它适合临时排障,或者让无公网出口的服务器完成一次受控的下载或 API 请求。
把连接方向理清楚
服务器访问互联网时,通常由服务器所在网络提供出口。使用反向隧道后,服务器的连接先到 SSH 转发,再从客户端的代理出口发出。
服务器不需要直接拥有公网出口,只要客户端的代理可用,就能通过已经建立的 SSH 连接访问外部服务。
正向转发让客户端进入远端网络,反向转发则让远端服务器使用客户端能访问的服务。
遇到临时网络问题时,可以先建立反向隧道验证请求链路,再决定是否需要改造防火墙、NAT 或网关。
