← 返回首页

自建远控体系拆解:RustDesk 工作原理与所有需要配置的地方

〇、这套系统是什么

一句话:一台 VPS 上跑 RustDesk 的信令服务(hbbs)和中继服务(hbbr),内网机器做被控端,我在任何地方的控制端凭 ID + 密码接入。内网机器不需要任何端口映射、不需要公网 IP——这是选 RustDesk 而不是传统远控的核心原因:被控端是主动向外注册的,防火墙只放行出站就够。

三个角色分工明确:

  • 服务器(VPS,Docker 部署):hbbs 负责信令、ID 注册、打洞协调;hbbr 负责打洞失败时的中继转发。它只经手加密数据,看不到画面内容。
  • 被控端:内网里那台要被远程的机器,装 Windows 服务,无人值守,开机自动在线。
  • 控制端:我自己坐着的电脑或手机,只发起连接。公司电脑上这份客户端还需要绕过按进程名识别的防火墙——后面会讲怎么不重编译就改掉进程名。
架构总览

一、工作原理:一次连接是怎么建立的

连接时序

整个流程分五步,前四步是信令(走 21116),最后一步才是画面数据:

  1. 注册:被控端启动后向 hbbs 的 21116 端口(UDP)注册,领到一个固定 ID,之后周期性心跳保活。注意心跳走 UDP——想用 TCP 连接数判断客户端在线是查不到的,要看服务端日志。
  2. 发起:控制端输入对方 ID 点连接,请求到达 hbbs。
  3. 协调:hbbs 查注册表,把双方的公网地址:端口互相下发给两端。
  4. 打洞:两端同时向对方的公网地址发包,在两边 NAT 上各凿一个洞。
  5. 通路二选一:打洞握手成功 → P2P 直连,数据不过服务器,流量费为零;失败 → 自动回落到 hbbr 中继(21117),所有画面数据经服务器转发。
打洞成败基本由 NAT 类型决定:锥形 NAT 大概率成功,对称 NAT(很多手机热点、公司网络)几乎必败。我这套体系里被控端就是对称 NAT,所以「促成直连」的所有努力都白费了,中继是常态——这直接决定了后面限速和压码率两个配置的分量。

另外两个容易误解的点:

  • 画面数据是端到端加密的,中继服务器只转发密文;
  • 直连和中继的切换是自动的,界面上只会显示连接状态,要看真实通路得看服务端日志里 paired 双方是谁。

二、服务端:必须显式写的那些参数

用 rustdesk-server-s6 镜像,单容器同时管 hbbs 和 hbbr。别用 scratch 精简镜像——实测它不认 RELAY、ENCRYPTED_ONLY 这些环境变量,写了等于没写。

环境变量(全部实测有效):

  • ENCRYPTED_ONLY=1:整个部署里最重要的一条。hbbr 默认不校验密钥,官方文档原话是空 key 等于允许任何无密钥客户端使用中继——意思是公网上任何人拿官方客户端把中继地址填成你的,就能白嫖你的流量。必须显式开启。
  • RELAY=<你的服务器>:21117:告诉客户端中继在哪。
  • TEST_HBBS=no:服务器是 1:1 NAT(内网私有地址)时,hbbs 启动自检会报 UDP 错误刷日志,此变量关闭自检。
  • LIMIT_SPEED / SINGLE_BANDWIDTH / TOTAL_BANDWIDTH / DOWNGRADE_START_CHECK:限速三件套 + 降级判定时长。单路带宽上限、总硬上限、持续跑满后压到的速率、多久开始降级。限速不只是省钱,是安全措施——只要有人知道你的服务器地址,几十行脚本就能把带宽和流量套餐吃干净。
存储与网络:
  • 数据目录用 bind mount(比如 /opt/rustdesk/data),容器怎么重建密钥都不变;密钥丢了所有客户端都要重新配。
  • restart: always,配合 systemd 的 docker 服务实现开机自愈。
  • 端口:放行 TCP 21115 / 21116 / 21117 + UDP 21116。UDP 21116 漏放行是最常见的「永远连接中」事故。21118 / 21119 是 WebSocket 口,不用 Web 客户端就直接封掉——它们信任 X-Real-IP 头,有被伪造的隐患。
  • 防火墙在云安全组层面做,主机本身不用再开洞。
运维两句话:
  • 查客户端在不在线:docker logs | grep <ID> 看注册/心跳记录;
  • 查被控端真实 ID:服务端 SQLite 库里 select id from peer——比看客户端界面可靠,因为一台机器装了服务后,用户态和服务态各有一份配置目录,界面上可能显示两个不同的 ID。

