Files
RemoteDesk/docs/online-updates.md
T

91 lines
5.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# RemoteDesk 在线升级
## 1. 安全模型
控制端只接受管理员配置的 HTTPS 更新清单和 Ed25519 公钥。CDN 传输不能替代发布签名:客户端先验证清单签名,再验证其中的产品、目标平台和版本,下载 MSI 后还会验证签名清单内的精确文件大小和 SHA-256。任何一步失败都不会启动安装程序。
更新清单使用以下信封格式,`payload` 是原始 UTF-8 JSON 字节的 Base64,避免 JSON 字段顺序或空白变化破坏签名语义:
```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 私钥,例如:
```powershell
openssl genpkey -algorithm Ed25519 -out remotedesk-update-private.pem
```
私钥不得提交到仓库、写入安装包或上传 CDN。生产环境应把它保存在受审计的 CI 密钥库或离线签名设备中,并限制发布工作流读取权限。
## 3. 生成清单
先完成 MSI 构建和 Authenticode 签名,再生成更新清单。清单中的哈希必须对应最终上传的字节:
```powershell
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_PASSWORD`PFX 密码。
- `UPDATE_ED25519_PRIVATE_KEY_PEM`Ed25519 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`。固定清单地址为:
```text
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 的固定 `UpgradeCode``MajorUpgrade` 规则负责原位升级并拒绝降级。便携预览版也可以检查并安装 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 原位升级。