Files
RemoteDesk/docs/online-updates.md

5.5 KiB
Raw Permalink Blame History

RemoteDesk 在线升级

1. 安全模型

控制端只接受管理员配置的 HTTPS 更新清单和 Ed25519 公钥。CDN 传输不能替代发布签名:客户端先验证清单签名,再验证其中的产品、目标平台和版本,下载 MSI 后还会验证签名清单内的精确文件大小和 SHA-256。任何一步失败都不会启动安装程序。

更新清单使用以下信封格式,payload 是原始 UTF-8 JSON 字节的 Base64,避免 JSON 字段顺序或空白变化破坏签名语义:

{
  "schema": 1,
  "payload": "BASE64_SIGNED_JSON",
  "signature": "BASE64_ED25519_SIGNATURE"
}

签名载荷包含固定产品名 remotedesk、发布通道、三段式版本、发布时间、目标平台、MSI HTTPS URL、文件大小、SHA-256 和可选发行说明。Windows x64 当前使用目标值 windows-x64

2. 发布密钥

在隔离的发布环境中生成 Ed25519 PKCS#8 私钥,例如:

openssl genpkey -algorithm Ed25519 -out remotedesk-update-private.pem

私钥不得提交到仓库、写入安装包或上传 CDN。生产环境应把它保存在受审计的 CI 密钥库或离线签名设备中,并限制发布工作流读取权限。

3. 生成清单

先完成 MSI 构建和 Authenticode 签名,再生成更新清单。清单中的哈希必须对应最终上传的字节:

cargo run --locked -p remotedesk-update-manifest -- `
  --installer .\artifacts\RemoteDesk-M0-0.3.0-windows-x64.msi `
  --installer-url https://updates.example.com/stable/RemoteDesk-M0-0.3.0-windows-x64.msi `
  --private-key C:\secure\remotedesk-update-private.pem `
  --version 0.3.0 `
  --notes-file .\release-notes.txt `
  --output .\artifacts\stable.json `
  --public-key-output .\artifacts\update-public-key.txt

生成器拒绝覆盖已有清单,避免意外替换已发布元数据。将 MSI 和 stable.json 上传到只提供 HTTPS 的静态发布源;清单和安装包 URL 不允许凭据、query 或 fragment。

Drone 构建配置只生成未签名开发包,不读取生产签名材料。正式发布应在受保护的 Drone 发布流水线或离线签名环境中执行,并把版本标签精确绑定到 Cargo workspace 版本,例如 v0.3.0。发布环境必须配置并限制以下 secrets:

  • WINDOWS_SIGNING_PFX_BASE64:生产 Authenticode PFX 的 Base64 字节。
  • WINDOWS_SIGNING_PFX_PASSWORDPFX 密码。
  • UPDATE_ED25519_PRIVATE_KEY_PEMEd25519 PKCS#8 私钥全文。
  • UPDATE_ED25519_PUBLIC_KEY_BASE64:预先登记的 32 字节公钥 Base64,用来阻止误用另一把私钥发布。

发布流水线临时导入 PFX,验证私钥、有效期和 Code Signing EKU,以 SHA-256 和 RFC 3161 时间戳签署控制端内部 EXE、PowerShell helper、控制端 MSI、Windows Host PowerShell 模块和 Host MSI,并在生成任何发布哈希前逐个验签。随后生成 Ed25519 更新清单和 SPDX JSON SBOM,先上传不可变的版本资产,最后才替换稳定通道中的 stable.json。固定清单地址为:

https://updates.example.com/stable/stable.json

发布对象存储必须只提供 HTTPS;客户端只跟随 HTTPS 且最多三次。Drone 发布环境应配置审批、保护版本标签的创建权限,并在缺少任何 secret、标签与源码版本不一致、证书或时间戳无效、最终 MSI 验签失败或公钥不匹配时,在更新发布通道前终止。

4. 控制端配置

在“全局设置 -> 在线升级”中填写清单 HTTPS URL,以及生成器输出的 Base64 公钥并保存。启用“自动检查更新”后,控制端每次启动会检查一次;也可以手动点击“检查更新”。

发现更高版本后,用户确认“下载并安装”,控制服务会:

  1. 再次下载并验证签名清单,防止检查和安装之间被替换。
  2. 下载 MSI 到 %LOCALAPPDATA%\RemoteDesk\updates,限制最大 1 GiB。
  3. 验证字节数和 SHA-256,启动独立升级 helper。
  4. 返回接受状态后退出控制服务;helper 再次校验哈希并运行 msiexec /passive /norestart
  5. 安装成功后重新启动 RemoteDesk;MSI 日志保存在更新目录。

当前 MSI 的固定 UpgradeCodeMajorUpgrade 规则负责原位升级并拒绝降级。便携预览版也可以检查并安装 MSI,但不会覆盖原便携目录。

5. 本地接口

控制端页面通过回环控制服务调用以下接口:

接口 成功状态 用途
GET /api/v1/settings 200 读取更新 URL、公钥和自动检查开关
PUT /api/v1/settings 200 保存配置;URL 与公钥必须同时提供或同时清空
POST /api/v1/update/check 200 请求体为 {};下载、验签并比较版本
POST /api/v1/update/install 202 请求体为 {"version":"x.y.z"};重新验签、下载并启动退出后安装流程

请求 JSON 或字段不合法返回 400;未配置更新源、重复发起安装返回 409;检查阶段的网络、HTTP、清单或签名错误返回 502;安装准备或 helper 启动失败返回 500。所有错误都返回 {"error":"..."},不会在失败后静默安装。

6. 正式发布边界

仓库已实现客户端升级机制、签名清单生成器和 Drone 构建入口,但不会内置生产私钥、PFX 或把信任公钥静默写入客户端。普通 Drone CI 和本地打包明确生成未签名开发包;正式签名资产必须由受保护的发布环境生成。首次生产发布仍需审计 Drone 权限、证书链、时间戳服务、对象存储下载和真实 MSI 原位升级。