Smail/docs/部署后自动归档与证书保留.md
smail 9c450f1734
Some checks are pending
Security Checks / security (push) Waiting to run
运行时配置清理阶段检查点
2026-10-05 01:31:34 +08:00

12 KiB
Raw Permalink Blame History

部署后自动归档与证书保留

已确认要求

  • 正式程序在任意部署目录启动后自动创建目录,不依赖手动整理或开发脚本。
  • 已采用 config/ssl/data/log 布局的旧部署,启动时自动归档三把业务密钥,保留原字节。
  • 不再创建 ssl/managed。自动签发的成对文件直接存放于 ssl/,保留当前新证书和上一套未过期证书,上一套到期后清理。
  • 域名证书原文件继续保存在 ssl/ 根目录,不与业务加密密钥混放。
  • 模拟部署使用 yunx-test/,修改完成后必须完整构建并核验最新二进制。

程序创建的目录

部署目录/
├── config/                         # 初始化完成后的五个生效 JSON
├── ssl/
│   ├── credentials/
│   │   └── credential.key
│   ├── external-email-encryption/
│   │   └── external-email-encryption.key
│   ├── external-email-fingerprint/
│   │   └── external-email-fingerprint.key
│   ├── dkim/
│   ├── smail-tls-<版本>.crt          # 自动签发的当前或上一套证书
│   ├── smail-tls-<版本>.key
│   ├── 域名.crt
│   └── 域名.key
├── data/
├── log/
├── smail
└── smail-admin

目录由主服务启动入口 InitializeDeployment 创建;密钥由对应业务首次需要时创建,不能为凑齐文件生成无用密钥。Windows 二进制带 .exe 后缀。初始化未完成时不会伪造五个完成态配置文件。

三个目录分别保存配置凭据加密密钥、外部邮箱加密密钥、外部邮箱检索指纹密钥。ACME 账户密钥的进一步归档不在本次三个业务目录改动中,原文件继续保留。

启动迁移

启动先检查再移动:已初始化部署缺少密钥或数据库、新旧密钥重复、密钥不是 32 字节、存在链接或无效目标目录时拒绝继续,不覆盖、不重新生成替代密钥。Linux 还检查密钥文件权限。

有效的 ssl/credential.key 等旧文件使用重命名归档到业务目录,内容不变,因此旧配置密文、外部邮箱密文及指纹仍有效。每次移动后同步目录;迁移中断后允许下次启动继续,重复启动不会再次生成密钥。

更早的 config/ 或 data/ 混放部署仍须执行原有完整迁移流程,不能只搬三把密钥而遗漏数据库、证书和历史引用。

两份保留规则

  • 一份是完整的证书和私钥组合,不是两个单独文件。
  • 当前生效证书优先保留;提交新版本时记录上一份完整且未过期的材料,不按文件时间补足两份,也不保留更早的历史链。
  • 待应用候选、正在导入或续期的材料、磁盘已提交引用受到保护;操作进行时可能暂时超过两份,应用或取消后再次清理。
  • 启动后、定期证书维护、配置应用、回滚、取消候选及重新加载时自动执行。
  • 仅删除程序识别的历史版本。未知文件、链接、无法安全检查的目录保留并告警;事务恢复未完成时暂停清理。
  • 私钥文件锁保护跨进程暂存,配置状态锁协调提交与清理。停止或替换管理器时释放暂存锁。
  • 每分钟检查上一套的到期时间;先通过五分区配置事务清空引用,再删除程序生成的成对文件。当前材料不可用或事务未恢复时暂停回收。
  • 已落盘但运行态发布失败的清理事务保留恢复入口,下轮维护继续发布,不重复提交同一代次。
  • 成对清理先删除私钥再删除证书;中断遗留的已识别孤立证书在下轮重试,无法解析或配对的材料不擅自删除。
  • 已清理证书的历史配置不能回滚,预览和执行均返回明确中文错误,必须重新导入有效证书。

域名文件与运行引用的边界

ssl/域名.crt、ssl/域名.key 是用户放置的证书原文件,自动归档和清理不删除它们。

