7.1 KiB
SQL 切库与部署回归补充验收
结论与边界
北京时间 2026 年 9 月 21 日,在云服务器隔离实例补齐 MySQL/PostgreSQL 同引擎切库及部署脚本回归。正式 /opt/smail 未替换、正式数据未清空,共享数据库及其他项目未停止。
- [✓] MySQL/PostgreSQL 同引擎切库、内部重启及真实系统级进程重启。
- [✓] 目标账号不可用时明确停止,不回退源库;恢复账号后可以重新启动。
- [✓] 三阶段真实 SMTP 收信、IMAP/POP3 读取旧新中文正文及附件。
- [✓] 源库不再有应用连接且逻辑内容不变,配置历史完整保留在目标业务库。
- [✓] 两种 SQL 运行目录均没有 SQLite 文件。
- [✓] 验收器 55 项本地回归;云端扩大部署回归 125 项、零跳过。
- 跨引擎应用级迁移重启矩阵、复制/提交途中进程被杀后的恢复。
- [!] 其他生产门禁尚未关闭,不能据本记录宣布整个项目审计或部署验收通过。
迁移源库保留为离线证据,不是第二个活动业务库,未经许可不删除。最终运行仍是 SQLite/MySQL/PostgreSQL 三选一,业务数据与配置历史使用所选同一数据库。
工具与权限控制
test/tools/runtime_sql_migration.py 通过正式迁移 API 完成切库。目标数据库不预创建;目标账号随机生成,只服务于本轮测试。连接凭据通过标准输入或子进程环境传递,不输出到证据或命令行。
- MySQL 仅对准确的目标库授权,下划线转义,不授予全局权限。
- PostgreSQL 为创建目标库临时授予
CREATEDB,迁移准备完成即撤销,结束后禁止登录。 - 只锁定本轮目标账号模拟故障,不停止共享数据库、不操作正式账号。
- 独立限时网络单元保持迁移前后的网络命名空间,避免把数据库桥接随应用进程替换丢失误判为应用缺陷。
- 全表行摘要证明源库不变,配置历史按完整行比较;MySQL 对字段取十六进制并保留空值,避免二进制字段破坏摘要序列化。
结果中的 temporary_creation_privilege_revoked=true 对 MySQL 是无操作的通用阶段标记;MySQL 从未获得 PostgreSQL 式全局临时建库权限,不能据此宣称撤销过这种权限。
云端结果
| 项目 | MySQL | PostgreSQL |
|---|---|---|
| 实例目录后缀 | 20260921-053754 |
20260921-054131 |
| 内部重启前后 PID | 774526,不变 |
775325,不变 |
| 系统重启后 PID | 774998 |
775619 |
| 故障恢复后 PID | 775106 |
775757 |
| 三阶段源/目标连接数 | 各为 0 / 3 |
各为 0 / 3 |
| 目标账号不可用 | MainPID 为零、failed、退出码 1 | MainPID 为零、failed、退出码 1 |
| 源/目标配置历史条数 | 1 / 1,完整行一致 |
1 / 1,完整行一致 |
| SQLite 文件 | 零个 | 零个 |
| OOM、资源采集错误、清理错误 | 全部为零 | 全部为零 |
实例完整路径前缀为 /var/tmp/smail-review-runtime-。内部重启保持 PID、InvocationID 和旧会话;系统级重启验证 PID、InvocationID 改变。待重启阶段拒绝业务写入,返回 DATABASE_RESTART_REQUIRED。故障阶段配置仍指向目标库;账号恢复后读写成功,新增邮件仅出现在目标库。
本地证据目录为 test-log/production-remediation-20260921/sql-migration-runtime-evidence/。汇总为 sql-migration-results-20260921-053743.json;05:57 再次核对两轮共 26 份本地与远端文件摘要,记录为 sql-migration-recheck-20260921-055702.json。
数据库桥接、应用和网络锚点均已停止,源/目标账号均锁定。05:57 再次查询数据库系统表,四个账号状态符合收尾要求,PostgreSQL 两个账号均不可登录且没有建库权限;远端记录为 /var/tmp/smail-sql-account-check-20260921-055742.json。
扩大部署回归:保留失败及修正过程
本地 Windows 执行 test/deployment/test_*.py 时,86 项中有 2 个失败和 2 个错误:Linux fcntl 不可用以及平台路径分隔符差异。不能把它记录为全部通过,也未通过跳过测试消除问题。
| 云端隔离目录 | 结果与原因 |
|---|---|
smail-review-python-20260921-054448 |
105 项、8 个错误;证书测试要求隔离 root 身份及文件所有权 |
smail-review-python-20260921-054632 |
125 项、1 个错误;隔离 Nginx 工作进程 setuid(65534) 返回 EPERM |
smail-review-python-20260921-055549 |
125 项、零失败、零错误、零跳过,退出零 |
诊断目录为 /var/tmp/smail-review-capabilities-20260921-055359/。实际 /proc/self/status 显示能力边界为 0xc1,但许可及有效集仅为 0x41,缺少 CAP_SETUID;独立 Python 降权探针与隔离 Nginx 的系统调用跟踪均复现权限错误。这里确认的是实际进程缺少能力,不推断未经验证的系统组件内部原因。
仅增加 AmbientCapabilities=CAP_SETUID 后,有效集恢复为原边界内的 0xc1,小探针成功降权至 UID 65534,随后另建空工作目录重跑完整套件通过。能力边界仍仅为 CAP_CHOWN CAP_SETUID CAP_SETGID;保留私有网络、只读系统、隐藏正式目录及服务控制接口、禁止新增权限等隔离属性,没有使用完整 root 能力。
最终隔离单元内存限额 96 MiB、交换限额零、CPU 25%、进程数 32、运行时限 120 秒;实际运行约 7.6 秒,内存峰值 28.3 MiB,无交换使用。真实隔离 Nginx 换证重载与失败回滚用例通过,不能扩大为正式 Nginx 或全协议换证已经完成验收。
最终日志为 python-linux-tests-20260921-055549.log,SHA-256 为 05e48d2669483bcfd921af5cfd46e6db1d90e012ed77ea294a4bcb8200a07581,已与远端文件核对。摘要为 python-linux-result-20260921-055549.json。测试日志中的续期提示来自受控测试路径,不证明实际申请了公网证书。
构建与正式环境保护
候选版本 review-sql-migration-20260921,构建源码标识 375ffcabfea607690ea89e31b72cc346cfc122f3,主程序 SHA-256 为 6b700120e8bc0b6ca1ce723d0319852259d4aa983646bbc69c4c2fe73e88309d。sql-migration-build-manifest.json 记录产物与验收工具摘要,restart-source-manifest.json 覆盖 610 个后端 Go 文件。
本次重新编译后端,复用此前未改动的前端与 Linux 插件资源,不声称本次再次构建前端。新增工具和测试提交为 4844762;它不修改应用源码,不能混淆为候选二进制已经包含不同的应用修改。
复核时正式 Smail、Nginx、MySQL、Scert、FDM、PostgreSQL 六个服务 PID 和 InvocationID 均与测试前一致,没有运行中的本轮测试单元。正式主程序摘要仍为 c14ef1c5ba1dc2096c312c8357039a7330c985cbeac90765aed01d8151e05e1f。用户原有 云端全项目测试报告.md 保持不变,不加入本次提交。
历史凭据撤销轮换、剩余静态安全告警、真实浏览器、正式管理员交互及 Windows 提权 IPC、DNS 自动续期与失败告警、外部邮箱最终收件、外部插件、长时容量与异机灾备仍需后续证据。本轮没有将其中任何一项改为通过。