工程手记

不装远控软件,从 Mac 进客户的 Windows

通过 SSH 隧道 + VPS 中继接客户 Windows,支持 10 并发、一次性证书、5-10 秒快速连接。

客户机在 Windows、开发机在 Mac,现场排障需要上机。RDP 和商业远控工具都太重——要客户配合打开、有安装门槛、按并发收费。我用 SSH 隧道解决这个问题,核心思路是客户运行一个通用 BAT(自动获取连接码),通过 VPS 反向隧道连我的 Mac,然后我用 ProxyJump 直连到他的 PowerShell。这套工具支持 10 个客户并发,用一次性 ed25519 证书做身份认证,OpenSSH 缓存把首次安装时间从 30-60 秒压到 5-10 秒。

架构:VPS 中继 + 反向隧道 + ProxyJump

整个系统分三端。客户 Windows 上运行 BAT,通过 SSH 建立反向隧道到 VPS(隧道用户只有端口转发权限,无 shell 访问);VPS 负责分配连接码和证书,维护实时会话面板;我的 Mac 上配置好 SSH 别名,一条命令 ssh cust-<code> 就能跳转到客户机。

┌─────────┐              ┌─────────────────────────────────┐              ┌──────────────┐
│   Mac   │              │       VPS Relay (中转站)        │              │   Customer   │
│         │              │                                 │              │   Windows    │
│  ssh    │──ProxyJump──►│ Web Panel  (Node 20 + Vue 3)    │◄── HTTPS ────│  Universal   │
│cust-XXX │              │   ↓ /api/allocate (cert)        │              │   BAT        │
│         │              │   ↓ /api/sessions (kill/list)   │              │              │
│         │              │   ↓ /ws (realtime push)         │              │              │
│         │              │ sshd  reverse port pool         │              │              │
│         │              │   12100~12199 (cert-only auth)  │◄ ssh -R 12X ─│              │
│         │              │ nft per-port counters → traffic │              │              │
│         │              │ SQLite (sessions+audit+samples) │              │              │
└─────────┘              └─────────────────────────────────┘              └──────────────┘

流程是这样的:

  1. 客户双击 远程调试-连接.bat
  2. BAT 生成一次性 SSH 密钥对,调用 POST https://中转站/api/allocate
  3. 服务器返回三位连接码(例如 147)、对应端口(12147)、签发的证书、工程师公钥、VPS 主机公钥
  4. BAT 自动安装或启动 OpenSSH Server,把工程师公钥写入 authorized_keys
  5. BAT 用证书建立反向隧道:ssh -i key -o CertificateFile=cert -R 12147:127.0.0.1:22 tunnel@中转站
  6. BAT 显示连接码给客户("您的连接码: 147")
  7. 客户告诉支持这个数字,我在 Mac 上运行 ssh cust-<code>
  8. 连接实时出现在 Web 面板上(代码、IP、流量、时长)
  9. 客户关闭 BAT 窗口或我在面板点击"关闭",隧道断开

设计点一:动态连接码 + 端口池,支持 10 并发

三位数连接码(100199)是为了让客户容易记住和口头传达。服务器维护一个端口池 1210012199,每次客户请求时随机分配,确保 10 个客户可以并发连接而不冲突。

请求 /api/allocate 时,服务器:

  • 检查当前空闲端口
  • 返回 code(100~199 之间随机)和对应的 port(12100+code)
  • 这个分配关系存进 SQLite,关联客户 IP、时间戳、隧道建立状态

如果 10 个端口都满,新客户会得到 HTTP 503 错误和"pool_full"错误码,这是硬限制的立即拒绝。

设计点二:一次性 ed25519 证书,5 分钟有效且绑定源 IP

传统做法是给每个客户一个长期 SSH 密钥,密钥泄露风险大。这里改成 OpenSSH 证书认证:

  • 客户 BAT 生成临时 ed25519 密钥对(仅在 BAT 运行时内存中存在)
  • 客户把公钥和源 IP 发给服务器的 /api/allocate 接口
  • 服务器签发一张证书:标明这个公钥只在这次连接有效、5 分钟过期、源 IP 绑定
  • BAT 用证书建立隧道