三、被控端:无人值守的全部配置

被控端的目标是:断电恢复后自动上线、没人碰也永远可连、机上的人改不掉配置。

安装与密码:

  • 静默安装参数 --silent-install,然后注册 Windows 服务:LocalSystem 身份、启动类型 Auto、不依赖任何用户登录。注意服务必须先装,设密码才不会报错(有先后依赖)。
  • 永久密码用随机强密码,别用有规律的。真正拦得住人的是 2FA(TOTP)——强密码防猜,防不了「你被人看着输密码」。2FA 需要在被控机上人工扫码启用,脚本化不了,这是流程里必须留的人工环节。
锁死配置(配合把配置文件设为只读):
  • hide-network-settings / hide-server-settings / hide-security-settings / hide-proxy-settings / hide-stop-service:把设置面板逐块藏掉;
  • disable-change-permanent-password / disable-change-id:密码和 ID 不许改;
  • allow-auto-update = N:官方客户端的自动升级会覆盖预配置、绕过所有锁,必须关掉并固定版本分发;
  • allow-remote-config-modification = N。
省流量(自托管下这几个默认全是 N,不显式开就全走中继):
  • enable-udp-punch / enable-tcp-punch / enable-ipv6-punch = Y;
  • enable-abr = Y + custom-fps + image-quality:在编码端就压码率,比服务端限速更省。
可靠性:
  • 服务崩溃自愈:给服务写 FailureActions,5s / 10s / 30s 三次自动重启;
  • 防休眠:电源计划里睡眠 / 显示器 / 硬盘超时全设「从不」;
  • BIOS 里把 Restore on AC Power Loss 设成 Power On——否则跳闸断电恢复后机器不会自己开机,远程必然失联;
  • 千万别强杀服务进程来「重启服务」:实测会触发 RustDesk 的自清理,删掉 data 目录和服务注册项。恢复办法是用安装包重跑 --silent-install 补回 data,再重建服务,配置和 ID 都还在。
改被控端配置的正确姿势:先完全退出客户端(含托盘),再写配置文件,否则它退出时会把内存里的旧值回写覆盖你的修改。

四、控制端:SETUP 做了什么,进程为什么叫 mywindow

控制端故意保持轻量:不装服务、不设永久密码、不锁设置面板——这三件事做了,这台机器就也变成了可被连入的目标。它只需要:检测/静默安装客户端 → 把服务器地址、中继、密钥三件套写进配置 → 回读磁盘校验真的写进去了 → 启动。

但公司电脑有个现实问题:防火墙按进程名识别并拦截 rustdesk。解决思路是不重编译、只做改名副本:

先要理解这个程序的结构——rustdesk.exe 只是一个约 370KB 的 Flutter 壳,真正的核心在旁边的 librustdesk.dll(约 40MB)。壳启动时按自己的文件名去找 lib<名字>.dll,再从里面查导出符号。所以:

  • mywindow.exe:rustdesk.exe 的副本,并对其中的版本信息做 UTF-16 等长字符串补丁("RustDesk" → "MyWindow",8 字符换 8 字符,文件偏移零变化)。这一步改掉了任务管理器「进程」页显示的友好名——从 "RustDesk Remote Desktop" 变成 "MyWindow Remote Desktop"。
  • libmywindow.dll:核心库的字节级原样副本,只改文件名。核心库的导出表是按符号名排序的结构,内容一个字节都不能动——实测改了导出符号名字节后,GetProcAddress 直接查不到,程序报 "Failed to get …_core_main" 退出。这也意味着核心里的配置目录名、窗口标题都不变,配置目录保持共享,ID 和服务器配置零迁移。
  • 开机自启项从 rustdesk.exe 重定向到 mywindow.exe,重启后存活的是改名版。
  • 代价:两个 exe 共用一把单实例锁,同一时间只能跑一个——控制端本来也只需要一个。
最终效果:进程页显示 "MyWindow Remote Desktop",详细信息页是 mywindow.exe,防火墙按进程名匹配时看到的不再是 rustdesk。窗口标题和界面文字仍是 RustDesk——按需取舍,我的诉求只是进程名。

