概览

实现 SSH 免密登录,并让云服务器流量转发到本地端口,通过代理再返回。

免密登录

打开 ~/.ssh/config,写入:

1
2
3
4
Host <server ip>
HostName <server ip>
User root # 默认为 root 用户
Port 22 # SSH 默认端口

打开 Cursor 的 Remote Explorer,会看到多了 <server ip> 这个选项。

现在配置免密

  1. 本机生成密钥

    1
    ssh-keygen -t ed25519  # 随便取一个文件名
  2. 公钥写入服务器

    1
    ssh-copy-id ~/.ssh/id_ed25519.pub <user>@<server-ip>
  3. 验证免密登录

打开 Cursor 的 Remote Explorer,直接点击配置的 <server ip>;也可以使用命令行:

1
ssh <user>@<server-ip>

端口转发与代理

原理

建立与云服务器的 SSH 正向隧道后,再搭建一条反向隧道(云服务器 -> 本机)。以下以 “云服务器 1080 端口 ↔ 本机 7897 端口” 为例:

  • 云服务器访问外网时,将流量统一发到 1080 端口。
  • 反向隧道把 1080 端口的流量转发到本机 7897。
  • 本机 7897 端口交给代理软件,按节点分发,再把结果回传给云服务器。

实现

提供三种实现方法,实现方法三选一即可, 但是前置步骤要完成

前置步骤

  1. 在代理软件的端口设置中开启混合端口转发(以 7897 为例)。
  2. 在云服务器中配置环境变量,统一走 1080 端口。编辑 ~/.bashrc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 检查代理是否可用并自动选择代理或直连
PROXY_SERVER="127.0.0.1"
PROXY_PORT="1080"

# 测试代理是否可用
if curl -x socks5://$PROXY_SERVER:$PROXY_PORT -s --head --max-time 5 https://www.google.com | grep "HTTP/1.1 200 OK" > /dev/null 2>&1; then
echo "代理可用,使用代理"
export ALL_PROXY=socks5://$PROXY_SERVER:$PROXY_PORT
export HTTP_PROXY=$ALL_PROXY
export HTTPS_PROXY=$ALL_PROXY
else
echo "代理不可用,切换到直连"
unset ALL_PROXY
unset HTTP_PROXY
unset HTTPS_PROXY
fi

保存后运行 source ~/.bashrc 使其生效。

  1. 编辑 /etc/ssh/sshd_config,将以下参数取消注释并设为 yes
    • AllowTcpForwarding:允许 SSH 客户端发起本地/远程端口转发。
    • GatewayPorts:远程端口转发可绑定 0.0.0.0/::
    • PermitTunnel:允许基于 SSH 的三层隧道。

三种方法

注: 三种方法在云服务器上绑定的端口都是 1080, 最好用完一个方法就关闭, 而不是一起使用, 否则可能造成端口冲突

  1. 写入本地 SSH 配置
    ~/.ssh/config 中(基于免密配置)写入:
1
2
3
4
5
6
7
8
Host <server ip>
HostName <server ip>
User root
Port 22
# 追加下面三条即可
RemoteForward 1080 localhost:7897
ServerAliveInterval 30
ServerAliveCountMax 3

写好后,Cursor 远程连接服务器时会自动启动反向隧道。

  1. 使用脚本与工具保持隧道

    • 普通 SSH,一旦关闭终端会话就会断开:
    1
    2
    ssh -M 0 -f -N -v -o ServerAliveInterval=30 -o ServerAliveCountMax=3 \
    -i ~/.ssh/<key file name> -R 1080:localhost:7897 <user>@<server ip>
    • 使用 autossh 可以保持连接,先通过 Homebrew 安装:
    1
    brew install autossh

    再写脚本方便启动和提示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 文件名 start_ssh_tunnel.sh
autossh -M 0 -f -N -v -o ServerAliveInterval=30 -o ServerAliveCountMax=3 -i ~/.ssh/<key file name> -R 1080:localhost:7897 <user>@<server ip>

if [ $? -eq 0 ]; then
echo -e "\033[32m✅ 隧道启动成功!\033[0m"
echo -e "📌 验证方法:"
echo -e " 1. Mac 端查看进程:ps aux | grep autossh"
echo -e " 2. 服务器端查看端口:ss -tulnp | grep :1080"
echo -e " 3. 关闭隧道:pkill autossh"
else
echo -e "\033[31m❌ 隧道启动失败!\033[0m"
echo -e "🔍 可能原因:"
echo -e " 1. autossh 未安装(执行 brew install autossh)"
echo -e " 2. 密钥路径错误(当前路径:~/.ssh/id_rsa)"
echo -e " 3. 服务器 1080 端口被占用"
fi

后续操作:

1
2
3
4
5
6
7
8
# 打开端口转发
./start_ssh_tunnel.sh

# 关闭端口转发
pkill autossh

# 查看 autossh 相关进程(含 grep 本身)
ps aux | grep autossh
  1. Termius 配置端口转发
    在 Termius 左侧 Port Forwarding 中新建规则:

    • Remote host: <server ip>
    • Remote port: 1080
    • Bind address: 127.0.0.1
    • Destination address: 127.0.0.1
    • Destination port: 7897

    没搞懂 Bind address 和 Destination address 的区别
    不过确认 Termius 显示 From <server ip>:1080 to 127.0.0.1:7897 即表示已连通,启用后再连接服务器即可完成转发。

测试

测试要使用 curl, 而不能使用 ping
因为 ping 基于 ICMP 协议, 运行在网络层,而我们配置的 SSH 端口转发 + 代理仅支持 TCP/UDP 协议,ICMP 数据包无法通过代理隧道转发

1
2
3
4
5
# 请求国内网络
curl -v https://baidu.com

# 请求国外网络
curl -v https://google.com