OpenSSH 证书的威胁模型是:

  • 能保护的:密钥长期泄露风险消除了(每次用完即弃);隧道被劫持后,拦截者只能在 5 分钟内、同一源 IP 范围内冒充这个客户,其他时间段无法复用
  • 不能保护的:如果源 IP 被欺骗(ARP 欺骗、BGP 劫持)、或 VPS 时钟不准(偏差超过 5 分钟),证书校验可能失败或被绕过;BAT 文件本身被篡改(在传输或客户机上被修改),工程师公钥被替换,我连接时会连到恶意机器而不是客户机

证书的有效期使用 OpenSSH ssh-keygen -V +300s 格式,依赖客户机和服务器的系统时钟一致。如果两端时钟相差超过几分钟,OpenSSH 会因时间戳不在有效范围内而拒绝证书。系统没有显式的时钟容差机制,NTP 同步精度需要控制在分钟级以内,IPv6 或 VPN 环境下源 IP 绑定的有效性 待补:取决于 VPN 方案是否保留客户真实 IP

设计点三:隧道账户仅端口转发,无 shell 权限

VPS 上创建一个专用账户 tunnel,SSH 配置中限制它:

# sshd_config 关键配置
Match User tunnel
  AllowAgentForwarding no
  AllowTcpForwarding yes
  PermitListen 127.0.0.1:12100-12199
  PermitOpen 127.0.0.1:12100-12199
  PermitTTY no
  AllowUsers tunnel
  ForceCommand /bin/false

这样即使客户的密钥(或我的长期密钥)泄露,攻击者登陆 tunnel 账户最多只能建立反向端口转映射,无法获得 shell、无法读写文件系统、无法执行命令。双重保护:一是账户本身没有 shell,二是 sshd 配置显式禁止了 TTY 和代理转发。

这里的威胁模型假设:VPS 的 sshd 配置被信任,未被篡改;客户 Windows 机器上没有被植入远控木马(BAT 文件下载、执行、隧道运行的全过程都可能被监控)。如果客户机本身被入侵,这套机制无法保护 VPS 或我的 Mac。

设计点四:OpenSSH 预缓存,5-10 秒装好而不是 30-60 秒

Windows 内置的 OpenSSH Server 需要从 Windows Update 下载,通常要 30-60 秒。方案是在 VPS 上预先缓存一份 OpenSSH-Win64.zip(~50MB),BAT 运行时优先从本地 VPS 的 HTTP 服务(18022 端口)下载,失败才降级到 Windows Update。

VPS 侧的一次性准备:

sudo bash server/prepare-openssh-cache.sh

这个脚本:

  • 从 GitHub PowerShell/Win32-OpenSSH releases 官方源下载 OpenSSH-Win64.zip
  • 启动 HTTP 服务在 18022 端口,提供这个 ZIP
  • 不做显式的哈希校验,而是依赖 Windows 对 ZIP 文件内签名的验证(Windows 的 Add-WindowsCapability 内置验证机制)

客户 BAT 的逻辑:

  • 尝试从 VPS http://中转站:18022/OpenSSH-Win64.zip 下载(网络好时 5-10 秒)
  • 下载失败或超时则降级到 Windows Update(30-60 秒)
  • 无论哪种方式都自动继续,用户不需要干预

这个优化需要几个前提条件生效:

  • VPS 和客户机的网络连接要稳定(下载中断会回源 Windows Update,反而更慢)
  • VPS 和客户机在同一地域最佳(跨域 50MB 可能需要 20-30 秒,降级意义不大)
  • HTTP 18022 端口要对客户机开放(如果防火墙限制只有 22/443,这个缓存无法使用)

素材里给了性能表:

场景时间 (v1.0)时间 (v1.1)加速
首次安装(网络好)30-60s5-10s3-10x
首次安装(网络慢)2-3 min30-60s2-5x
已安装0.5s0.5s相同

测量条件 待补:网络"好"和"慢"的具体定义、客户端机型、测试样本数量。现有数据支撑的结论是:新客户首次连接,这个优化能缩短 60-75% 的等待时间(从 60s 到 10s 这个最好情况);但不能保证 100% 稳定达成(取决于网络波动)。

Web 管理面板:实时掌握会话状态

