让云服务器借用本机网络
场景:阿里云 ECS 需要访问外网(起初是
git pull失败),而本机(Windows)出网正常, 两台机器之间已有内网通道。本文记录为什么能借、怎么借、能推广到什么程度,以及踩过的坑。归档时间:2026-09-16
一句话:ssh -R 让 ECS 上多出一个通往本机的 SOCKS 口。
靠它既能拉代码,也能推广成「ECS 的全部出站流量都走本机」—— 只是覆盖面越大,代价越大。
一、问题现象
在 ECS 上执行 sudo git pull:
error: RPC failed; curl 16 Error in the HTTP2 framing layerfatal: expected flush after ref listingcurl 16 的典型含义是中间链路对 HTTP/2 支持有问题(代理、防火墙设备、或线路本身),
不一定是「被墙」。所以排查要分两层:先怀疑协议协商,再怀疑网络出口。
二、网络环境(这次的实际拓扑)
阿里云 ECS ──(Tailscale 内网)──> 本机 Windows ──(默认网关)──> 软路由 ──(透明代理)──> GitHub关键事实,逐条都实测确认过:
| 事实 | 怎么确认的 |
|---|---|
| 本机与 ECS 之间有 Tailscale 内网 | ipconfig 里看到 Tailscale 适配器,地址 100.70.180.65 |
| 本机不经任何代理就能直连 GitHub | curl --noproxy '*' -o /dev/null -w "%{http_code}" https://github.com → 200,约 1 秒 |
| 本机没有任何代理软件在监听 | 列全部监听端口 + 进程名,没有 clash / v2ray / verge 之类 |
| 代理其实跑在软路由上 | 本机默认网关指向软路由,是它做了透明代理 |
⚠️ 最容易走错的一步:以为「本机能上 GitHub」= 「本机有代理端口可以借」。 实际是透明代理在软路由上,本机只是流量经过它。别在本机上找代理端口, 找不到不是配置问题,是结构本来如此。
三、先试这两步(很可能不用配代理)
在 ECS 上按顺序执行:
# 1. 查有没有残留的代理设置(这台机器以前配过 127.0.0.1:10808)env | grep -i proxygit config --global --get-regexp proxy
# 2. 若有指向 127.0.0.1 但服务早已不存在的配置,先清掉unset http_proxy https_proxy HTTP_PROXY HTTPS_PROXYgit config --global --unset http.proxygit config --global --unset https.proxy
# 3. 让 git 退回 HTTP/1.1 —— 这一行经常直接解决问题git config --global http.version HTTP/1.1
sudo git pull如果通了,下面的都不用做。
四、主方案:SSH 反向隧道
原理
ssh -R 是在远端(ECS)监听一个端口。连到这个端口的流量,会被塞进已经建立好的
那条 SSH 连接送回本机,由本机发起真实的对外连接。
ECS 上的 git ──连 127.0.0.1:1080──┐ │ (这是 ECS 自己的回环地址) ↓ SSH 连接(早已建立,穿过 Tailscale) ↓ 本机 ssh 客户端代它发起连接 ↓ 本机 → 软路由 → GitHub步骤
① 在本机执行(窗口保持开着):
ssh -N -R 1080 -o ServerAliveInterval=30 -o ExitOnForwardFailure=yes <你的服务器别名>参数说明:
| 参数 | 作用 |
|---|---|
-N | 不执行远端命令,只做转发 |
-R 1080 | 在远端开一个 SOCKS5 监听(省略 host:hostport 即为动态转发) |
ExitOnForwardFailure=yes | 端口被占用时直接报错,而不是静默失败让你白排查 |
ServerAliveInterval=30 | 每 30 秒发心跳,防止中间设备掐掉空闲连接 |
② 在 ECS 上执行:
# 先验证隧道(应该返回 200)curl -s -o /dev/null -w "%{http_code}\n" --socks5-hostname 127.0.0.1:1080 https://github.com
# 再让这一次 pull 走隧道cd /path/to/your/reposudo git -c http.proxy=socks5h://127.0.0.1:1080 pull为什么 127.0.0.1 不用填本机 IP
这是最容易困惑的一点,答案有两层:
-
127.0.0.1指的是 ECS 自己。因为-R把监听开在 ECS 上, ECS 上的 git 连的是它自己的一个端口,跟本机地址无关。 -
不需要填本机 IP,因为那条 SSH 连接早就建立好了。
ssh <服务器别名>敲下去的那一刻,本机就已经穿过 Tailscale 找到 ECS 并完成握手。 反向转发只是复用这条现成的连接 —— 就像电话已经拨通,让对方把东西递过来,不用再拨一次号。
顺带一提:ECS 上 sshd 默认 GatewayPorts no,所以这个转发端口只绑回环、不绑 0.0.0.0。
好处是外网碰不到它,不会把家里的网络无意中暴露出去。
对照记忆:-L 还是 -R
| 参数 | 监听在哪 | 那个 127.0.0.1 属于谁 | 典型用途 |
|---|---|---|---|
-L(本地转发) | 本机 | 本机 | 本机访问远端的内网服务 |
-R(远程转发) | 远端 | 远端 | 让远端借用本机的网络出口 ← 本次 |
两个必踩的坑
1. sudo 会清掉环境变量。
export https_proxy=socks5h://127.0.0.1:1080sudo git pull # ✗ 这个变量传不进去,等于没配正确做法(任选其一):
sudo git -c http.proxy=socks5h://127.0.0.1:1080 pull # ✓ 一次性参数,最干净sudo -E git pull # ✓ 保留环境变量sudo git config --global http.proxy socks5h://127.0.0.1:1080 # ✓ 写进 root 的配置2. 用 socks5h:// 而不是 socks5://。
结尾那个 h 表示域名交给隧道另一端解析(也就是本机那边),
绕开服务器本地可能被污染的 DNS。这个字母在实际使用中差别很大。
用完清理
sudo git config --global --unset http.proxysudo git config --global --unset https.proxy五、推广到全流量:三种粒度
上面那条隧道本身就是一个完整的 SOCKS5 代理,出口在本机。 剩下的只是「让哪些东西用上它」——覆盖面从窄到宽有三档:
| 粒度 | 覆盖范围 | 适合 |
|---|---|---|
| 环境变量 | 只覆盖读它的程序 | curl / git / wget / pip / npm |
| proxychains | 所有 TCP 程序(含不读环境变量的) | apt / docker pull / 任意程序 |
| Tailscale exit node | 全部出站流量(TCP / UDP / ICMP) | 想彻底把它当第二张网卡用 |
5.1 环境变量(最简单,不用装东西)
export ALL_PROXY=socks5h://127.0.0.1:1080export http_proxy=socks5h://127.0.0.1:1080export https_proxy=socks5h://127.0.0.1:1080⚠️ sudo 会清掉这些变量 —— 要 sudo -E,或者改用各工具自己的配置
(git config / pip.conf / .npmrc)。
5.2 proxychains(强制任意程序走代理)
有些程序压根不读环境变量。proxychains 用 LD_PRELOAD 劫持 socket 调用,
不管程序愿不愿意都会走代理:
sudo apt install proxychains4# /etc/proxychains4.conf 最后一行改成:# socks5 127.0.0.1 1080# 并确认 proxy_dns 是打开的(让域名在隧道另一端解析)
sudo proxychains4 apt updatesudo proxychains4 docker pull nginx只覆盖 TCP,UDP 不行。
5.3 Tailscale exit node(全局,最彻底)
# ECS 上执行sudo tailscale up --exit-node=<出口节点的 Tailscale IP> --exit-node-allow-lan-access=true
# 撤销sudo tailscale up --exit-node=前提是把某台机器在 Tailscale 后台 批准成 exit node(该机器 → Edit route settings → 勾选 Use as exit node)。
--exit-node-allow-lan-access=true别省 —— 不然连局域网访问都会被一起绕走。
谁来当出口节点更好:软路由 > 你的电脑。 软路由本来就是网络出口,不依赖某台电脑常开;如果它上面已经跑着透明代理, 当 exit node 相当于把既有的出口直接分享出来。(OpenWrt / iStoreOS 都有现成的 tailscale 包。)
六、代价与风险
覆盖面越大,这三条越要放在心上。
6.1 你的家宽上行,就是服务器的上限
家庭宽带上行通常只有几十 Mbps(下行才是几百兆)。ECS 上 apt upgrade 或 docker pull
会明显慢,而且同时把你家网络占满 —— 你在家看视频会卡。
这条决定了它只适合小流量场景,不适合当下载通道。
6.2 本机一断,服务器就断
隧道依赖那个 SSH 窗口活着;exit node 依赖那台机器开机。笔记本合盖休眠 = 服务器断网。
好消息是 Tailscale 会自动给自身流量留例外,所以 ECS 不会把自己 SSH 进死 —— 这一点让它比手改默认路由安全得多。
⚠️ 千万别手动用
ip route把默认路由指向隧道。 那会把你自己的 SSH 连接也绕进去,当场自断,而且没有远程恢复的手段 (只能去阿里云控制台开 VNC)。这个坑值得单独记住。
6.3 只影响出站
exit node 管的是服务器主动发起的连接。nginx 对外提供服务(入站)不受影响; 但服务器上如果有程序要主动调外部 API(推送、回调、拉取),出口机一断就全失败。
6.4 一句合规提醒
把云服务器当作转发出口给多台设备用,可能触碰云厂商关于「利用云服务器提供代理服务」的限制条款。 用于自己的开发运维 —— 拉代码、装包、拉镜像这类 —— 一般没问题,但别把它做成通用出口分给别人。
七、兜底方案:不在服务器上 pull
只要本机能出网,这条路永远可用 —— 从本机把代码推过去,不需要服务器有任何出网能力:
cd /path/to/your/repotar czf - --exclude=.git --exclude=data --exclude=media --exclude=__pycache__ . \ | ssh <你的服务器别名> 'sudo tar xzf - -C /opt/your-app'ssh <你的服务器别名> 'sudo systemctl restart your-service'⚠️
data/和media/一定要排除,那里面是运行数据,不能被覆盖。
八、速查表
# ── 本机:开隧道(窗口保持开着)──ssh -N -R 1080 -o ServerAliveInterval=30 -o ExitOnForwardFailure=yes <服务器别名>
# ── ECS:验证隧道 ──curl -s -o /dev/null -w "%{http_code}\n" --socks5-hostname 127.0.0.1:1080 https://github.com
# ── ECS:走隧道 pull ──sudo git -c http.proxy=socks5h://127.0.0.1:1080 pull
# ── ECS:临时让所有命令走隧道(sudo 要加 -E)──export ALL_PROXY=socks5h://127.0.0.1:1080export https_proxy=socks5h://127.0.0.1:1080
# ── ECS:整台机器全走本机(需先在 Tailscale 后台批准)──sudo tailscale up --exit-node=<出口节点IP> --exit-node-allow-lan-access=truesudo tailscale up --exit-node= # 撤销
# ── 排查:先看协议和残留代理 ──git config --global http.version HTTP/1.1env | grep -i proxy一句话总结
ssh -R 1080让 ECS 上多了一个通往本机的 SOCKS 口; 那个口在 ECS 的127.0.0.1上,出口在本机 —— 所以不用填本机 IP, 因为承载它的 SSH 连接早就建立好了。想推广到全部流量,就把这个口接到
proxychains或 Tailscale exit node 上 —— 但记住:出口是你家的宽带,你电脑一断,服务器就断。