SSH 反向隧道远程访问内网开发机配置指南
开发环境放在内网是常见做法:公司云桌面、实验室服务器只能从内网访问,手机和外部电脑在没有 VPN 入口时无法连入。只要内网机器能访问公网,就有一条通用出路——让它主动出站,建立反向 SSH 隧道到一台公网 VPS,外部设备再通过 VPS 跳转进来。本文给出从 VPS 加固、隧道保活到手机客户端连接的完整配置流程,适合需要在移动设备上远程使用内网开发环境(例如挂着一个长期运行的终端会话或 AI 编程助手)的开发者。
架构
关键点在于连接方向:内网机器无法被动的入站连接触达,但它可以主动出站连到 VPS,并在 VPS 上开一个转发端口。外部设备先登录 VPS,再经这个端口到达内网机器。
端口规划:
| 端口 | 用途 | 监听位置 |
|---|---|---|
| 22 | VPS SSH 服务(外部连接 + 内网机建隧道) | VPS |
| 2222 | 反向隧道映射端口(指向内网机 22) | VPS,由内网机主动建立 |
一、VPS 配置
1.1 创建专用用户
sudo adduser jump
sudo usermod -aG sudo jump 1.2 配置 SSH Key 登录
本地生成密钥并上传:
ssh-keygen -t ed25519 -f ~/.ssh/jump_key -C "jump-vps"
ssh-copy-id -i ~/.ssh/jump_key.pub jump@your-vps-ip 1.3 配置 sshd
编辑 /etc/ssh/sshd_config:
Port 22
PermitRootLogin no
PasswordAuthentication no
AllowTcpForwarding yes
GatewayPorts clientspecified
ClientAliveInterval 60
ClientAliveCountMax 3 验证并重启:
sudo sshd -t && sudo systemctl restart sshd
sudo ss -tlnp | grep sshd 1.4 安装 fail2ban
公网 VPS 的 22 端口会持续收到扫描和爆破尝试,fail2ban 可以自动封禁多次失败的来源 IP:
sudo apt update && sudo apt install -y fail2ban
sudo systemctl enable --now fail2ban 如果 fail2ban 误封 IP,解封:
sudo fail2ban-client status sshd
sudo fail2ban-client set sshd unbanip 你的IP 1.5 防火墙放行
sudo ufw allow 22/tcp
sudo ufw enable 云厂商控制台的安全组也要放行 22 端口。
二、内网开发机配置
2.1 安装必要软件
# Node.js 22 LTS
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt-get install -y nodejs
# Claude Code
npm install -g @anthropic-ai/claude-code
# autossh(自动重连)
sudo apt install -y autossh
# tmux
sudo apt install -y tmux 2.2 配置 SSH Key(内网机到 VPS)
# 生成无密码密钥(用于自动化)
ssh-keygen -t ed25519 -f ~/.ssh/reverse_tunnel -N "" -C "intranet-desktop-tunnel"
# 上传公钥到 VPS
ssh-copy-id -i ~/.ssh/reverse_tunnel.pub jump@your-vps-ip
# 修复私钥权限(必须 600,否则 SSH 拒绝使用)
chmod 600 ~/.ssh/reverse_tunnel 踩坑: 私钥权限必须为 600,0664 等更开放的权限会导致 SSH 报 UNPROTECTED PRIVATE KEY FILE 错误并拒绝使用该密钥。
2.3 配置 SSH config(内网机需要代理出网时)
编辑 ~/.ssh/config:
Host jump-vps
HostName your-vps-ip
User jump
Port 22
IdentityFile ~/.ssh/reverse_tunnel
# 通过 HTTP 代理连接(如果需要)
ProxyCommand /usr/bin/ncat --proxy your-proxy:port --proxy-type http %h %p 不需要代理则去掉 ProxyCommand 那行。
测试连接:
ssh jump-vps echo "连接成功" 2.4 手动建立反向隧道
ssh -R 2222:localhost:22 -N jump-vps 参数说明:
-R 2222:localhost:22:在 VPS 上开 2222 端口,转发到内网机的 22 端口-N:不执行远程命令,只建隧道- 光标停住无输出 = 成功
踩坑: 反向隧道端口不能和 VPS 已有服务端口冲突,否则报 remote port forwarding failed。
在 VPS 上验证隧道:
ss -tlnp | grep 2222 2.5 systemd 自动保活
手动建立的隧道在断线后不会自动恢复,用 autossh + systemd 做成常驻服务。
创建 /etc/systemd/system/reverse-tunnel.service:
[Unit]
Description=Reverse SSH Tunnel to VPS
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=你的内网机用户名
ExecStart=/usr/bin/autossh -M 0 -N \
-o "ServerAliveInterval=30" \
-o "ServerAliveCountMax=3" \
-o "ExitOnForwardFailure=yes" \
-o "StrictHostKeyChecking=no" \
-i /home/你的内网机用户名/.ssh/reverse_tunnel \
-R 2222:localhost:22 \
jump-vps
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target 参数说明:
-M 0:关闭 autossh 自带的监控端口,改用 SSH 协议层的 keepalive 检测断线ServerAliveInterval=30/ServerAliveCountMax=3:每 30 秒发一次保活请求,连续 3 次无响应即判定断线、SSH 退出,由 autossh 负责重连ExitOnForwardFailure=yes:端口转发失败时直接退出(而不是挂着一个没有隧道的空连接),交给 systemd 重启
如果 SSH 需要代理,ExecStart 改为使用 -F 指定 config 文件:
ExecStart=/usr/bin/autossh -M 0 -N \
-o "ServerAliveInterval=30" \
-o "ServerAliveCountMax=3" \
-o "ExitOnForwardFailure=yes" \
-o "StrictHostKeyChecking=no" \
-F /home/你的内网机用户名/.ssh/config \
-R 2222:localhost:22 \
jump-vps 启动服务:
sudo systemctl daemon-reload
sudo systemctl enable --now reverse-tunnel
sudo systemctl status reverse-tunnel 踩坑: User= 必须是实际存在的用户名,不能留占位符,否则 systemd 报 exit code 217(USER,表示该用户不存在)。
2.6 配置 Claude Code
# API Key 认证
echo 'export ANTHROPIC_API_KEY="sk-ant-xxxxx"' >> ~/.bashrc
# 如果需要代理访问 Anthropic API
echo 'export HTTPS_PROXY="http://your-proxy:port"' >> ~/.bashrc
source ~/.bashrc 2.7 创建 tmux 工作会话
tmux new -s claude
cd ~/your-project
claude
# 退出 tmux:Ctrl+B 然后按 D
# 重新进入:tmux attach -t claude tmux 鼠标模式(写入 ~/.tmux.conf 永久生效):
set -g mouse on 三、手机端配置(Termius)
Termius 是 iOS/Android 上常用的 SSH 客户端,支持 SSH Key 管理与跳板连接。
3.1 导入 SSH Key
Keychain → + → Import Key → 导入 jump_key 私钥文件
3.2 配置 VPS 连接
| 字段 | 值 |
|---|---|
| Hostname | VPS IP |
| Port | 22 |
| Username | jump |
| Authentication | 选择导入的 Key |
3.3 配置内网机直达(ProxyJump)
新建 Host:
| 字段 | 值 |
|---|---|
| Hostname | localhost |
| Port | 2222 |
| Username | 内网机用户名 |
| Proxy Jump | 选择上面配好的 VPS 连接 |
需要先把 VPS 的 SSH 公钥添加到内网机的跳板配置:
# 在 VPS 上执行
ssh-keygen -t ed25519 -f ~/.ssh/intranet-desktop-key -N "" -C "jump-to-desktop"
ssh-copy-id -i ~/.ssh/intranet-desktop-key.pub -p 2222 内网机用户名@localhost 然后把 intranet-desktop-key 私钥也导入 Termius,内网机 Host 的 Authentication 选这个 Key。
四、日常使用流程
- 手机打开 Termius
- 点击内网机 Host(ProxyJump 自动跳转)
tmux attach -t claude- 开始远程开发
- 退出 tmux:
Ctrl+B然后按D - Claude Code 在内网机持续运行,随时回来继续
五、安全加固
5.1 反向隧道端口仅限本机访问
默认情况下(GatewayPorts no),sshd 把远端转发端口绑定在回环地址上,只有登录 VPS 的用户才能访问。由于 1.3 节配置了 GatewayPorts clientspecified(允许客户端指定绑定地址),建议在隧道参数里显式写明回环绑定,避免配置变化后端口意外暴露:
-R 127.0.0.1:2222:localhost:22 这样外部无法直连 2222,必须先登录 VPS 才能跳转。
5.2 内网机 SSH 加固
编辑 /etc/ssh/sshd_config:
PermitRootLogin no
PasswordAuthentication no
AllowUsers your-username 5.3 定期检查
# VPS 上查看隧道连接
ss -tlnp | grep 2222
# 查看登录记录
sudo last
sudo journalctl -u ssh --since "1 hour ago" 踩坑汇总
| 问题 | 原因 | 解决 |
|---|---|---|
| fail2ban 安装后 SSH 连接被拒 | IP 被误封 | fail2ban-client set sshd unbanip IP,或先停 fail2ban 排查 |
| 私钥报 UNPROTECTED PRIVATE KEY FILE | 私钥文件权限太开放 | chmod 600 ~/.ssh/reverse_tunnel |
| 反向隧道报 remote port forwarding failed | 端口被 VPS 已有服务占用 | 换一个未占用的端口 |
| systemd 报 exit code 217 | User 字段写的是占位符 | 替换为实际用户名 |
| tmux 滚轮变成方向键 | 鼠标模式未开启 | set -g mouse on 写入 ~/.tmux.conf |
结语
整套方案可以概括为三点:
- 方向反转:内网机不可入站,但可出站——反向隧道把访问方向倒过来,VPS 只做中转。
- 保活三件套:
autossh -M 0关闭独立监控端口、ServerAliveInterval检测断线、ExitOnForwardFailure保证空连接立即退出重启,三者配合让隧道在断网后能自愈。 - 最小暴露面:VPS 只开 22 端口、密钥登录 + fail2ban,隧道端口显式绑定回环地址——公网上暴露的攻击面只有一个加固过的 sshd。