VPS 上跑的 Node 20 + Vue 3 前端,可以访问 https://中转站(配置 Let's Encrypt 证书)。面板功能:

  • 实时会话列表:显示当前在线客户的代码、IP、连接时长、流量统计
  • 强制断开:点击"关闭"按钮立即切断隧道
  • 历史记录:已结束的所有会话,记录开始/结束时间、流量、断开原因(主动关闭 vs 网络中断 vs 空闲超时)
  • 审计日志:Web 面板登陆尝试、谁强制断开了谁、异常操作

数据存储在 VPS 的 SQLite 里,包括 sessions 表(会话元数据:代码、端口、客户 IP、开始/结束时间、流量总计)、audit 表(操作日志)、traffic_samples 表(按时间采样的流量数据:会话 ID、采样时刻、接收字节、发送字节)。

流量统计通过 nft(Linux 内核防火墙规则)的计数器实现,每个端口 12100~12199 对应一个计数规则,实时汇总给 Web 后端。

登陆认证是用户名 + 密码(在 install-panel.sh 时设置)。

部署:4 个步骤完成全套安装

第一步:生成隧道密钥(Mac 侧)

ssh-keygen -t ed25519 -f keys/customer_tunnel_key -N "" -C "customer-tunnel"

第二步:配置 VPS 隧道和 sshd

ssh root@中转站
git clone <仓库地>
cd sshts
sudo bash server/setup-vps.sh keys/customer_tunnel_key.pub

这会创建 tunnel 账户、配置 sshd、启用反向隧道、写入 authorized_keys。

第三步:安装 Web 面板(v1.2+)

sudo DOMAIN=中转站 bash server/install-panel.sh

脚本会提示输入:

  • 工程师 SSH 公钥(会被嵌入所有 BAT)
  • Web 面板管理员用户名和密码

然后自动安装 Node 20、编译 Vue 3 前端、配置 systemd 服务、Nginx + Let's Encrypt。

第四步:配置 Mac 的 SSH 别名(每个工程师一次)

WIN_USER=Administrator bash mac/install-ssh-config.sh

这会在 ~/.ssh/config 里添加 100 条记录:cust-100cust-199,每个都用 ProxyJump=中转站,自动登陆指定的 Windows 管理员。

之后,把 client/远程调试-连接.bat 发给客户(通用的,不需要填任何参数),客户一运行就能连接。

限制和取舍

  • 支持 10 并发:这是硬限制,对应 VPS 单机性能(待补:性能上限如何测试、是否会因为某个客户的高流量拖累其他客户)。
  • 仅支持 Windows 10 1809+ 或 Windows 11:早期 Windows 版本的 OpenSSH 行为不兼容,素材里明确要求 Administrator 账户。
  • 需要 Administrator 权限:BAT 要写 SSH 密钥到系统目录、启动 sshd 服务,非 Admin 用户无法运行。BAT 会自动检测管理员账户,优先当前用户,降级到 Administrator(需要 UAC 弹窗确认)。
  • 证书有效期 5 分钟:这是为了限制泄露风险,代价是客户机上的系统时钟如果跟服务器相差超过几分钟,连接会失败。系统依赖两端时钟同步,无显式容差机制。
  • Web 面板仅支持单一 admin 账户:系统只有一个管理员用户,通过用户名+密码认证。所有认证用户都拥有完整权限(查看所有会话、断开任何连接、查看审计日志),不支持按角色分配权限或只读账户。

关键点

这套工具解决的是客户排障的第一步——快速、轻量地建立远程访问,而不需要客户安装 TeamViewer、配置 RDP、开放额外端口。支持 10 并发、一次性证书,OpenSSH 缓存把安装时间从 30-60 秒压到 5-10 秒,意味着哪怕客户现场网络不稳定、客户 IT 管理严格,也能以最小摩擦快速上机取证。没有这套工具,那些现场的故障根本无法追踪——光是等待客户开 RDP 就要 10 分钟,OpenSSH 缓存的优化才让 5-10 秒的快速连接成为现实。

星野的头像

星野 XINGYE

一个人维护 AI 平台的工程师。这里记录 63 篇复盘:18 份故障档案、OTA、架构演进与工作流。