自定义证书直接保存原始路径,不复制。部署目录内优先使用 ssl/域名.crt、ssl/域名.key,部署目录外保留绝对路径;普通文件、链接、大小、私钥权限、强度、有效期、配对及服务域名校验仍执行。

启动时自动迁移旧 managed 引用:自定义模式优先复用字节完全相同的根目录原件;没有匹配原件时将必要材料迁到根目录程序版本文件。先提交配置事务,再回收已退出引用的旧目录;未知文件不会删除。

自动续期先写一套独立新文件,完整校验后事务切换当前引用。SMTP、IMAP、POP3、内置 HTTPS 共享动态证书,新 TLS 握手读取新证书,已有连接不主动关闭。新材料加载失败时仅回退到仍有效的已校验证书;重启无内存缓存时尝试配置中的上一套。

手工证书不受自动删除管理,也不自动复制一份历史。外部维护者应先准备好完整的新材料再更新引用;分别覆盖证书与私钥期间验证失败时进程保留有效缓存,但不保证此时重启仍能加载被覆盖的旧材料。

验证与边界

  • 自动化回归覆盖新建目录、旧密钥字节不变、中断续迁、重复启动、冲突拒绝、跨模式总数两份、暂存保护、未知文件保护、过期前版本、旧 current 清理及历史回滚拒绝。
  • test/deployment/verify_ssl_startup.py 在原前后端停服且最新包已部署后运行,将原 yunx-test 暂存至 test-log,用同一二进制在空 yunx-test 正式启动两次,再恢复原部署。所有测试产物保留,不删除业务库。
  • 空目录正式启动只验收自动目录创建和初始化页面可达,不等于完成整个首次初始化、管理员恢复权限或插件生产隔离验收。
  • Linux 编译通过不等于 Linux 生产运行验收。正式管理员恢复通道的保护不降低。

本轮实测记录(2026-09-09)

验证项目 本轮结果 记录位置
完整自动化回归 前端 155/155、部署 4/4、Go 全量通过 test-log/run-2026-09-09T09-12-56-738Z-9840/
专项并发检查 配置、SSL 设置及 HTTP、SMTP、IMAP、POP3 监听包通过 -race test-log/run-2026-09-09T09-04-43-786Z-46884/
证书生命周期 当前加上一套保留、到期事务清理、发布失败恢复、暂存租约、未知材料保护、孤立证书重试通过 test/backend/ssl_lifecycle_test.go 及上述回归日志
TLS 热切换 Go 本机 TLS 测试中,新握手换用新证书,原连接继续收发;无缓存及缓存到期时恢复上一套 同上,属于自动化 TLS 测试,不代表真实 CA 续期
开发 HTTPS 实际 HTTPS 服务执行 setSecureContext 后新连接换证、原连接保留,绝对路径和上一套恢复通过 test/frontend/dev-tls.test.mjs 及上述回归日志
最新包部署 前端、三个插件、主程序及管理员程序完整构建,部署后源码指纹和文件哈希验证通过 out/build-state.json、test-log/service-launch/
真实配置迁移 启动事务将代次 27 升到 28,改为 ssl/example.com.crt 和 ssl/example.com.key,旧 ssl/managed 不再存在 本机 yunx-test/config/ssl.json
原文件保护 原证书、原私钥、ACME 账户密钥、三把业务密钥的字节及创建时间不变 test-log/ssl-direct-baseline-20260909.json 为比较基线,不含密钥内容
浏览器保存与刷新 MCP 表单输入同一原件的绝对路径,经当前密码确认后显示“配置已保存”;规范化成原件相对路径,五分区代次均为 29;刷新状态及忽略缓存重载后路径一致、登录仍有效 当前会话 MCP 操作记录

浏览器操作期间后端 PID 为 31988,前端 PID 为 14648,保存配置未触发服务重启;这些编号仅代表此次现场。前端保留源码热更新,后端使用 yunx-test 最新编译程序。部署根目录只有 config、ssl、data、log 及两个运行程序。

