跨地区访问内网服务器时,Tailscale、RustDesk 等工具通常会先尝试 UDP 打洞。打洞失败后,流量会转发到公共中继节点,连接虽然仍然可用,但路径不可控,延迟也可能明显增加。本文使用一台网络质量较好的 VPS,通过反向 SSH 隧道建立一条固定的 TCP 链路,使 SSH 和远程桌面流量都按照“本地设备 → VPS → 内网服务器”的路径传输。
graph LR
A[本地 Mac] -->|SSH| B[中转 VPS]
C[内网服务器] -->|持久反向 SSH 隧道| B
B -->|22022 → SSH 22| C
B -->|23389 → RDP 3389| C实现原理
内网服务器通常不能接受公网入站连接,但可以主动访问互联网。我们让内网服务器主动连接 VPS,并通过 SSH 的 -R 参数在 VPS 上创建两个反向端口:127.0.0.1:22022 对应内网服务器的 SSH 端口 22,127.0.0.1:23389 对应 RDP 端口 3389。SSH 连接建立后,VPS 收到的本地端口流量会通过已经存在的加密连接送回内网服务器。
1
2
3
4
| ssh -NT \
-R 127.0.0.1:22022:127.0.0.1:22 \
-R 127.0.0.1:23389:127.0.0.1:3389 \
tunnel@<VPS_IP>
|
其中,-N 表示不执行远程命令,-T 表示不分配终端,-R 用于建立反向端口转发。两个端口都绑定到 VPS 的 127.0.0.1,因此只能由 VPS 本机访问,不会直接暴露到公网。
配置专用用户
首先在 VPS 上创建一个只用于维持隧道的用户,并锁定其密码登录:
1
2
3
| sudo useradd --create-home --shell /bin/bash tunnel
sudo passwd -l tunnel
sudo install -d -o tunnel -g tunnel -m 700 /home/tunnel/.ssh
|
然后在内网服务器生成一对专用 SSH 密钥:
1
2
3
4
5
6
| sudo mkdir -p /root/.ssh
sudo chmod 700 /root/.ssh
sudo ssh-keygen -t ed25519 \
-f /root/.ssh/id_ed25519_hk_tunnel \
-N '' \
-C 'lab-to-vps-tunnel'
|
将公钥写入 VPS 的 /home/tunnel/.ssh/authorized_keys。为了避免该密钥被用于其他操作,可以限制它只能建立指定的反向端口:
1
| restrict,port-forwarding,permitlisten="127.0.0.1:22022",permitlisten="127.0.0.1:23389" ssh-ed25519 <PUBLIC_KEY>
|
最后修正文件权限:
1
2
| sudo chown tunnel:tunnel /home/tunnel/.ssh/authorized_keys
sudo chmod 600 /home/tunnel/.ssh/authorized_keys
|
保持隧道在线
手动运行 SSH 只适合测试,长期使用可以把它交给 systemd 管理。在内网服务器创建 /etc/systemd/system/vps-tunnel.service:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| [Unit]
Description=Persistent reverse SSH tunnel to VPS
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=root
ExecStart=/usr/bin/ssh -NT -i /root/.ssh/id_ed25519_hk_tunnel -o BatchMode=yes -o ExitOnForwardFailure=yes -o ServerAliveInterval=20 -o ServerAliveCountMax=3 -R 127.0.0.1:22022:127.0.0.1:22 -R 127.0.0.1:23389:127.0.0.1:3389 tunnel@<VPS_IP>
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
|
启用服务后,隧道会随系统启动,并在网络中断后自动重连:
1
2
3
| sudo systemctl daemon-reload
sudo systemctl enable --now vps-tunnel.service
sudo systemctl status vps-tunnel.service
|
验证隧道
在 VPS 上检查监听端口:
1
2
3
| ss -lnt | grep -E '22022|23389'
nc -vz 127.0.0.1 22022
nc -vz 127.0.0.1 23389
|
如果两个端口均显示为开放,说明 VPS 已经能够通过反向隧道访问内网服务器的 SSH 和 RDP 服务。
使用 SSH
本地设备可以使用 ProxyJump 登录 VPS,再从 VPS 的回环端口进入内网服务器:
1
| ssh -J <VPS_USER>@<VPS_IP> -p 22022 <SERVER_USER>@127.0.0.1
|
为了方便日常使用,可以将下面的内容写入本地 ~/.ssh/config:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| Host hk-vps
HostName <VPS_IP>
User <VPS_USER>
IdentityFile ~/.ssh/id_rsa
Host lab-server
HostName 127.0.0.1
Port 22022
User <SERVER_USER>
ProxyJump hk-vps
HostKeyAlias lab-server-through-vps
Host lab-rdp
HostName <VPS_IP>
User <VPS_USER>
IdentityFile ~/.ssh/id_rsa
LocalForward 13389 127.0.0.1:23389
ExitOnForwardFailure yes
ServerAliveInterval 20
ServerAliveCountMax 3
|
IdentityFile 指向本地设备上用于登录 VPS 的 SSH 私钥,而不是以 .pub 结尾的公钥。如果 ~/.ssh 中已有 id_rsa 和 id_rsa.pub,这里应填写 ~/.ssh/id_rsa;如果使用 Ed25519 密钥,则填写 ~/.ssh/id_ed25519。首次使用前,需要把对应的公钥复制到 VPS:
1
| ssh-copy-id -i ~/.ssh/id_rsa.pub <VPS_USER>@<VPS_IP>
|
如果 macOS 没有安装 ssh-copy-id,也可以执行:
1
| cat ~/.ssh/id_rsa.pub | ssh <VPS_USER>@<VPS_IP> 'umask 077; mkdir -p ~/.ssh; cat >> ~/.ssh/authorized_keys'
|
完成后测试密钥登录:
1
| ssh -i ~/.ssh/id_rsa <VPS_USER>@<VPS_IP>
|
如果不再要求输入 VPS 账号密码,说明密钥已经配置成功。私钥 id_rsa 只保存在本地,不能上传或发送给其他人;复制到 VPS 的只能是 id_rsa.pub。
之后只需执行 ssh lab-server。这等价于 ssh -J <VPS_USER>@<VPS_IP> -p 22022 <SERVER_USER>@127.0.0.1,VS Code Remote-SSH、Cursor 和 SFTP 也可以直接使用 lab-server。
使用远程桌面
RDP 端口没有暴露到公网,因此需要先在本地建立一层 SSH 本地转发。如果已经添加了上面的 lab-rdp 配置,执行:
该命令会读取 LocalForward 13389 127.0.0.1:23389,等价于下面的完整命令:
1
2
3
4
| ssh -NT \
-o ServerAliveInterval=20 \
-L 13389:127.0.0.1:23389 \
<VPS_USER>@<VPS_IP>
|
保持终端运行,然后使用 RDP 客户端连接 127.0.0.1:13389。此时流量依次经过本地端口 13389、VPS 端口 23389 和内网服务器端口 3389,整个过程都位于 SSH 加密通道内。