update-client-hardening 四工作流合入(构建/静态检查已过): - 配置:不可变产品策略经 UpdateClientResources.cmake 归一化校验后 内嵌 qrc(URL/相对路径/主程序必须落 install_root/版本/UUID 格式, CMake override 写入前复验);可变机器状态迁系统级原生存储; 运行目录不再落可编辑 app_config.json。 - i18n:生产源全英文(no-Han 检查 41/41),中文进 translations/ zh_CN 目录;翻译加载收敛 Common/TranslationHelper 唯一入口; Bootstrap 补资源文件。 - 依赖:Qt/OpenSSL/架构/Perl 机器路径改 imported targets + UpdateClientDependencies.cmake,3rdparty 由父工程契约供给。 - 清理:旧 README/SDK README/3 个重复 PowerShell 打包脚本/PDF 提取 文本删除,canonical README + 5 份结构化英文文档替代;Launcher requireAdministrator manifest;统一 MSVC /utf-8 删运行时编码设置。
7.0 KiB
更新客户端架构
目的
更新客户端强制执行一条受控的产品入口路径,并执行事务性更新,而不会将可编辑的安装文件作为信任边界的一部分。父项目 SimCAE 拥有集成和交付的控制权。
本文档描述了运行时合约。服务器发布、管理员界面、数据库模式和对象存储实现均在此仓库之外。
产品入口规则
在每个支持的操作系统上,必须通过 Launcher 进入发布版本。如果某个平台尚未提供可用的启动器和更新运行时,其发布配置或打包必须失败。直接启动应用程序不是可接受的备用方案。
调试版本可以禁用启动门以供开发者迭代使用。但带有此例外的构建版本不得分发。
组件
启动器
启动器 是面向用户的入口。它:
- 加载嵌入式产品配置和机器范围的可变状态。
- 建立或验证设备凭证。
- 加载并验证已签名的版本策略。
- 检查防回滚、系统时钟倒退和离线有效性条件。
- 将已安装的版本与已签名的清单进行比对验证。
- 在有网络连接时检查服务器是否允许更新。
- 当需要更新时启动
Updater。 - 否则创建一个短期有效的一次性票据并启动 SimCAE。
在需要提升权限才能进行机器范围状态或安装更新的平台上,发布启动器必须通过平台原生机制请求所需权限。
主程序
交付的 SimCAE 可执行文件实现了合同中的主应用程序部分。此仓库中的 MainApp 是一个集成示例,不是第二个产品可执行文件。
该应用程序必须:
- 在正常启动前消耗并验证
--ticket-file=<path>; - 拒绝在受控发布构建中缺失、过期、重复或不匹配的票证;
- 仅在启动达到更新器预期的健康点后,将
ok\n写入--health-file=<path>。
在当前集成中,启动器拥有已签名策略和已安装文件完整性检查。应用程序独立验证短期票证;将策略或清单验证移至 SimCAE 需要单独的共享运行时协议,且不得由本文件暗示。
启动票证阻止了对 Launcher 的随意绕过。它不是已签名策略、清单验证或服务器授权的替代品。
更新器
Updater 拥有更新事务。它:
- 获取已签名的目标清单和授权下载位置;
- 在进行任何文件操作前验证路径;
- 比较本地文件与完整清单;
- 仅下载缺失或不匹配的文件;
- 在安装前验证签名、大小和哈希值;
- 备份被替换或删除的文件;
- 安装阶段内容;
- 在必要时将锁定文件的替换委托给
Bootstrap; - 使用启动票和健康文件请求启动更新后的应用程序;
- 仅在健康确认后提交;
- 在失败时恢复上一版本。
下载可以是增量的,但完整性验证始终针对完整的签名清单。
引导启动
Bootstrap 有意设计得非常小。它会等待更新程序进程释放被锁定的文件,执行已批准的替换方案,并重新启动更新程序以继续验证。它必须拒绝不安全的相对路径,且不得擅自制定更新策略。
通用运行时
共享库提供配置访问、原生状态、设备标识、HTTP 请求、签名策略处理、本地防回滚状态、清单完整性验证以及启动票操作。共享辅助工具不会削弱组件特定的检查。
启动序列
用户
-> 启动器
-> 嵌入式产品配置
-> 机器状态和设备凭证
-> 签名策略和清单验证
-> 是否需要更新? -> 更新器 -> 需要时启动引导程序
-> 创建一次性票据
-> SimCAE
-> 消耗票据
-> 重新验证策略和完整性
-> 按请求报告启动健康状态
设备激活是机器识别,而不是终端用户登录。普通客户端启动不会创建管理员会话,也不应接收服务器的发布权限。
更新事务
一个持久的事务必须至少区分这些阶段:
- 初始化事务标识符和暂存区。
- 验证目标清单和所有暂存的负载。
- 记录已更改和过时的路径。
- 备份当前文件。
- 安装未被锁定的文件。
- 当需要时,将锁定文件的工作交给
Bootstrap。 - 启动候选版本并等待其健康标记。
- 提交并删除临时数据仅在成功验证后进行。
- 在任何失败后回滚并验证恢复的版本。
在进程启动时,必须在开始新事务之前恢复中断的事务。在持久化状态仍需要恢复时,不得删除暂存和备份数据。
信任边界
| 数据或机制 | 权限 | 说明 |
|---|---|---|
| 服务器私有签名密钥 | 仅限服务器 | 从未运送到客户。 |
| 已签署的版本策略 | 服务器 | 控制运行、更新、降级和离线决策。 |
| 签名的清单 | 服务器 | 定义了完整的受保护文件集合和预期的哈希值。 |
| 签名的设备凭证 | 服务器 | 绑定应用程序、频道、安装、设备和许可证身份。它不是用户会话。 |
| 嵌入式产品配置 | 构建流水线 | 防止随意覆盖安装文件;可由管理员检查和修改。 |
| 原生可变状态 | 本地机器 | 保持注册和安装状态;它不涉及授权。 |
| 一次性启动票 | 本地启动器 | 证明预期的启动器启动了此进程。它不授予服务器权限。 |
| 健康标识 | 候选流程 | 信号提交或回滚的启动完成;它不能证明包的真实性。 |
带签名的 JSON 合约必须使用确定性序列化,从已签名的负载中省略签名字段,标识算法和密钥 ID,并支持受控的公钥轮换窗口。验证失败将导致系统关闭。
运行时布局
父安装程序拥有最终的布局。预期的可执行文件关系为:
<install-root>/
bin/
Launcher<platform-suffix>
Updater<platform-suffix>
Bootstrap<platform-suffix>
SimCAE<platform-suffix>
config/
<仅在需要时缓存已签名的运行时>
安装目录中不应包含可编辑的产品策略 JSON 文件。更新缓存、备份、已签名策略缓存和机器状态必须使用集成运行时选择的位置,并且必须遵守平台的写入权限规则。
失败策略
- 无效签名、不安全路径、身份不匹配、策略回滚以及受保护文件损坏将导致系统关闭。
- 一个临时的网络故障可能会使用仍然有效的已签名离线策略;它不能静默地创建新的允许项。
- 过期的离线策略或被禁止的版本会阻止启动。
- 失败的候选健康触发器会导致回滚。
- 回滚失败会留下可恢复的状态和诊断错误;它不能报告更新成功。
接受场景
发布接受必须涵盖:
- 通过
Launcher正常启动; - 直接 SimCAE 启动拒绝;
- 首次设备注册及后续离线启动;
- 更新到较新的已发布版本;
- 篡改的受保护文件被拒绝;
- 篡改的策略或清单被拒绝;
- 强制更新和禁用版本行为;
- 频道选择和禁止降级行为;
- 中断更新恢复;
- 候选失败和成功回滚;
- 从实际安装位置启动并更新,通过正常的最终用户交互并需要提升权限。