五、安全模型:六个反直觉的结论

  1. hbbr 默认不校验密钥。全网教程抄来抄去都漏掉这条:不开 ENCRYPTED_ONLY,你的中继对全世界开放,而这台机器按流量计费。
  2. 密钥不是安全边界。公钥必须写进每个客户端,任何人拿到客户端都能从配置里读出来(base64 明文)。防「别人用我的服务器」只能靠网络层不可达,而不是藏密钥。
  3. 成本攻击比入侵攻击更现实。不需要入侵,知道地址就够——把带宽和流量吃干净,同机的其他业务跟着遭殃。限速是安全措施,不只是省钱。
  4. 服务器 SSH 是整个体系的安全下限。root 一旦失守,丢的不只是服务器,是所有受控机的密码哈希和中继信道。先改密钥登录、关密码登录。
  5. 拦得住人的是 2FA,不是密码。强密码防机器猜,防不了被人看着输。TOTP 2FA 才是真门槛,代价是要人工扫码。
  6. 不锁死客户端,一切加固白搭。被控机器上的人随时能把服务器改回公共网络、改掉密码——所以才有第三节那一排 hide/disable 开关。

六、成本:一切设计围绕「少走中继」

成本对比

按国内云流量约 0.8 元/GB 估算:一路 1080p 全走中继 ≈ 237 GB/月 ≈ 190 元;压到 720p 低画质 ≈ 63 元;打洞成功的直连是 0 元。所以「促成直连」不是性能优化,是省钱刚需——但在对称 NAT 下这条路基本堵死,于是剩下两件事:编码端压码率(比服务端限速更省,它在数据产生之前就少生了数据),和服务端限速兜底(防单路失控和恶意滥用)。

七、踩坑清单(每条都真踩过)

  • 抄教程用了 scratch 精简镜像 + ENCRYPTED_ONLY:镜像根本不认这些变量,静默失效;
  • 安全组漏放 UDP 21116:客户端永远显示连接中,TCP 全通也没用;
  • 在腾讯云上用腾讯内网镜像源测带宽:测出 362 Mbps 假象(走云内链路),必须用外部源测公网真实带宽;
  • enable-udp-punch 自托管默认是 N:不显式开启,直连永远不会被尝试;
  • 官方客户端自动升级:新版本绕过全部锁与预配置,必须 allow-auto-update=N 固定版本;
  • 用 TCP 连接数判断客户端在线:心跳走 UDP,ss -tn 里看不到 established 是正常的,看服务端日志;
  • 强杀服务进程重启:触发自清理,data 目录和服务项一起消失;
  • 一台机器出现两个 ID:用户态和服务态各一份配置目录,以服务端 SQLite 为准;
  • PowerShell 5.1 的编码坑:.ps1 必须纯 ASCII,无 BOM 的 UTF-8 中文注释会把解析器搞出二十几个语法错误;
  • 环境变量同时存在 http_proxy 和 HTTP_PROXY:Start-Process 抛「字典中的关键字重复」,若异常被吞,脚本显示成功但一条命令都没执行;
  • 给 exe 改名牵出的两个坑:壳按自身名找同名核心库 DLL;核心库导出表按符号名排序,就地改字节 = 符号查不到。

八、一张清单带走

服务端(Docker 环境变量):

TEXT
ENCRYPTED_ONLY=1            # 中继强制校验密钥,防白嫖
RELAY=<服务器>:21117         # 中继地址
TEST_HBBS=no                # 1:1 NAT 场景关自检
LIMIT_SPEED / SINGLE_BANDWIDTH / TOTAL_BANDWIDTH / DOWNGRADE_START_CHECK

端口:TCP 21115/21116/21117 + UDP 21116,21118/21119 封掉。

被控端关键配置键(RustDesk2.toml 的 [options]):

TEXT
custom-rendezvous-server / relay-server / key   # 服务器三件套
hide-network-settings / hide-server-settings / hide-security-settings
hide-stop-service / disable-change-permanent-password / disable-change-id
allow-auto-update = N          # 关自动升级,防绕锁
allow-remote-config-modification = N
enable-udp-punch = Y           # 自托管默认 N,不开全走中继
enable-tcp-punch = Y / enable-ipv6-punch = Y
enable-abr = Y / custom-fps / image-quality   # 编码端压码率
verification-method = use-permanent-password

被控端机器本身:服务 Auto + FailureActions 三段自愈、电源计划全「从不」、BIOS 来电自启、2FA 人工扫码启用。


这套东西的全部价值在于:数据端到端加密、内网零暴露、无人值守自愈,以及——清楚地知道每一分流量花在哪。配置项看着多,其实就三句话:服务器别当公共中继,被控端别被机上的人改回去,控制端别在别人的防火墙上报真名。


附件

💬 评论 (0)

今日还可评论 3 条

还没有评论,来抢沙发吧。