当前仍是自定义证书模式,因此不会为手工原件额外复制上一套,也没有触发真实 ACME 签发。自动续期的两套保留通过独立测试材料验证;Linux 实机运行、正式管理员恢复通道、真实 CA 续期、全流程邮件收发仍不属于本轮验收。数据库没有清空,本轮是存量部署迁移验收,不是首次初始化验收。

开发服务器首次进入懒加载页面时发生过依赖预构建刷新,Vite 日志可确认;再次打开页面后完成上述验证。构建仍有富文本编辑器约 809 kB 的分包告警,开发代理有 util._extend 弃用提示,不属于本次 SSL 改造范围。MCP 快照落盘被其工作区根目录限制拒绝,未绕过限制写入;页面验收证据保留在本会话工具记录中。

本轮任务状态

  • [✓] 自定义证书直接引用,不生成副本。
  • [✓] 自动续期使用根目录版本文件,保留当前与上一套未过期证书。
  • [✓] 到期先清除配置引用再清理文件,暂存及异常材料受到保护。
  • [✓] 新握手热加载,不主动关闭已有连接,失败保留有效证书。
  • [✓] 旧部署迁移、完整构建、部署包核验和浏览器保存刷新完成。
  • [!] 真实 CA 续期及 Linux 生产实机运行未执行,不能视为已验收。

历史实测记录(2026-09-08,旧存储方案)

以下为旧 managed 存储方案的历史记录,不代表 2026-09-09 直接引用与热切换改造已验收。

验证项目 实测结果 本地记录
空目录正式启动两次 仅放两个程序,使用 smail.exe -p 15201,两次均自动创建部署目录及五个 SSL 子目录,初始化页面返回 200 test-log/ssl-startup-20260908-202756/result.json
旧部署自动升级 三把业务密钥归档前后字节不变,旧位置不再保留重复文件 test-log/ssl-upgrade-result-20260908.json
旧证书自动清理 由新程序启动完成,受管材料从 11 套收敛为 2 套,当前证书和私钥仍存在 同上
原文件保护 五个生效 JSON、根目录证书、ACME 密钥及其他原 SSL 文件字节不变;已有运行目录完整恢复 同上及空目录启动记录
邮件 TLS 465、993、995 均完成 TLS 1.3 握手,实际证书指纹与当前配置指向的证书一致 test-log/ssl-upgrade-tls-20260908.log
全量自动化回归 前端 140/140、部署 4/4、Go 全量通过 test-log/run-2026-09-08T12-25-13-445Z-12312/
专项并发检查 配置、运行路径、配置凭据、外部邮箱及 SSL 设置包的 -race 测试通过 test-log/run-2026-09-08T12-26-15-343Z-27176/
最新部署包 前端、三个插件、Windows 内嵌资源主程序及管理员程序完成构建,源码指纹和部署哈希通过 node deploy/build-state.mjs verify --deployed 核验 out/build-state.json

当前页面优化环境保留为前端源码热更新、后端最新编译程序,运行配置统一读取 yunx-test/config/。前端 HTTPS 及后端 /api/ping 均返回 200;后端使用已明确标记的开发恢复通道豁免参数,与上表不含该参数的空目录正式启动测试分开记录。

本轮没有进行浏览器页面交互验收,也没有宣称完整生产初始化或 Linux 运行验收通过。前端已有富文本编辑器分包体积提醒不在本次修改范围内。

历史任务状态(2026-09-08)

  • [✓] 正式主程序自动创建业务目录,不依赖部署脚本创建。
  • [✓] 已采用 SSL 布局的旧部署自动迁移三把业务密钥,重复启动不更换密钥。
  • [✓] 受管证书正常稳定状态保留两套,保护生效、暂存和恢复所需材料。
  • [✓] 最新包部署、空目录启动、旧部署升级及邮件 TLS 验证完成。
  • [!] 当时尚未实现根目录直接引用,已由上文 2026-09-09 新规则替代。
  • [!] Linux 正式运行、完整首次初始化及正式管理员恢复流程尚未完成本轮实机验收。