Smail/docs/根域名证书校验补充验收.md
smail 2e520eb5a8
Some checks are pending
Security Checks / security (push) Waiting to run
统一根域名与泛域名证书校验并补充云端反例验收
2026-09-21 04:38:46 +08:00

89 lines
7.3 KiB
Markdown
Raw Permalink 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.

# 根域名证书校验补充验收
## 结论与范围
北京时间 2026 年 9 月 21 日,在已提交并推送的 `37bb937` 基线上补充验收。发现并修复自动 DNS 证书的校验不一致:申请时要求根域名和泛域名,但运行时共用的域名列表遗漏根域名。正式程序没有替换,其他项目没有停止,用户原有云端测试报告没有修改。
**这是一个已实机复现并修复的证书覆盖缺口,不代表真实 DNS 续期或整体验收通过。**
## 需求与任务清单
- [✓] 自动 DNS 证书在申请、配置复验、加载和导出阶段均要求根域名与泛域名。
- [✓] 在云服务器保留整改前失败反例,再用新编译测试程序验证修复。
- [✓] 无效新证书不替换有效导出文件;热加载失败时保留尚有效的内存证书。
- [✓] 后续完整证书仍可热加载,外部证书管理模式的既有兼容测试通过。
- [✓] 云端配置证书专项 21 个顶层测试、SSL 初始化包 26 个顶层测试,均零跳过通过。
- [✓] 本地运行时校验与热加载专项竞态回归通过。
- [✓] 最新前端、插件和 Linux 程序已编译,三库空数据实机回归通过。
- [✓] 配置包静态安全复扫完成,原有七条告警未增加;扫描退出 1,不计为安全门禁通过。
- [!] 公网 DNS 挑战、自动续期、真实 CA 换证和全协议热加载仍需单独实机验收。
## 整改前反例
测试通过托管存储准备有效期内、密钥配对正确但仅包含 `*.example.com` 的测试证书,故意不在准备阶段校验域名,模拟已有文件的加载场景。没有使用正式私钥或修改正式证书。
云端目录:`/var/tmp/smail-review-ssl-apex-20260921/20260921-042413-before/`。
- 配置引用复验错误接受缺少根域名的证书。
- 当前及旧自动 DNS 配置的运行时加载均错误接受。
- 导出错误接受该证书,并替换已导出的有效证书包。
- 根域名加泛域名的正向用例通过;只有根域名的反例已能正确拒绝。
本轮两个顶层测试失败,零跳过,云端测试退出 1。日志完整保留在 `test-log/production-remediation-20260921/ssl-apex-before/`。这是一个共享校验列表缺口的多路径表现,不按断言数宣传漏洞数量。
## 修复和回归
`backend/config/ssl_custom.go` 的 `validationCertificateDomains` 在自动 DNS 项目托管模式明确加入根域名,继续要求泛域名及已配置的服务域名。保留既有文件安全、证书密钥匹配、有效期和强度校验,不改变手动证书入口、管理责任或 DNS 记录。
新增 `backend/config/ssl_apex_test.go`,覆盖配置复验、当前及旧配置加载、导出文件内容和身份保留,以及内存证书的拒绝与后续恢复。热加载测试调用实际运行时证书回调,但不是外部客户端对全部邮件协议的换证演练。
| 云端轮次 | 目录后缀 | 结果 |
| --- | --- | --- |
| 最终配置证书专项 | `20260921-042721-final` | 21 个顶层测试通过,零跳过,退出零 |
| SSL 初始化完整包 | `20260921-042935-setup-all` | 26 个顶层测试通过,零跳过,退出零 |
测试使用非特权 `smail`、独立临时目录及私有网络,正式目录不可访问;内存上限 256 MiB、CPU 配额 50%、禁用交换空间、运行时间上限 300 秒。最终配置证书专项执行输出记录内存峰值 8.1 MiB,不据此推导生产容量。
本地命令为 `node test/run.mjs backend -race -count=1 -p 1 -run '^TestProjectDNS(Runtime|Reload)' ./config`,通过。日志目录 `test-log/run-2026-09-20T20-28-01-801Z-38632/` 使用 UTC 文件名,不是北京时间日期。
## 候选产物
前端、Telegram 界面、三个 Linux 插件、Linux 主程序与管理员工具重新编译。版本为 `review-apex-20260921`,内嵌源码标识为 `37bb937c4040ae35a9393c418b9d7679f7bd436d-dirty`,不冒充已发布版本。
| 产物 | SHA-256 |
| --- | --- |
| 主程序 | `bb451caf67f6421253c5b4edddc6fce9961ac04613c4fe3166db36a465291939` |
| 管理员工具 | `e5b540a23f598f892f23b17f59e2f97c7560f8c3dca317ba6b6cdf2d67485821` |
| 最终配置测试程序 | `6d2cda0c4f3483bb05de86b8067588e27689f80043bd17b3e501e38cbbedacb4` |
| SSL 初始化测试程序 | `263126767560b5e09eccbc415eff5de4192c9c5babc6b16d90417f904a81db4d` |
构建日志和摘要位于 `test-log/production-remediation-20260921/build-apex.log`、`apex-build-manifest.json`;配置目录 94 个 Go 文件的摘要记录在 `apex-config-source-manifest.json`。
## 最新候选三库实机回归
同一主程序上传至 `/var/tmp/smail-review-apex-20260921/`,每轮创建新的空数据隔离实例,以 192 MiB 内存限额验证直接 TLS 拓扑。
| 数据库 | 实例目录后缀 | 结果 |
| --- | --- | --- |
| SQLite | `smail-review-runtime-20260921-043114` | 通过;只有 `smail.db`,完整性检查正常 |
| MySQL | `smail-review-runtime-20260921-043229` | 通过;配置历史与业务同库,SQLite 文件零个 |
| PostgreSQL | `smail-review-runtime-20260921-043411` | 通过;配置历史与业务同库,SQLite 文件零个 |
覆盖空库初始化、中文邮件和附件、IMAP APPEND/EXPUNGE、HTTP 旧会话撤销、协议码及客户端码撤销后的旧连接拒绝、过载恢复。三轮均无 OOM、资源采集错误或清理错误;SQL 测试账号已锁定,桥接已停止。
证据为 `apex-runtime-results.json`、`apex-runtime-evidence/` 和 `apex-evidence-index.json`,共十二份下载文件的摘要已经逐一复核。`apex-live-final.json` 再次确认六个保护服务和正式程序摘要未变,没有运行中的本轮隔离服务。该回归使用隔离域名和手动测试证书,不冒充公网域名验收、真实自动续期或新候选的 Nginx 正向复验。
## 静态扫描边界
Windows 主机设置 Linux/amd64、禁用 CGO,对 `./config` 运行原有 `gosec v2.22.10`。55 个文件、11,378 行,包加载错误零,原有 `nosec` 为 4;七条告警均为中等级,G304 五条、G302 两条,退出码 1。逐条按规则、文件、行号和代码片段与上一轮全项目报告中的配置包比较,集合完全一致。
原始日志为 `gosec-apex-config.log`,完整提取的 JSON 为 `gosec-apex-config.json`,SHA-256 为 `7c0eed5bd0cb48ab5fd926c431a8155671c5f8418aaeaa9830e49512c2ca7db9`。扫描后配置目录九十四个源码文件与构建时清单一致。这里只复扫变更包,不把它说成又一次全项目扫描;上一候选全项目三十八条仍是未通过记录,其他待办没有关闭。
## 保留的工具失败与证据边界
首次修复后,两个测试已经退出零,但验收辅助工具无条件再次停止已被 systemd 回收的短时单元,收到退出 5,将该轮判为失败;原始记录保留在 `ssl-apex-after/`,没有改成通过。
后续辅助工具额外读取 `LoadState`、`ActiveState`、`MainPID`,仅在进程为零、状态不活动且明确 `not-found` 时接受该停止返回值;其他停止错误仍拒绝。新建隔离轮次重新执行后通过。失败记录没有重置,正式服务进程标识和正式主程序摘要均保持不变。
原有浏览器、凭据轮换、外部邮件最终收件、迁移进程重启、容量和异机灾备等门禁仍保留,详见 `生产补充验收记录.md`。