chore: 保存 Agent 安全优化前基线

This commit is contained in:
Logan.Li committed 2026-09-02 23:31:38 +08:00
1 parent 08696896f9
commit ff016347cd
116 files changed
+3858 -2485

No files matched your search

+84 -13
View File
@@ -87,25 +87,96 @@ lsof +D /Volumes/<临时卷名>
先正常退出该 OpsXShell 进程,再卸载对应的临时卷并重新构建。不要在应用仍从卷内运行时直接删除 `rw.*.dmg`。
## 签名和公证
## 签名、公证和自动更新(未完成)
标签 `tauri-v*` 会触发 `.github/workflows/tauri-release.yml`。正式标签构建要求签名变量齐全。
**当前状态:没有签名凭据,公开包是 ad-hoc / 未签名,未公证,没有自动更新。**
没有 `APPLE_*` / Authenticode 证书、minisign 私钥和 HTTPS 更新源之前,不得在 README、界面、Release 说明或 CI 绿勾上写「已签名」「已公证」「支持自动更新」。`tauri.conf.json` 的 `signingIdentity` 现在是 `"-"`;`plugins` 只有 deep-link,没有 updater。
macOS:
现有门禁只做「有凭据才验、没凭据就标明开发包」:
- `APPLE_CERTIFICATE`
- `APPLE_CERTIFICATE_PASSWORD`
- `APPLE_SIGNING_IDENTITY`
- `APPLE_ID`
- `APPLE_PASSWORD`
- `APPLE_TEAM_ID`
- `REQUIRE_SIGNING=true` 时,`scripts/release-preflight.mjs` 检查环境变量齐全,`scripts/verify-release.mjs` 拒绝 macOS ad-hoc、要求 Windows Authenticode `Valid`。
- 未设该变量时,`./scripts/build.sh` 仍可打开发包,验证只检查架构和体积。
- CI 工作流 `.github/workflows/tauri-release.yml` 已能在 Secrets 齐全时写入 `src-tauri/tauri.signing.conf.json` 再构建;**本地 `build.sh` 还不会生成这份配置**,所以本机即使导出了同样的环境变量,也不会走出 ad-hoc。
- 全仓库没有 `notarytool` / `staple`。Developer ID 签过但未公证的 `.app`,用户机器上仍会被 Gatekeeper 拦。
- `scripts/release.py` 仍提示「右键打开」——这是 ad-hoc 包的权宜说明,正式公证后必须删掉。
Windows:
下面是有凭据之后的做法。没齐之前不要改 `signingIdentity`、不要接 updater 插件、不要在设置里放「检查更新」。
- `WINDOWS_CERTIFICATE_BASE64`
- `WINDOWS_CERTIFICATE_PASSWORD`
### 1. 凭据(先买/申请,再接线)
缺少凭据时只能生成开发构建,不能标记为正式签名或已公证版本。
| 平台 | 要什么 | 放到哪里 |
| -------- | ---------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| macOS | Apple Developer Program;**Developer ID Application** 证书(`.p12`);App 专用密码 | 本机环境变量 + GitHub/Gitee Secrets:`APPLE_CERTIFICATE`(p12 的 base64)、`APPLE_CERTIFICATE_PASSWORD`、`APPLE_SIGNING_IDENTITY`(如 `Developer ID Application: …`)、`APPLE_ID`、`APPLE_PASSWORD`、`APPLE_TEAM_ID` |
| Windows | 代码签名证书(OV/EV PFX) | `WINDOWS_CERTIFICATE_BASE64`、`WINDOWS_CERTIFICATE_PASSWORD` |
| 自动更新 | 单独的 **minisign** 密钥对,与 Apple/Authenticode 不是同一把 | 私钥只进 CI Secrets;**公钥**写进仓库的 updater 配置 |
Linux 安装包(deb/rpm/AppImage)不走 Apple/Authenticode。自动更新的 `.sig` 是另一套签名,三平台都要。
`src-tauri/tauri.signing.conf.json` 由脚本现场生成,必须进 `.gitignore`,不得提交。
### 2. 本地与 CI 同源签名
目标:本机 `REQUIRE_SIGNING=true ./scripts/build.sh` 与 CI 走同一条路径,都能复现同一类签名产物。
1. `build.sh` / `build.bat` 在签名变量齐全时,生成 `tauri.signing.conf.json`(macOS 写 `bundle.macOS.signingIdentity`;Windows 写 thumbprint / sha256 / timestamp),再 `npx tauri build --config src-tauri/tauri.signing.conf.json`。
2. 不要改仓库里的 `tauri.conf.json` 默认 `"-"`——没凭据的开发机构必须还能打 ad-hoc。
3. `REQUIRE_SIGNING=true` 时 `verify-release.mjs` 必须失败于 ad-hoc / 无效 Authenticode;不要为了绿而关校验。
4. 正式标签才设 `REQUIRE_SIGNING=true`。日常 PR 构建保持未签名。
### 3. macOS 公证(notarytool + staple)
签名成功 ≠ 用户能打开。必须公证并钉票:
```bash
# 构建并 Developer ID 签名之后
xcrun notarytool submit "$DMG" \
--apple-id "$APPLE_ID" --password "$APPLE_PASSWORD" --team-id "$APPLE_TEAM_ID" \
--wait
xcrun stapler staple "$DMG"
# 若 staple 的是 .app,再重新打 DMG,或对 DMG 再 staple
spctl --assess --type install --verbose "$DMG"
```
验收(必须在**另一台未关 SIP、未关 Gatekeeper 的 Mac**上,不要只在构建机看 `spctl`):
1. 下载 DMG,双击打开,拖到「应用程序」,**不要**右键「打开」、不要 `xattr -d`。
2. 能启动、能连 SSH,菜单里没有 DevTools。
3. `codesign --verify --deep --strict` 且 `codesign -dv` 不是 `Signature=adhoc`。
4. 删掉 Release 说明和 `release.py` 里的「右键绕过 Gatekeeper」。
没做完 staple 之前,文档继续写「未公证」。
### 4. Windows Authenticode
CI 已有 PFX 导入和 `Get-AuthenticodeSignature` 门禁。本机应对齐:
1. 导入 PFX,把 thumbprint 写入 `tauri.signing.conf.json`(`digestAlgorithm: sha256`,时间戳如 `http://timestamp.digicert.com`)。
2. 对 `.exe` / `.msi` 都验 `Status -eq Valid`,不要只签主程序。
3. 在一台干净 Windows 上安装:SmartScreen 不应再是「未知发布者」。EV 证书对 SmartScreen 更友好,OV 可能仍有一段时间的声誉积累——如实写,不要标「已通过 SmartScreen」。
### 5. 自动更新(有公钥、HTTPS、回滚才开)
架构规则已经写死:没有签名公钥、HTTPS 更新源和回滚策略,更新必须保持关闭。接入顺序:
1. 生成 minisign 密钥;公钥进 `tauri.conf.json` 的 `plugins.updater.pubkey`;私钥只进 Secrets。
2. 依赖 `tauri-plugin-updater`,capabilities 只开 `updater:allow-check` 与 `updater:allow-download-and-install`。
3. 构建打开 `createUpdaterArtifacts`,每个安装包旁有 `.sig`。
4. 发布时生成 `latest.json`(版本、各平台 URL、签名),放到 HTTPS(Gitee/GitHub Release 附件或静态镜像,建议双 endpoint)。
5. 前端「检查更新」:**公钥和 endpoint 未进正式包之前不要画这个入口**。
6. 回滚:保留上一份 `latest.json`;更新失败或健康检查失败时指回上一版本;文档写清如何撤回错误版本(覆盖 manifest,而不是删掉用户已装的包)。
7. `release.py` 除 `.dmg/.exe/.msi` 外还要上传 `.sig` 与 `latest.json`。
未完成前,设置页、欢迎页、菜单都不要出现「自动更新」或「已是最新」。
### 6. 发布工作流补齐(与签名独立,但同一闭环)
`.github/workflows/tauri-release.yml` 现在只 `upload-artifact`(约 90 天过期),`permissions.contents: read`,不能建 GitHub Release。有凭据并公证之后再改:
- tag 与现有 Gitee 流程对齐(不要两套版本号)。
- `contents: write` 后创建 Release,附 SHA256SUMS、安装包、`.sig`、`latest.json`。
- 公证步骤放在「Verify native package」之前或之中;未 staple 不得当正式附件。
GitHub Release 不是「已公证」的替代;没有 notary/Authenticode 时,Release 也只能标开发包。
## 产物验证