Scert/doc/Linux生产流程测试记录.md

2112 lines
240 KiB
Markdown
Raw 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.

# Linux 生产流程测试记录
## 2026年10月2日18:35 后台原子收尾整改(未部署)
- [x] 上批 background-full.log 与 background-vet.log 已取回:BACKGROUND_FULL_EXIT=0、BACKGROUND_VET_EXIT=0。第二台既有服务 active,进程检查仅保留系统 MySQL;磁盘剩余约 1014MB,不新增数据库实例。
- [!] 第一台 scert-report-race-20261002-1801 的 Result=timeout,运行十分钟退出一;内存上限 512MB、CPU 上限 50% 实际生效。没有竞争检测通过结论,无残留测试/编译进程,Scert、Nginx、MySQL active,可用内存约 903MB。
- [x] 修改前已推送 cf3bc7a。后台原子化测试归档 SHA256 为 d32c8d1849ebbd542523006cb131275e2f6ed925401e143ac69d4f1dc5a3ff40,两端一致;旧实现 background-atomic-before.log 退出一,报告缺失、任务/尝试/审计部分提交被复现。结果写入失败场景还触发重复重试后的同名触发器错误,测试已改为幂等安装触发器,不将该错误作为独立产品缺陷。
- [x] 修复归档 background-atomic-fix-20261002.tar,SHA256 为 78a140e9f16f83ca3eeac7e86635afe6f199cc1351210b48589f9ece7acb1123,两端校验通过;background-atomic-after.log:应用 21.663 秒、仓储 10.407 秒、HTTP 29.936 秒,BACKGROUND_ATOMIC_AFTER_EXIT=0。
- [x] 新测试覆盖五处写入故障整体回滚、每次重试完整报告、重试等待取消、禁止重试重新激活取消任务、无尝试收尾不能绕过活动尝试,以及无尝试路径的审计失败回滚。
- [x] background-atomic-full.log 与 background-atomic-vet.log:全后端测试及 go vet 均通过,BACKGROUND_ATOMIC_FULL_EXIT=0、BACKGROUND_ATOMIC_VET_EXIT=0;既有三项服务 active,磁盘剩余约 961MB。外部数据库未启用时的全量测试不当作三库实库回归。
- [x] background-atomic-repeat.log:新增收尾和取消专项重复二十次通过,应用 48.982 秒、仓储 49.985 秒,BACKGROUND_ATOMIC_REPEAT_EXIT=0;普通重复测试不替代竞争检测。
- [!] 本批仅隔离源码开发及自动回归,不修改正式程序和线上数据库;未运行三库首次 Web 或正式部署,不代替生产验收。
## 2026年10月2日18:11 后台取消与 MySQL 报告迁移回归(未部署)
- [x] 新增原生 Go 回归 TestCertificateCancellationPreservesDeploymentOutcome;background-cancel-before.log 中四个场景全部失败,BACKGROUND_CANCEL_BEFORE_EXIT=1。三个场景被错误标为 cancelled,已恢复场景未保存逐服务器结果。
- [x] 修复为部分成功优先、恢复未确认/失败不被取消掩盖,并使用独立十秒收尾上下文保存实际结果。background-cancel-after.log 重复二十次通过,BACKGROUND_CANCEL_AFTER_EXIT=0,耗时 17.847 秒。普通重复回归不替代竞争检测。
- [x] 新增原生 Go 实库测试 TestMySQLRealTaskReportMigrationRecovery;使用系统已有 /usr/sbin/mysqld 8.0.46,不安装额外数据库,不接受已有实例连接串。运行前检查磁盘余量;新实例仅监听 127.0.0.1:3887,严格 TLS,测试使用随机凭据。
- [x] report-mysql8.log:三条迁移恢复路径与五类冲突全部通过,REPORT_MYSQL8_EXIT=0,耗时 49.100 秒。覆盖超过普通 TEXT 容量的中文报告保持、主键顺序、可空字段、存储引擎及额外字段;冲突重复拒绝且结构和报告不被覆盖。
- [x] 实库产物保留于 /var/tmp/scert-report-mysql-20261002-1808/scert-mysql-integration-1850050069;没有连接或清理线上业务数据库,没有创建辅助验收脚本。
- [~] background-full.log 与 background-vet.log 为新增后台修复后的全量回归,当前尚待收集最终结果。
- [~] 第一台从干净源码归档 4e8d509 启动受限竞争检测,归档 SHA256 为 401d480814aa3f777fcb51b36b7837ced08ba6964475b2d996066167da48eec8。独立单元 scert-report-race-20261002-1801,MemoryMax=512M、MemoryHigh=384M、MemorySwapMax=0、CPUQuota=50%、RuntimeMaxSec=600;限定任务与 HTTP 异步专项,不扩展成全项目竞争检测通过。
- [!] 当前 MySQL 5.7、PostgreSQL 新迁移、后台全报告原子化、其他长操作、关停 502、通知重置及统一生产门禁仍待完成;本批未正式部署。
## 2026年10月2日17:56 完整报告与审计事务回归(未部署)
- [x] 源码 4e8d509 已推送;第二台隔离目录 /var/tmp/scert-async-20261002-1556/source 执行,不修改正式安装目录或线上数据库。
- [x] 最新输入 async-report-20261002-1743.tar 在两端核对 SHA256 为 0b3ec412aa6d8d0c93e03d4c01b067ebe8e6047ecfc03eea4a0cdf4a9a2158d8;第一次等待密码的 SCP 已断开,确认终止后重新上传,重传退出 0。
- [!] report-first.log:第一轮失败,仓储测试错误绕过正式重试入口,SQLite 取消测试在单执行线程下没有覆盖取消;已改用正式重试入口和显式让出调度,未放宽终态或取消覆盖断言。
- [!] report-second.log:仓储和数据库通过,HTTP 三项失败;修复重复完成审计及 failed 到 error 的既有审计状态契约,更新启动审计失败时真实失败审计的断言。
- [x] report-third.log:仓储 9.488 秒、HTTP 30.477 秒、数据库 31.531 秒,REPORT_THIRD_EXIT=0。覆盖完整报告重读、重试隔离、权限撤销、脱敏及报告/逐目标/审计故障整体回滚;使用隔离 SQLite,不当作 MySQL 或 PostgreSQL 实库通过。
- [x] 本机 .runtime/async-report-third-frontend-20261002.log:256 项通过,零失败、零跳过;.runtime/async-report-third-build-20261002.log:生产构建通过,退出码均为 0。
- [x] report-full.log 与 report-vet.log:全后端测试与静态检查通过,REPORT_FULL_EXIT=0、REPORT_VET_EXIT=0;使用 nobody、GOMAXPROCS=1、GOMEMLIMIT=384MiB、-p 1。未启用外部数据库的测试不计实库通过。
- [!] 本批无正式部署、无首次 Web 验收、无竞争检测通过结论。第一台旧竞争检测已中断退出 130,不重启无限制负载;不新增公网端口、不放宽 TLS 或 SSH 主机校验。
## 2026年10月2日17:11 任务收尾事务与服务配置回归(未部署)
- [x] 第二台隔离源码首次关联回归:internal/httpapi、internal/repository、internal/servicehost、internal/app 全部通过,COMPLETION_FIRST_EXIT=0;日志为 /var/tmp/scert-async-20261002-1556/completion-first.log。
- [x] 补充回滚失败的取消边界后,使用非特权用户、GOMAXPROCS=2、包并行数一执行完整后端测试和 go vet,COMPLETION_FULL_EXIT=0、COMPLETION_VET_EXIT=0;日志为同目录 completion-full.log 与 completion-vet.log。
- [x] 终态仓储失败注入、任务与尝试一致性、启动恢复接管、取消不覆盖实际结果、动态及静态单元预算断言均纳入本批测试;数据库用例为隔离 SQLite,未触及线上业务库。
- [~] 同目录 completion-repeat.log 正在记录异步、终态事务及服务配置二十次普通重复回归;completion-unit.log 将记录静态单元检查。
- [!] 第一台 race-second.log 的竞争检测尚未取得退出状态。新建本机 SSH 会话无握手结果;从第二台按固定 ED25519 指纹和密码方式连接同样在 banner exchange 超时。原执行会话无输出,已发送中断但无确认,不以本地句柄存活断言远端仍正常运行,也不擅自重复启动测试。
- [!] Git 推送出现两次连接超时,本地提交 9e666be 已保存。网络问题不能算产品用例失败,也不能掩盖缺失的竞争检测证据。
- [!] 本批没有安装线上程序、清空业务数据库、开放外部端口、关闭 TLS 校验或创建辅助验收脚本。最终构建部署、三数据库首次 Web 流程和生产复审仍需独立完成。
## 2026年10月2日16:47 异步任务开发回归记录(未部署)
### 已完成的开发回归
- [x] 本机前端:npm.cmd test 为 252 项通过、零失败、零跳过;npm.cmd run build 通过。日志为 .runtime/async-frontend-20261002-final.log 与 .runtime/async-frontend-build-20261002-final.log。
- [x] 第二台隔离源码、非特权用户执行 go test ./... -count=1 和 go vet ./... 均返回零;日志位于 /var/tmp/scert-async-20261002-1556/backend-full.log 与 backend-vet.log。首次旧同步接口断言失败已修复,原始日志保留。
- [~] 第二台竞争检测因未启用 CGO 且无本机编译器未执行;第一台首次离线代理缺少依赖也未执行用例。已复用第二台公共模块缓存、三端校验归档摘要一致,在第一台启用 CGO 后重新执行异步专项竞争检测二十次,等待结果。
### 证据边界和剩余任务
- [~] 复核发现分发单元与动态安装模板的停止预算尚未同步,继续修复并回归,不以静态文件修改代替实际安装行为。
- [!] 本批未替换线上候选,未执行浏览器首次初始化,未清理线上数据库,未新增辅助验收脚本,也未降低 TLS 或 SSH 主机身份校验。
- [ ] 正式候选统一部署后仍需验证长任务超过反代等待窗口、断线后查询、取消回滚、进程重启恢复及三数据库首次完整 Web 流程。
- [!] 下方 504 与 502 为真实历史失败,异步开发回归通过并不自动关闭实机问题;旧事务、系统通知、备份恢复及总体生产门禁继续保留。
## 2026年10月2日15:31 正式候选实机负向恢复与重试
### 范围和准备
- [x] 沿用已部署 7ba4046 正式候选,复用 scert-multi-20260930.apmmk.cn 当前证书,不重新签发。第二台服务器配置经正式 API 临时使用九十秒等待后失败的校验命令,重载命令不变;未创建脚本文件或新增远端备份。
- [x] 故障前无活动任务,目标目录无事务残留;证书和证书链 SHA256 为 7e8b56227b345ed9b66659e428f9bd4421604f11253ab88d65b3ed61737f35cf,私钥仅记录摘要,不输出内容。
### 摘要不匹配与锁归属不匹配
- [x] 正式部署任务 e3a9e1c8-c930-4fa0-b2aa-2730da019503,第二台事务 20261002-072057.661993684-c999333470ae。只读确认 installed/pending、尚未重载及等待进程存活后,向本事务生成的 cert.pem 旧文件追加一个换行。注入前校验事务记录摘要、普通文件类型、大小与 inode;第一次因 SQL 路径转义错误拒绝注入,修正后才执行。
- [x] 15:22:37 第一次尝试失败:恢复备份身份不一致,状态 recovery_required。注入后及任务终态的整个目标目录快照一致,输出 PRESERVED_EXIT=0,产品未覆盖或删除未知内容。
- [x] 按 inode、大小、注入后摘要核对后,仅截去本次追加的一个字节;恢复原摘要成功。随后核对锁标识与事务证据一致、文件类型和六十四字节长度,受控替换本事务 cert.pem 锁标识,未改数据库。
- [x] 15:24 重启正式服务:第二台因部署锁不属于本次事务而拒绝恢复,OWNER_PRESERVED_EXIT=0;第一台事务 20261002-072230.603680358-5718e8942e14 自动 rolled_back。第二台进程变为 39183。
- [x] 核对 inode、长度及受控标识后还原原锁内容,未手工释放锁。15:25:03 再次由正式服务启动恢复,两事务均 rolled_back,第二台进程 39281;目标三个文件恢复基线摘要,本次锁、临时文件和旧文件均由产品收尾。
### 原任务重试与最终状态
- [x] 重启后的旧会话返回 401;通过正式登录接口重新认证,再经正式服务器 API 恢复标准 nginx -t 与 nginx -s reload,持久化配置确认两台一致。
- [x] 同一任务第二次尝试于 15:27:36 成功,正式重试接口 HTTP 200;新事务 20261002-072722.386340837-1d6236abe88e 与 20261002-072731.782859344-4f9f668d2629 均 committed。成功后再次重试 HTTP 409,任务尝试仍只有首次失败及第二次成功。
- [x] 15:28 双机三个文件摘要一致,本次事务无残留;两台本地回环 SNI、系统 CA 和主机名验证通过,第一台 Scert、Nginx、xmail 均 active。双机 Scert 仍为 127.0.0.1:8443;第一台访问第二台普通公网域名 readyz 为 HTTP 200、TLS 校验零。内部 SNI 检查不替代第一台公网平台限制验收。
- [x] 当前活动任务为零、历史缺证据 recovery_required 仍为两条,未补造证据。管理证书续期阈值仍为六天、十天。
### 保留的失败和新问题
- [!] Nginx 日志确认 15:21:56 初次部署请求 upstream timed out,HTTP 504、响应为 HTML;任务到 15:22:37 才实际失败。浏览器探测的 JSON 解析错误不是任务终态,需要整改正式长任务提交及查询机制,不通过扩大超时掩盖问题。
- [!] 15:24:19 受控重启期间 readyz 返回 502,原因是上游连接拒绝;后续严格 HTTPS 首页及 readyz 恢复 200。该中断不能记作无中断升级通过。
- [~] 本轮为保留数据的故障注入复核,没有清库,不替代首次完整 Web 流程、邮件通知、备份恢复及最终生产验收;上述局部实机证据等待复审。
## 2026年10月2日15:15 管理入口接线修复与正式轮换
- [x] 14:58 环境维护:在既有协调锁和辅助激活锁保护下,使用上游现有受信材料修复 Nginx 专用引用,配置检查后平滑重载。旧事务摘要保持不变,旧证据仅保存本机,远端未新增备份;该人工接线步骤不算产品自动协调验收。
- [x] 15:03:15 正式 Web 策略更新触发 management_renewal 任务 a1c7ffd8-5623-4740-9deb-b7cdef88c3f2;审计终态 success 时间为 15:03:31,任务阶段 activate。ACME 复用了仍有效的授权,未重新执行 DNS 挑战,因此本次不能增加 DNS 新挑战通过结论。
- [x] 应用 coordination.json 的 phase 与辅助 transaction.json 的 status 均为 committed;request_id、proxy_id 与辅助 id 均为 6ce45429e2cbef69f9083dc2a88147c6。旧失败预检任务 f11c8768-b280-4f7e-ace2-26a132af038a 继续保留。
- [x] 代理 443 与上游 8443 实际新叶指纹均为 237e637e60f5553a8dba343340ed63a4db20cb5a14ec02eff9381aa2ba3c7d6d;有效期至 2026-12-31 06:04:52 UTC。普通域名首页、readyz 严格 HTTPS 返回 200;nginx -t 通过。
- [x] Scert 主进程仍为 37512,监听 127.0.0.1:8443,未新增公网端口。辅助新版本目录 root:root 0700,私钥 root:root 0600、单硬链接。
- [x] 15:14 经正式 Web 保存原续期阈值六天、临近阈值十天;只读读取持久化配置确认。管理续期仍只有本次成功及此前失败两条,没有再次签发。
- [~] 本次未清库、未运行三数据库首次流程,不代表故障恢复矩阵或整体生产门禁通过;正常轮换结果等待独立复审。
## 2026年10月2日14:52 独立辅助程序与管理续期安全预检
- [x] 第二台独立辅助程序于 14:26:38 完成原子替换,核心服务和 socket 恢复 active。当前程序 SHA256 为 651caa143f7ffcb6411ad9231e7e3418c43a65088ee11fa5a10173961c33e88e,与正式候选一致;unit 模板一致,未改 unit。旧程序仅下载到本机,远端未新增备份。
- [x] 升级前后策略 SHA256 为 5b701811a3d6c0759f5f4e6cd440a7c3b1ab127640ed6f5606767a9786755876,历史事务为 36ea481ae6e9cc1bfd0da5450a2d604c9ea46a759a05ae62f7b58a873ccdd76f,引用为 8b58e905141176791ad155e366495213c5a08da0ba440801313486174f334cfc。升级没有重建状态或证书目录。
- [!] 14:45 至 14:47 使用带主机名验证的 OpenSSL 分别核查回环代理与上游:代理叶指纹为 1D5A7F2C2E6D229DF56F87CFE18DD37C1E4C9638C8B56F99AB50022872D526A0,到期 2026-12-24 15:56:36 UTC;上游为 1C033D1628968235132D9D6E5B0AAEC1C9411C5C884554950A83AA7B3759B8CC,到期 2026-12-28 19:43:37 UTC。二者不一致,不认定协调就绪。
- [x] 14:48 经正式 Web 系统设置保存续期阈值八十八天、临近阈值九十天,策略更新自动触发调度。管理任务 f11c8768-b280-4f7e-ace2-26a132af038a 终态 failed、阶段 preflight;审计保留 running 与 failed。辅助程序实际由 socket 启动,主进程 38462;核心日志记录本地激活操作未完成。未重复发起签发或使用自制协议脚本。
- [x] 已通过同一 Web 表单恢复阈值六天、十天,14:50 只读核查持久化配置一致。原证书签发任务计数不变,管理任务只有上述一条;策略、旧辅助事务及引用摘要不变,未产生应用协调记录。此为签发前拒绝的负向证据,不是正常轮换成功。
- [x] 普通域名首页和 readyz 继续返回 200 且 TLS 校验为零,Nginx 配置检查通过;双机 Scert 均仅监听回环 8443,未新增公网端口。
- [~] 本轮为保留数据的升级及负向复核,没有清业务库,不代替三数据库首次流程。接线一致性、正常双层提交、故障恢复与整体生产门禁仍待完成。
## 2026年10月2日14:20 候选正式构建与双机核心升级
### 构建与候选检查
- [x] 源码提交:7ba4046135bae9d55468c5c132019073db280c37。第二台 `/var/tmp/scert-recovery-release-20261002-1401/source` 为独立干净检出,正式构建入口返回 `RELEASE_BUILD_EXIT=0`,manifest 的 source_dirty=false,构建后 Git 状态仍干净。
- [x] 正式归档 SHA256:042f25acc87d8a68b0629f0b0bbacaa987204c00776e67ba0f5c3ddf2fd58edb。第二台、本机、第一台传输摘要一致,两台从正式归档解包后 SHA256SUMS 均通过,安装器具备归档声明的执行权限。
- [x] 前端测试日志 `frontend-test.log`:二百四十项通过、零失败、一项 Windows 专用发布脚本用例跳过。Linux 范围不把该项记为通过。
- [x] 候选全量模块检查 `module-verify.log` 返回 `RELEASE_MODULE_VERIFY_EXIT=0`,输出 all modules verified。第一台额外的全量模块检查因离线缓存仅含竞争用例所需模块而失败,不能记为全量模块通过;此前竞争检测本身退出零,范围不变。
- [x] `listen-negative.log`:发行程序使用 nobody、独立且不存在的配置路径,0.0.0.0、::、192.0.2.1、localhost 均退出 1,未创建配置文件;没有开启额外公网监听来完成测试。
### 第二台管理入口升级
- [!] 北京时间 14:09:05 首次误从构建工作目录调用安装器,因文件无执行权限退出 126,产品安装未发生;停服已发生,立即恢复旧服务,14:09:34 严格域名 readyz 恢复成功。失败保留在 `install-second.log`,这次额外停服不得从记录中隐去。
- [x] 随后使用正式归档解包目录 `package-second`,14:11:04 开始升级、14:11:06 服务 active;`install-second-archive.log` 对应 `SECOND_ARCHIVE_UPGRADE_EXIT=0`。未通过 chmod 构建目录文件来替代正式归档校验。
- [x] Scert 主进程 36639,Type=notify,监听 127.0.0.1:8443;首页与 readyz 的普通域名 HTTPS 均为 HTTP 200、TLS 校验零,HTTP 入口为 301。后续五次 readyz 均 HTTP 200、TLS 零、curl 退出零。
- [x] Nginx 与 MySQL 主进程分别仍为 792、775,Nginx 配置摘要未变;Scert-admin 保持 inactive。
### 第一台部署目标升级与范围
- [x] 第一台正式归档与包内摘要通过,14:13:30 至 14:13:32 完成核心升级,`install-first-archive.log` 返回 `FIRST_ARCHIVE_UPGRADE_EXIT=0`,Scert 主进程 25145,只监听 127.0.0.1:8443;未新增管理站点。
- [x] Nginx、MySQL 主进程仍为 1015、1092,Nginx 配置摘要未变;xmail.service 升级前后均 active,未对它执行停止、重启或配置操作。首次只读查询误用大小写不同的 Xmail.service,随后按实际单元名核实,不据此误报邮件服务故障。
- [x] 双机 scert、scert-admin、scert-storage 与对应候选包逐字节一致;主程序 SHA256:d7e05a0dcf2afc06b68dc00959fe2057eb40cc54bbfdabb26b12f84bcc88073f。
- [x] 双机 unit 校验均退出零。升级后双向 IPv4 探测对方的 8443、3306、33060 均超时未建立连接,仅证明这两个来源的对应路径,不能扩大为全部来源或 IPv6 外部探测结论。
- [x] 第一台跨机普通域名 https 检查 odacm.cn 的 readyz 返回 ready、HTTP 200、TLS 零、curl 退出零。
- [ ] 第二台独立 `/usr/local/libexec/scert-nginx` 仍记录旧提交 431fd043,和候选包摘要不同;普通安装器不安装该独立组件,本轮未改其程序、策略或事务。当前只读事务状态为 rolled_back,socket active;这不是新候选的双层协调验收,后续需按独立升级流程完成配套验证。
- [~] 本轮没有清业务库、没有执行浏览器首次流程,没有删除历史锁或事务;P0-10 业务负向矩阵、升级停服窗口、系统通知、三数据库首次流程和最终生产门禁仍未通过。
## 2026年10月2日13:59 测试隔离修复与完整检出回归
- [x] 十个测试文件共二十一行调整:重置状态文件、敏感配置及 SQLite DSN 均进入临时目录;共享连接池和子进程仍复用同一个测试数据库。不改产品行为、不提权写正式目录。
- [x] `/var/tmp/scert-recovery-full-20261002-1344/full-backend.log` 保留归档复测失败:三个原路径污染包已通过,仅发布边界测试因没有 Git 元数据失败;同目录 `full-vet.log` 退出零。
- [x] `/var/tmp/scert-recovery-checkout-20261002-1348/source` 为完整 Git 检出 3b25449,叠加十个测试文件修复;nobody 执行 `go test ./... -count=1`,输出 `CHECKOUT_FULL_EXIT=0`,`full-backend.log` 记录三十一个通过包。未跳过发布边界检查。
- [x] 13:55 双机监听核查:Scert 均仅监听 127.0.0.1:8443,Nginx 占用 80/443,Scert 与 Nginx 均 active;未改公网端口或共存业务。
- [!] 第一台 `/var/tmp/scert-recovery-race-20261002-1345/race.log` 的竞争检测未完成:依赖下载连接持续 SYN-SENT,13:58 中断后退出 130,不能记为通过。
- [x] 缓存归档三端 SHA256 一致,使用独立离线代理及新 GOPATH 执行 `go test -p 2 -race ./internal/deployment -run 'TestRecovery|TestJournal' -count=20`;保留 go.sum 校验,13:58:59 返回 `OFFLINE_RACE_EXIT=0`,`race-offline.log` 记录通过。仅覆盖恢复与事务用例,不声称全后端竞争检测通过。
- [x] 完整 Git 检出目录重新执行全后端 go vet,日志为同目录 `full-vet.log`,返回 `CHECKOUT_VET_EXIT=0`。
- [~] 本批修复未正式构建部署,线上仍为此前 1150c39 对应版本;未清业务库,未显式开启外部数据库用例,不扩大为三数据库生产验收。
## 2026年10月2日13:41 上传归属与扩大回归
- [x] 同一隔离目录的 `upload-baseline.log`:带事务日志的上传碰撞保护和未知临时文件恢复保护两个场景均失败,退出码 1,确认原实现会误删。
- [x] 增加临时文件归属摘要字段,只有独占创建确认后才登记并持久化;回滚及提交恢复在任何清理前核验完整临时文件集合。旧日志仍有临时文件但缺少归属证据时保留现场,无残留文件且其他证据完整的旧事务仍可恢复。
- [x] `upload-fixed.log` 中 deployment、repository、app 均通过,退出码 0;`upload-repeat.log` 中恢复及事务用例重复二十次退出 0;`full-vet.log` 全后端静态检查退出 0。
- [x] 真实回环密码 SSH/SFTP 用例扩展为七个场景,包括临时文件变更和归属未落库,不能用此替代业务服务器完整恢复流程。
- [!] `full-backend.log` 全后端测试退出 1:admin 重置状态文件、certificate 敏感信息目录及部分 database 测试仍错误使用 `/scert` 正式路径,被 nobody 拒绝;发布边界测试缺 `.gitignore` 及正式构建入口。没有提权或放宽生产权限,待测试隔离修复及完整源码复测。
- [~] 第一台只读检查发现已有 gcc,下一步可在独立非特权目录补做 Linux 竞争检测;此前第二台的失败不改写为通过。尚未构建部署本批修复。
## 2026年10月2日13:30 恢复预检原生 Linux 回归
本轮为第二台隔离源码测试,不是清库首次生产验收。使用 nobody、Go 1.26.6 和临时目录;未修改业务数据库、服务配置、Nginx 或公网端口。线上仍为此前 1150c39 对应发行包。
- [x] 隔离证据目录:`/var/tmp/scert-recovery-preflight-20261002-1308/`。`baseline.log` 记录旧代码八个不可信状态、两个部分解锁重入场景失败,退出码为 1。
- [x] 修复后部署包完整测试退出零;`regression-final.log` 中 deployment、repository、app 三包均通过,退出码为 0;`vet.log` 对应三包 go vet 退出零。
- [x] 新增八个不可信状态、两个已验证阶段重入、六个合法中断状态、HTTPS 失败后再恢复、五个认证前身份检查及五个真实回环密码 SSH/SFTP 子场景。SFTP 测试检查磁盘内容和权限,拒绝不可信现场时不能有文件变更。
- [x] `repeat-final.log`:`go test ./internal/deployment -run 'TestRecoveryPreflight|TestRecoveryVerifiedCleanup' -count=20` 实际退出零。
- [x] 首次扩大回归中仓储测试错误尝试写入 `/scert/data`,被 nobody 拒绝;六个相关测试改用临时 SQLite DSN 后完整重跑。未提权、未放宽正式路径权限;原失败保留在 `regression.log`。
- [!] `race-repeat.log` 首次因 CGO 未启用退出 2;`race-repeat-cgo.log` 显式启用后因找不到 gcc 退出 1。均未完成竞争检测,不记为通过。
- [x] 北京时间 13:25 第二台 Scert 为 active,Scert 8443、MySQL 3306/33060 均仅监听 127.0.0.1;这不代表修复版已经部署。
- [~] 上传碰撞与事务日志组合仍需核查,本批正式构建部署、业务实机负向恢复和历史事务证据核查待完成。可选真实数据库用例未显式开启,不宣称三数据库验收通过。
## 2026年10月2日12:54 双机正式候选升级记录
本节属于升级与安全复核,不是清库首次生产验收。未开放额外端口,未修改 Nginx,未关闭 TLS 或 SSH 主机指纹校验,未创建辅助验收脚本。只使用正式安装器自带发布事务,没有额外远端备份。
### 正式候选及拒绝路径
- [x] 原生 Linux 构建实际退出零,源码提交 1150c3904eb82d9274fbedf7bfd3e6d402623216,source_dirty=false,local_path_check=passed;四个二进制内嵌同一提交、vcs.modified=false、trimpath=true。
- [x] 专属 TMPDIR 消除了通用 /tmp/ 与安装器目录拒绝规则的误匹配;未修改路径检查代码,原有检查继续覆盖实际构建目录和临时目录。
- [x] 正式归档 SHA256:cf0529095eefc39de44d55da95fbd196a7340773c8e9701b9bcdf15b308126c9。双机传输后归档和包内 SHA256SUMS 全部校验通过。
- [x] 发行程序以非特权用户、独立且不存在的配置文件路径启动,0.0.0.0、::、192.0.2.1、localhost 均被明确拒绝,退出码为 1,未创建该配置文件。
### 第二台管理入口
- [x] 12:47:55 开始升级;12:47:56 主进程启动,12:48:01 收到就绪通知后进入 active。Type=notify、NotifyAccess=main,状态文字明确要求通过 readyz 检查业务就绪。
- [x] 主程序仅监听 127.0.0.1:8443;严格普通域名 HTTPS readyz 返回 ready、HTTP 200,首页 200,HTTP 入口 301 跳转 HTTPS。12:51 后续连续 10 次 readyz 均 HTTP 200、TLS 校验零、curl 退出零。
- [x] Nginx 配置摘要未变,Nginx 与 MySQL 主进程编号均未变。
- [!] 第一台跨机连续 60 次采样包含 7 次 HTTP 502,发生在 12:47:56 至 12:48:00;12:48:01 恢复 200。另有 2 次一秒连接超时发生于升级窗口外,不能仅凭该采样归因于 Scert,也不将 HTTP 000 时的默认 TLS 数值解释为校验成功。其余 51 次为 HTTP 200。
- [!] 启动通知已真实验证,但单实例停机窗口仍存在,不能声明零 502 或无中断升级。
### 第一台部署目标及边界
- [x] 12:51:39 开始升级,12:51:40 进入 active;同样收到真实就绪通知,仅监听 127.0.0.1:8443。未新增管理站点,未将其默认站点当作管理入口验收。
- [x] 第一台 Nginx、MySQL、Xmail 主进程编号和运行态保持不变;未操作其他业务服务。
- [x] 两台已安装的 scert、scert-admin、scert-storage 摘要对应一致。主程序 SHA256:91f32ea0e6971c7c66668d395ec91bcb6e78ebbd60bee1ddd82910d9f8f14a8e。
- [x] 升级后双向 IPv4 探测对方的 8443、3306、33060 均未连接成功;仅作为这两个来源的 IPv4 路径证据,不扩大为所有来源或 IPv6 外部探测结论。
- [x] 双机 systemd-analyze verify 均退出零;主机已有 snapd 警告、第一台已有 cloudmonitor 警告保留,未修改其他服务配置。
- [!] 三数据库清库首次 Web 流程、系统通知实发与重置闭环、P0-10 负向恢复矩阵及最终统一验收仍未完成。
## 2026年10月2日12:42 Linux 回环监听回归
- [x] 第二台隔离源码目录以 nobody 用户、Go 1.26.6 执行 config、bootstrap、cli、app、serviceready、servicehost 六包完整回归,实际退出零;监听相关新用例重复 20 次通过,六包 go vet 退出零。
- [x] 覆盖默认与空值回环、IPv4/IPv6 通配地址拒绝、非回环 IP 和域名拒绝、环境覆盖后校验、无环境覆盖时历史通配配置拒绝、初始化及正式入口在建立资源前拒绝。
- [x] 首轮失败原因分别为旧配置测试误写 /scert/conf 以及隔离源码未复制 deploy 目录;修正测试临时文件路径并补齐原有部署文件后完整重跑。没有提权绕过权限,没有触碰生产配置或数据库。
- [x] 路径扫描命中的 install-linux.sh 与源码 SHA256 相同;命中内容为已有的 /tmp/ 禁止目录规则。下一次构建配置专属 TMPDIR,扫描仍覆盖实际临时目录,不删除检查项。
- [ ] 正式包构建、安装及运行态复核待进行,本节不计作清库首次生产验收。
## 2026年10月2日12:19 启动通知原生回归记录
范围为开发回归及旧线上候选只读检查,不是清库后的首次生产验收。未创建辅助验收脚本,未额外备份或删除现有文件,未开放端口,未部署新候选。
### 源码与执行环境
- 基线为 b6b9642,加本轮启动通知、生命周期测试与 CLI 测试路径隔离改动。
- 第二台 Ubuntu 22.04.5,原生 Go 1.26.6;测试用户 nobody,独立目录 /var/tmp/scert-startup-20261002-1206,复用既有工具链及模块缓存,未以 root 运行业务测试。
- 最终测试源码增量归档 SHA-256:1731da7eafa720398bcfe784277f00f8674ce5f6e5ec7c8ce28c7f4f59da8cea;仅作为本次回归源码传输核对,不是正式发行包摘要。
### 实际结果
- [x] serviceready、servicehost、bootstrap、cli 四包执行 go test -p 1 -count=1 -timeout 180s 全部通过;耗时分别为 0.005、0.004、8.820、7.070 秒,退出码零。
- [x] 正式服务通知顺序、通知失败、启动取消、真实初始化入口通知、初始化 readyz 503、测试路径隔离等选定用例重复 20 次通过;两包耗时 0.668、0.216 秒,退出码零。
- [x] 四包 go vet -p 1 退出码零。
- [x] systemd-analyze verify 对新 unit 返回零。Windows 源码归档传入的测试 unit 初次提示权限过宽,已在隔离目录收紧为 0644 后重验;仍存在主机原有 snapd RestartMode 警告,与本次 unit 无关,未修改共享主机其他服务。
- [x] 首轮 CLI 包出现正式目录权限拒绝,判定为失败而非通过;定位到旧测试的 config.Default 在 Linux 使用固定正式路径。修复测试配置显式隔离全部目录与 SQLite DSN 后重跑成功,未提权写入 /scert,未触碰线上业务数据。
- [x] 12:18:38 北京时间:Scert、Nginx 均 active,ss 显示 127.0.0.1:8443;正常域名严格 HTTPS readyz 返回 ready,curl 退出码零。此时线上仍为此前候选。
### 未完成边界
- [ ] 新候选正式构建、二进制与 notify unit 配套安装、真实 systemd 启动及重启期间 Nginx 采样。
- [!] 启动通知不能消除单实例停机窗口,不能将 active 解释为业务 readyz、无中断升级或零 502。
- [!] 三数据库清库首次 Web 流程、系统通知实发与重置闭环、P0-10 负向恢复矩阵及最终统一生产验收继续待办。
## 2026年10月2日运行态采样及阻塞归因补充
### 11:49 至 11:50 邮件入口独立 DNS 诊断
- [x] 从第二台执行普通 A/AAAA 查询及严格 HTTPS 请求,curl 退出码为 6,未进入 TLS 握手;不能把 ssl_verify_result 的默认零值解释为 TLS 校验成功。
- [x] 本机解析器、1.1.1.1、8.8.8.8 对 web.solit.cn 的 A 查询均返回 NXDOMAIN,SOA 序列号为 2026090620。
- [x] dns13.hichina.com、dns14.hichina.com 的非递归查询同样返回 NXDOMAIN,带权威应答标志。第二台权威查询首次 IPv6 传输超时,后续实际权威应答为 NXDOMAIN,不把超时当作不存在依据。
- [!] 用户提供的 Smail Web 入口当前无法解析;需确认新入口或修复 DNS 后,再通过正式系统通知授权流程取得参数。本轮未修改 DNS、Smail 配置或账号凭据。
- [x] 同时复核 Scert、Nginx 均 active,本机 Nginx 严格 TLS readyz 返回 ready。该结果不替代三数据库首次流程或生产总体验收。
本节记录北京时间 10:18 至 10:34 的只读诊断。未清库、未重启业务、未创建辅助验收脚本,不计作首次生产验收,历史记录保留。
### 第二台管理入口
- [x] 密码 SSH 进入远端交互 shell,执行 20 次 odacm.cn 严格 HTTPS 请求,解析至本机 Nginx。10:18:39 至 10:19:00 均返回 ready、curl 退出码零;访问日志对应 20 次 HTTP 200。
- [x] CN、SAN 均包含 odacm.cn,签发者为 Let's Encrypt YR2;有效期为 UTC 2026-09-25 15:56:37 至 2026-12-24 15:56:36。现有证书验证完成、等待复审,不代表续期轮换通过。
- [x] Nginx 上游为回环 HTTPS 8443,proxy_ssl_verify 为 on,SNI 为 odacm.cn,使用系统 CA;没有关闭 TLS 校验。
- [!] 较早采样因 PowerShell 到 SSH 的参数转义失败,出现额外主机解析错误,不采纳为证据;仅采纳后续独立采样与访问日志。
### 启动窗口及外部端口
- [x] 10:09:05 的 Nginx 错误明确为连接回环 8443 被拒绝;Scert 进程于 10:09:03 启动,Type=simple 显示 active 不等于 HTTPS 就绪。启动窗口仍未完成整改。
- [x] 第二台 Scert、MySQL 仅监听 127.0.0.1;防火墙默认拒绝入站,未见相关端口 NAT 转发,IP 转发及 route_localnet 均为零。
- [x] 第一台跨机探测第二台 80、443、22 成功;8443、3306、33060 在四秒限时内均超时。仅证明该外部来源的 IPv4 路径,不扩大为所有来源或 IPv6 结论。
- [!] 本地连接来自代理网络地址,短时抓包未观察到对应报文;不将本地 TCP 成功当作公网开放证据,也不单凭零报文证明全网隔离。
### 第一台角色与邮件阻塞
- [x] 第一台 Nginx 未见 odacm.cn 管理站点或 8443 上游。odacm.cn 探测返回默认 salg.cn 证书;改为 salg.cn 后 readyz 返回 404,不是 Scert 健康证据。不新增管理站点、不复制证书私钥,也不将默认站点证书不匹配误记为 Scert 签发失败。
- [!] Scert 页面显示 SMTP 未启用,主机、用户名为空且专用凭据未配置。Smail 系统通知状态接口提供真实连接参数,独立凭据受账号授权保护,不能用登录密码或猜测值代替。
- [!] 本地浏览器访问 Smail Web 入口连接关闭,仅证明当前路径失败,不证明服务整体离线,需独立复核。
本轮未新增生产通过结论。三数据库清库 Web 流程、Smail 实际投递与密码重置、历史恢复证据及重启窗口仍未完成。
## 2026年10月1日实机进度更新(MySQL 5.7)
本节仅记录非敏感测试结果,不写入口令、RAM 密钥、数据库密码、令牌或其他认证信息。测试均使用隔离目录和正式发行程序副本,未创建辅助验收脚本,未开放额外外部端口;总体生产验收仍未放行。
### 第二台 Linux 实机:MySQL 5.7.44 隔离实例
- 官方 MySQL 5.7.44 Linux x86_64 包 SHA-256:`798ff0a14cdc1ab259f8a3b6a7f683403e9bbaa85957d82e5370f82b27ba6805`。
- 隔离实例位于 `/var/tmp/scert-mysql57-20261001`,由非 root 用户运行,随机端口且仅监听 `127.0.0.1`,启用严格 TLS、CA 校验和主机名校验。
- 数据库迁移全套测试退出码为 0;0 至 18 版本升级、DDL 中断恢复边界、业务数据保留、TLS 与错误主机名、强制安全传输、重启持久化、连接池断线与恢复、项目成员资源迁移重试及历史范围迁移均通过。
- 线上 MySQL 8.0、Scert 和 Smail 未被测试实例替换或修改。
### 第一台 Linux 实机:正式发行程序 CLI
- 正式发行程序复制到隔离测试目录后哈希与线上 `/scert/scert` 一致,未修改线上文件权限或内容。
- CLI 的全部 `TestMySQLReal*` 实库测试退出码为 0;官方 MySQL 客户端与 `mysqldump` 互操作,以及表、视图、触发器、例程、事件、权限、目录可见性、快照和恢复规划测试均通过。
- 完整日志保存在远程隔离测试目录的 `test-results/cli-full.log`,未将认证信息写入日志或提交。
### 误报修正与安全边界
- 连接池专项首次失败不是产品故障:MySQL 5.7 正常关闭约需 17 秒,测试夹具固定等待 15 秒后误报;提交 `a139e6b` 将等待上限调整为 30 秒,修复后专项及完整数据库套件均通过。
- 双机 Scert、Smail 和系统数据库服务保持原有运行状态;内部数据库及管理端口仅回环监听,公网入口继续限制为 Nginx 80/443。
- 本轮未清库、未触发真实密码变更或邮件投递、未删除历史事务或锁、未补造恢复证据;因此本轮结果不能替代最终清库首次生产验收。
### 尚未放行的生产门禁
- [~] P0-10 其余负向窗口、锁归属和摘要不匹配的实机验证。
- [!] 以下两条历史 `recovery_required` 事务仍缺可信恢复证据,不得删除锁、修改事务终态或补造证据:`20260930-231310.231207974-5bf9d1e37542`、`20260930-231313.144123222-ce71b1dfe14e`。
- [ ] 完整三数据库 Web 生产流程、最终清库后的首次初始化与完整业务验收。
- [ ] Smail 系统账户真实 SMTP 发信、验证码密码重置和改密后重新登录闭环。
- [!] 第一台公网平台或备案限制不能用本机结果替代;Windows 不在本轮范围。
## 公开密码重置账户枚举修复与双机复核(2026年10月1日22:50,北京时间)
### 范围与开发
本轮沿用Linux范围,保留现有数据执行升级与专项安全复核,不是最终清库首次验收。未触发验证码、发信或密码变更,未启动临时重置入口。运维仍为密码SSH/SCP及严格主机身份校验,未创建辅助验收脚本。
- 修改前检查点df0be7f已提交推送;修复提交51ab0a6d0c6853a68e36cbfb7918988d352ba8d9已推送。
- 新增password_reset_guidance.go,公开路由改为showPasswordResetGuidance。仅校验请求格式、标识格式及初始化状态,不查询或比较账户;使用独立结构返回title、description、action,设置Cache-Control: no-store。历史分类处理器未绑定路由。
- 新增七类已知/未知用户名、邮箱、大小写、首尾空格的一致性回归及输入边界测试;新测试在旧实现下实际失败,修复后通过。保留严格JSON、体积和初始化边界,不以泛化错误掩盖格式错误。
- 本地go test ./...、go vet ./...、go build ./...通过;重置查询专项go test -race通过;前端新增通用响应兼容测试,npm test共242项通过。未把条件跳过项算作实库验收。
### 正式发行及安装
- 使用固定正式构建入口build/build-release.ps1,仅选择linux-amd64;干净源码克隆构建,source_dirty=false,发布路径检查通过。
- 归档:build/release-20261001-public-reset/Scert-20261001-V0.1.0-linux-amd64.tar.gz。
- 归档SHA-256:3460577ee530350479c1461dbdcf44a8a376c6904ab2ce5cd121c7e823135d54。
- 主程序SHA-256:79799ebb6ceebd3819b5285baa8333017fe050848ef6f77b7eed693ba6ef5cf8。
- 双机密码上传到/tmp/scert-51ab0a6-linux-amd64.tar.gz,解包到/tmp/scert-51ab0a6-release,校验归档及包内SHA256SUMS全部通过。按正式安装器要求仅停止Scert相关服务和存储socket,安装并重新启动;双机upgrade_exit=0,主程序摘要相同。
- 安装器自身保留的发布文件事务:第一台/scert/.install-v9Ef2X3w,第二台/scert/.install-O5wFWo5q。未额外备份业务数据,未删除安装现场。
- 第二台MainPID=176134、NRestarts=0;第一台MainPID=2111996、NRestarts=0。第一台xmail.service保持active、MainPID仍1319,未重启Nginx或邮件服务。
### 无凭据公开接口及页面
- 操作前检查前后端健康和Scert专用MCP监听;沿用项目独立上下文,忽略缓存重新加载页面,不使用缓存响应作为证据。
- 浏览器以credentials=omit、cache=no-store请求正式公开接口,分别提交配置中的用户名、邮箱、未知用户名、未知邮箱及大小写变体。五者均HTTP200、Cache-Control: no-store,响应正文逐字一致,仅含通用标题、说明及contact_administrator动作。
- 只读核对第二台配置,确认测试使用的已知用户名和邮箱均与真实配置相符;不以未知邮箱冒充已知邮箱测试。
- 第一台作为独立公网客户端正常DNS、严格TLS访问第二台,四类标识均HTTP200、Content-Length=203、no-store、无Set-Cookie,正文相同。
- 浏览器实际填写已知账号并点击查询,页面仅显示“密码重置”与通用安全说明、重新输入按钮;没有角色识别、维护命令、重置链接或会话凭据。
- 第一台本机原有IP自签证书通过密码SSH读取核对,显式信任其证书,并按证书IP名称将连接指向回环8443;健康返回ready,真实配置用户名、邮箱及两类未知标识的响应SHA-256均为eaa3acf56700ce902fff1f1e72f65d7bda018c0bd7138163878117b548f76fe7。仅计作本机接口复核,不冒充公网受信管理入口验收。
- 过程中首次读取使用jq失败(目标机未安装),随后直接访问127.0.0.1因证书名称不符失败;这些失败不计通过,空响应摘要不作证据。改用现有Python只读配置、正确证书名称和连接地址后复核成功,未关闭TLS校验、未安装额外工具。
### 安全收尾及结论
- 双机8443、3306、33060仍只监听127.0.0.1;双方互探对方公网三个内部端口,六次退出124超时。未新增外部业务端口。
- 双机nginx -t通过;第二台管理上游proxy_ssl_verify on、SNI正确;管理首页和密码重置页200,/readyz返回ready,未开启的/reset返回404。
- 第二台任务仍为失败17、成功29,无新增部署任务;两条历史recovery_required事务及JSON null恢复证据保持原状,未改库或释放历史锁。
- [✓] 本轮公开查询账户枚举漏洞经源码回归、正式发行及实机复核通过,更新22:31的未修复状态。
- [!] 实际系统通知发信、超级管理员密码重置全流程、历史事务与其他负向恢复、MySQL5.7及完整Web矩阵、最终清库首次流程仍未全部通过;总体生产验收不放行。
## 公开密码重置只读安全复核(2026年10月1日22:31,北京时间)
- 当前部署候选源码仍为 00aeedc;上一轮进度文档已通过 94c674d 提交推送。本轮为保留现场的只读检查,不是清库首次验收。
- 双机实际服务名称为 Scert.service,核对均 active;小写 scert.service 不存在,不能据此误报服务停止。第二台 MainPID=175308、NRestarts=1,与前轮受控中断记录一致。
- 双机 8443、3306、33060 均只监听 127.0.0.1;nginx -t 均成功,第一台邮件服务 active。第二台管理反代 proxy_ssl_verify on、proxy_ssl_name odacm.cn,管理首页为 200,/readyz 返回 ready,/reset 为 404。
- 只读查询最终两条事务 20261001-141918.826867221-0ff9674d0969 和 20261001-141921.961222681-692080e5aeca,phase/status 均 committed,补齐上一章节收尾数据库终态证据。
- 浏览器使用原 Scert 专属上下文和页面,操作前核对前后端健康及回环 MCP 监听。检查公开密码重置页,没有显示维护命令;未清除数据库或另开项目窗口。
- 使用 credentials=omit 分别向 POST /api/v1/password-reset/resolve 提交已知账户及本轮不存在的测试标识。两者均 HTTP 200,但前者明确返回 account_type=super_admin、action=contact_administrator,后者返回 account_type=unknown、action=check_identifier,标题和说明也不同。
- [!] 结论:未登录者仍能枚举超级管理员身份。没有泄露维护命令不代表没有信息泄露;本项是新确认的上线阻塞,当前未修复。未请求验证码、未改变密码、未启用维护入口。
- [ ] 后续需要统一合法标识的响应,覆盖用户名及邮箱的一致性回归,构建正式候选后再复测;不得以此次只读检查替代整改验收。
## 不同证书进程中断恢复与多目标部分失败(2026-10-01 22:22,北京时间)
### 范围与已确认需求
沿用原清单,仅推进 Linux。使用两台现有测试服务器及 scert-multi-20260930.apmmk.cn,保留数据库执行连续负向回归,不计作最终清库首次验收。人工连接使用密码 SSH/SCP 和已有主机身份校验;产品继续使用其托管 SSH 身份。未新增 ACME 签发、未手工改任务状态或删除锁、未建立辅助验收脚本、未额外制作业务备份。
本轮任务:核对正式复用入口、真实不同证书进程中断、修复发现的问题、正式构建及双机安装、原任务重试、不同证书多目标部分失败、恢复标准配置和安全复核。以下均为实际执行结果,不用同证书测试替代不同证书证据。
### 证书及准备
- 运行基线为较新的有效证书,叶指纹 `9D866F8360FB65365A766BF53DC087E91254FC3CC6137F9DEF25E39FA253C742`。
- 复用的另一张有效证书叶指纹 `43063C0726D34362B254CE28B66D7EB637E00B165F68EC0D07FCED0DF2C64AD2`,来源为产品已有备份目录 `/scert/backups/scert-multi-20260930.apmmk.cn/20261001-132352.396840332`。
- 当前正式历史接口只保留当前版本,历史恢复已停用;因此使用正式证书路径导入接口,不改证书目录或数据库来切换版本。
- 临时通过正式服务器接口将双机配置校验命令设为 /bin/false,导入旧证书保存为 20261001-V1.1。接口返回 502、imported=true、双机 validation_failed;两事务均 rolled_back、reload_attempted=0。任务 `a62dca21-d667-421b-beed-ffd667d9ebc6` 保持失败,双机文件与实际 TLS 均保持较新证书,无本次锁。
- 导入自动保留的较新证书位于 `/scert/backups/scert-multi-20260930.apmmk.cn/20261001-135641.353585470`,核对指纹正确。
- 校验命令随后恢复 nginx -t;仅第一台临时使用有条件的重载等待:正常 nginx 重载后,只有目标文件为旧证书摘要时等待 45 秒。恢复到基线证书时不等待;没有创建脚本文件。
### 旧候选真实复现
- 源码候选 2e6d992,正式部署任务 `3517c6d2-095a-419c-a2bc-268bb3c3eab8`,第一次尝试 `831008dc-2f8e-4b18-8b6d-04a41b8a0081`。
- UTC 13:59:34 两事务均 reload_attempted/pending,第二台实际 TLS 已变为旧证书指纹;13:59:47 第一台也实际提供旧证书,受控等待进程仍在运行。
- UTC 14:00:02 再次验证两个事务仍在预期阶段后,对第二台主进程 PID 173940 发送 SIGKILL。systemd 启动 PID 174649,NRestarts 从 0 变为 1。浏览器原请求返回 Nginx 502,未伪报成功。
- 文件和运行证书已恢复较新基线,但两事务留下 recovery_required,错误为恢复站点指纹不符;本次锁被保留。
- 两事务:`20261001-135920.017620191-f547126af33a`、`20261001-135922.655846921-a98fb068e5f4`。
- 只读核对事务的 previous_fingerprint 正是较新证书,expected_fingerprint 是待部署旧证书,证明不是记录了错误基线。
- 根因:启动恢复重载后仅立即核验一次,而正常部署已有有限次等待;不同证书实际切换尚未完成时,单次结果导致安全留锁。本问题是恢复误判,不是放宽校验或假成功。
### 修复、测试及正式安装
- 检查点 adbb398 已提交推送;修复提交 `00aeedc3efe9507b0fa04bcc1269755b3f599499` 已推送。
- 修改 app.go 的启动恢复核验,新增 recovery_verification.go,复用正常部署六次、间隔 500 毫秒的严格 HTTPS 指纹核验,支持上下文取消和截止时间;持续不符或校验失败继续返回失败并保留现场。
- recovery_verification_test.go 覆盖真实受信 TLS 的回滚/提交两种不同证书切换,以及持续指纹不符、空指纹、校验错误、取消后匹配、截止时间共五种失败保护。单次核验实现不能通过新增专项,有限次核验后专项通过。
- 后端 go test ./...、go vet ./...、go build ./... 通过;前端 npm test 241 项全部通过;正式前端构建、发布路径检查及差异检查通过。本轮未运行竞态测试,不把条件跳过的数据库场景算作实库验收。
- 正式入口 build/build-release.ps1 -Target linux-amd64,产物 `build/release-20261001-startup-verification/Scert-20261001-V0.1.0-linux-amd64.tar.gz`,source_dirty=false。
- 归档 SHA-256:`e3aaf5caf54bab6b26cbd43907a4cdcc12b95cf130fd5ba74eb8b6b3f23eb2bc`。
- 主程序 SHA-256:`04f873c7fbec900f50ac17db172225a2a882e667755406c8978fcc031ed75cf1`,两台安装后相同。
- 两台通过密码 SCP 上传,校验归档及包内 SHA256SUMS,停止 Scert 相关服务后运行正式安装器,再启动目录 socket 和主服务。Nginx 及第一台邮件服务未停止,不宣称管理入口升级零中断。
- 安装器自身保留发布文件:第二台 `/scert/.install-PDtxYNpM`、第一台 `/scert/.install-qbJ9Hdnf`,未删除安装事务或历史文件。
- UTC 14:10:53,旧候选复现产生的两事务已由新候选启动恢复自动 rolled_back,本次锁自动释放。未靠手工改库/删锁处理。
- 关闭旧浏览器页面,新建独立 Scert-startup-verification-00aeedc 上下文并通过正式登录页面重新登录,不复用旧缓存。
### 修复版真实中断、自动恢复与原任务重试
- 对同一任务执行正式重试,第二次尝试 `652ca8d0-e4a7-4e4b-966d-3e79c4b6bdc0`。
- UTC 14:13:19 两台实际 TLS 均切换到旧证书指纹,两新事务均 reload_attempted/pending,第一台等待进程仍存活。
- UTC 14:13:33 对第二台主 PID 175047 发送 SIGKILL,随后 systemd 启动 PID 175308,NRestarts=1;原 HTTP 请求返回 502。
- UTC 14:14:02 两事务已自动 rolled_back,原任务 failed/service_restart,readyz 为 200;第二台文件及实际 TLS 已恢复较新证书。14:14:19 第一台同样确认文件及实际 TLS 恢复,严格 HTTPS 200,无本次锁、临时文件或事务旧文件残留。
- 两个新事务:`20261001-141302.527037588-1ca22507895f`、`20261001-141306.622429896-38f7ca5e5ce9`。不需要再次人工重启或介入释放锁。
- 通过正式服务器接口将双机配置均恢复 validation_command=[nginx,-t]、reload_command=[nginx,-s,reload],GET 复核一致。
- 第三次正式重试同一任务成功,尝试 `0102a206-639d-4bbe-960d-4c1d25609f50`,UTC 14:15:37 完成。HTTP 200、双机成功、verification_status=success;前两次失败记录保留。
- 成功事务 `20261001-141526.617981312-26d6f26fb817`、`20261001-141529.680082912-7006a376eaf9` 均 committed。双机实际提供所部署旧证书,严格 HTTPS 200;成功后重复重试返回 409。
### 不同证书多目标部分失败
- 复用前述较新证书备份,通过正式导入保存为 20261001-V1.2,没有再签发证书。
- 第二台使用标准校验/重载;第一台临时配置校验为等待 15 秒后失败,重载仍为标准命令。
- 导入任务 `38cd22e1-ceff-43ed-b43d-cec473bd7e8e`。UTC 14:17:44,第二台实际提供较新证书,事务 reload_attempted/pending;第一台仍提供旧证书,其事务 installed/pending、reload_attempted=0,受控校验仍在执行。
- UTC 14:17:57 接口返回 502、imported=true、部署失败;第二台 failure_code=recovery_restored,第一台 validation_failed,recovery_status=success。不是把部分推送成功当成整体成功。
- 两事务 `20261001-141727.636335840-f918958bcc2f`、`20261001-141731.015455136-3898de99c378` 均 rolled_back,分别记录已尝试重载和未重载。
- UTC 14:18:42 双机磁盘与实际 TLS 均为旧证书,目录无本次锁、临时文件和事务旧文件。确认已重载的目标也自动恢复了运行证书。
- 恢复第一台标准校验命令,GET 复核两台均为标准配置。通过正式部署当前证书而非重新导入/签发,任务 `39af8113-05a7-4075-8700-9fba2ca8608e` 于 UTC 14:19:36 返回 200,双机成功且 verification_status=success。
- 该任务事务:`20261001-141918.826867221-0ff9674d0969`、`20261001-141921.961222681-692080e5aeca`。
### 最终现场、安全边界及剩余门禁
- UTC 14:20:23 双机运行最新较新证书,叶指纹为 9D866F8360FB65365A766BF53DC087E91254FC3CC6137F9DEF25E39FA253C742。
- 双机 cert.pem 和 fullchain.pem SHA-256 为 `7e8b56227b345ed9b66659e428f9bd4421604f11253ab88d65b3ed61737f35cf`;证书和私钥导出的公钥 DER SHA-256 均为 `f71a91f1aaa8c7da7dd945b432520f3a6265d47586c432594091699ccaf0a7de`。未输出私钥。
- 双机本机 SNI 严格 HTTPS 200,第一台访问第二台公网同域名严格 HTTPS 200,nginx -t 成功,测试域名无本次锁或事务临时文件;活动任务为零。
- 双机 Scert 8443、MySQL 3306/33060 仅监听 127.0.0.1,双机互探对方公网三个内部端口,六次均退出 124 超时。Nginx 管理反代保持 proxy_ssl_verify on 和正确 SNI,未开放额外公网端口。第一台 xmail.service active。
- 两台服务器 API 最后 GET 仍为标准 nginx -t / nginx -s reload,故障注入命令已全部撤销。
- 两条旧历史事务仍 recovery_required,recovery_evidence_json 为 JSON null:20260930-231310.231207974-5bf9d1e37542、20260930-231313.144123222-ce71b1dfe14e。没有补造证据或人工清除。
- [✓] 本轮不同证书真实进程中断自动恢复、原任务重试/幂等、多目标部分失败回滚及收尾复核通过。
- [x] 修复代码、测试、正式发行与双机安装完成,等待整体发布复审。
- [~] 其余负向窗口、锁归属/摘要不匹配实机与历史事务仍待处理,不能据本轮结论将 P0-10 整项标为完成。
- [!] 系统通知 SMTP 参数及真实发信闭环、第一台测试域名公网平台限制、MySQL 5.7/完整三库 Web 等原清单剩余门禁、最终清库首次验收尚未完成。已有数据库迁移、独立正式 CLI 备份恢复和 Linux 原生构建证据继续有效。
- [!] 总体 Linux 生产部署验收未放行;Windows 不在本轮范围。
## 最新执行记录(2026年10月1日21:43,不同证书重载失败回滚修复)
### 范围、需求及基线
本轮从北京时间 21:17 开始,延续保留数据的 Linux 负向回归,不是最终清库首次流程。只操作测试域名 scert-multi-20260930.apmmk.cn 的正式部署功能及已关联服务器的重载配置,不修改管理域名证书,不操作第一台既有邮件配置;全部人工 SSH 和 SCP 使用密码及严格主机身份校验。未新增辅助验收脚本,未额外备份远端业务数据,未手工改任务状态或删锁。
- 双机旧 cert.pem、fullchain.pem 的 SHA-256:`ecf815ca6ba91abb43985663f7ff2df4ee7987362131e068b6ff32e4657f90bc`。
- 旧叶证书 SHA-256 指纹:`43063C0726D34362B254CE28B66D7EB637E00B165F68EC0D07FCED0DF2C64AD2`。
- 旧证书公钥与旧私钥导出的公钥,DER SHA-256 均为 `3cca90893f3edabd12398f85487452dbe45f913041b9c7ef505bf597b7897b84`。不输出私钥。
- 双机本机带 SNI 的严格 HTTPS 为 200。正式历史列表仅有一个可用版本,因此本轮只通过正式签发接口申请一次新证书;后续测试复用新证书,不重复签发。
- 通过正式服务器更新接口临时配置:执行 Nginx 重载,输出测试站点实际叶证书指纹,短暂等待,最后核对文件仍是旧证书摘要。新证书下此命令返回失败,旧证书恢复后返回成功;配置校验始终为 nginx -t。初次请求包含只读字段被接口以 400 拒绝,改为正式允许字段后双机均保存成功,没有绕过接口校验。
### 旧候选实机缺口
源码 9c58c5b 的正式签发任务 `829076c2-2db0-4204-a151-6acc76bab1dc` 于 UTC 13:23:45 创建,UTC 13:24:26 失败。证书申请成功,但双机重载失败,HTTP 502 为产品返回的失败响应,不是任务成功。
- 新版本:`20261001-V1.0`;新叶证书指纹 `9D866F8360FB65365A766BF53DC087E91254FC3CC6137F9DEF25E39FA253C742`,与旧证书不同。
- 两条事务:`20261001-132353.561367231-d6004095e899`、`20261001-132409.768963185-15f9d00ae683`。均为 reload_attempted/recovery_required,恢复证据存在。
- 失败码为 runtime_unconfirmed。UTC 13:25:09 双机磁盘文件摘要已恢复旧值,但实际 TLS 仍提供新指纹,证明文件回滚不足以恢复运行状态。
- 为先恢复现场,使用已有服务启动恢复机制;UTC 13:27:17 两条事务均自动成为 rolled_back,两台实际 TLS 均恢复旧指纹。未手工改库、未手工删锁。
- 随后通过正式接口恢复两台标准重载配置。原签发任务保留失败,不重试签发来生成多余证书。
### 修复及正式候选
- 修改前检查点 `7a08dfe`;修复 `2e6d992b985556f334fa237dd8b69cd44ec23c14`,均已推送。
- 修复使带站点恢复校验的部署流程在重载返回错误或非零退出码时,仍将失败目标交给完整恢复事务:回滚文件、校验旧配置、重新重载、验证实际旧指纹后释放锁。没有恢复证明或恢复失败时继续保留锁;不带恢复校验的旧接口继续失败关闭。
- 新增仓库正式回归文件 reload_recovery_test.go;五个子场景先在旧实现失败,再在修复后通过,包含多目标部分失败、连接返回错误、缺失恢复校验、指纹校验失败及恢复重载失败保护。
- 后端 go test ./...、go vet ./...、go build ./... 通过;前端 241 项通过;正式前端构建、发布路径检查通过。外部条件跳过项不计为真实实库验收。
- 正式包:`build/release-20261001-reload-recovery/Scert-20261001-V0.1.0-linux-amd64.tar.gz`,source_dirty=false。
- 归档 SHA-256:`1619525d2f5a595aa8c263ec174c2890ff37914a44b4a9bd9bdf3467b23f61b3`。
- 主程序 SHA-256:`973a41d763378d03bd757b7f75ec0c8fa80997fee916879630e56f1348e34da2`。两台安装后与本地产物一致。
- 两台均检查归档和包内 SHA256SUMS,停止 Scert 相关服务后运行正式 install-linux.sh,再启动主服务和目录 socket。Nginx 未停止,第一台邮件服务保持运行;不宣称升级零停机。
- 正式安装器自行保留发布文件:第二台 `/scert/.install-C3bYY04K`,第一台 `/scert/.install-sUT2BDU5`,没有额外制作数据备份。
- 浏览器关闭旧页面并创建新的 Scert 专属隔离上下文,重新通过正式登录页面登录;不复用旧缓存及登录状态。
### 修复后的双机失败恢复
部署任务:`87166c03-5501-482f-a4f6-db83ffc87a05`,UTC 13:37:04 创建,首次尝试 `159f5a56-fc5f-49fa-8e9b-4dc4ed6a1eb3` 于 UTC 13:38:04 结束。通过正式部署接口复用新证书,保留与复现时相同的受控重载失败条件。
- UTC 13:37:24 在第二台、13:37:35 在第一台实际 TLS 观测到新指纹;第一台此时严格 HTTPS 仍为 200,确认不是同证书重部署。
- 产品返回 HTTP 502、任务 failed,双机 failure_code=recovery_restored、recovery_status=success。部署失败与恢复成功分别记录,没有误报为部署成功。
- 两事务 `20261001-133705.924556306-b00c8eb271c9`、`20261001-133721.129018393-efcbf42821fa` 自动成为 rolled_back。
- 双机旧文件摘要、公钥对应摘要均恢复基线;UTC 13:38:21 双机实际 TLS 指纹恢复旧值。双机严格 HTTPS 均 200,Nginx 配置检查通过。
- 对目标目录不限制文件类型地检查锁、临时文件和事务旧文件,无本次残留。恢复及后续重试期间,第二台 Scert MainPID 保持 173940,NRestarts=0,未依靠再次重启触发恢复。
### 正常配置恢复、原任务重试及终态
- 两台正式服务器配置均恢复 validation_command=[nginx,-t]、reload_command=[nginx,-s,reload],PUT 为 200,后续 GET 核对一致。
- 正式重试原任务,UTC 13:40:13 返回 200,双机 total=2、success=2、failed=0、verification_status=success。
- 第二次尝试 `c5078566-0987-49fd-956d-2c270f8288ca` 为 success;第一次 failed 仍保留,父任务仍是同一个任务。
- 第二次事务 `20261001-133952.659511951-b8fb25286b52`、`20261001-133955.482383445-3bcd46a09e98` 均 committed;首次两事务仍 rolled_back。
- 双机新 cert.pem、fullchain.pem 的 SHA-256:`7e8b56227b345ed9b66659e428f9bd4421604f11253ab88d65b3ed61737f35cf`;证书和私钥对应的公钥摘要均为 `f71a91f1aaa8c7da7dd945b432520f3a6265d47586c432594091699ccaf0a7de`。
- 双机实际 TLS 均为新指纹,双机本机严格 HTTPS 200,第一台访问第二台公网测试域名严格 HTTPS 200。第一台本机成功不代替其已有公网平台拦截问题的验收。
- 成功后再次正式重试返回 409;活动任务为零,无本次锁、临时文件或事务旧文件残留。
- 双机 8443、3306、33060 均仅监听 127.0.0.1;双方互探这些公网端口,六次退出码均为 124。第二台管理入口 ready,Nginx proxy_ssl_verify on;第一台邮件服务 active。
### 状态与剩余门禁
- [✓] 本次不同证书重载失败后的自动运行恢复、原任务重试及重复请求保护复审通过。
- [x] 修复代码、正式候选及双机安装完成,等待整体发布复审。
- [~] 其他负向窗口、不同证书进程崩溃、多目标部分失败等仍需继续核对;不能把本次子场景扩大为 P0-10 全部通过。
- [!] 两条历史无可信恢复证据事务保持原状态:20260930-231310.231207974-5bf9d1e37542、20260930-231313.144123222-ce71b1dfe14e。
- [!] Smail 系统通知参数与真实发信闭环、第一台测试域名公网限制、MySQL 5.7 与完整三库 Web 等剩余门禁及最终清库首次部署未完成。已完成的 MySQL 8.0 迁移、独立正式 CLI 备份恢复和 Linux 原生构建证据不作废;Windows 不在本轮范围。
- [!] 整体生产部署验收未放行。
## 最新执行记录(2026年10月1日21:12,双机密码重置接口安全收敛)
本节覆盖下方同项待部署状态;本轮是保留业务数据的安全升级回归,不是清库首次部署验收。没有新增辅助验收脚本,未操作历史恢复锁和事务,也未关闭 TLS 或 SSH 主机身份校验。
### 候选与代码
- 源码修复:`9c58c5b`,已推送;此前仅检查说明文字,公开响应仍含本机命令处理标识,本次已纠正为通用联系管理员标识。
- 前端 241 项通过;后端 `go test ./...`、`go vet ./...`、`go build ./...` 通过;差异检查通过。外部依赖的条件跳过项不计作实机通过。
- 正式包:`build/release-20261001-reset-api/Scert-20261001-V0.1.0-linux-amd64.tar.gz`;构建记录 `source_dirty=false`,发布路径检查通过。
- 归档 SHA-256:`629f99ae7fdc068cad2fab19900dd4079df023576d148a8a252c6e3ee24cf66f`。
- 主程序 SHA-256:`8c20f38d0bf2f38d33ed97e75871e3d7bda4cd9a980ab44e495550e48ac8d41a`,本地候选与两台安装后的程序一致。
### 双机部署与运行边界
- 两台均使用密码 SCP、密码 SSH 和已登记主机严格校验;归档和包内 `SHA256SUMS` 全部通过,在全新临时目录解包,停止 Scert 相关服务后运行正式安装器,随后恢复主服务与目录 socket。
- 第二台安装事务:`/scert/.install-NcgAAfQK`;第一台安装事务:`/scert/.install-iJiDqDcn`。由正式安装器按既有保护机制保留旧发布文件,未额外创建备份,不据此宣称备份恢复验收通过。
- 两台 Scert、目录 socket、Nginx 为 active,主程序哈希一致;两台 `nginx -t` 通过。第一台既有邮件服务仍为 active。
- 双机 Scert、MySQL、MySQL X Protocol 分别仅绑定回环 8443、3306、33060;两服务器跨机探测对方这些公网端口,六次均在三秒后超时(退出码 124)。本机 TCP 探测曾显示连接建立,与服务端监听证据不一致,因此不采纳本机探测为直接公网可达证据。
- 第二台公开首页与就绪检查正常,Nginx 到 Scert 保持 HTTPS 与上游证书校验。第一台使用既有管理 IP 证书作信任锚,保持 IP SAN 校验,通过连接映射访问回环服务,就绪返回 ready;不把此项算作第一台公开域名入口验收。
### 页面与接口实证
- 第二台关闭旧页面并创建全新隔离浏览器上下文,通过正式页面输入账户并查询;页面没有维护命令、管理入口名称、操作系统命令指引或代码块,未出现水平溢出。
- 实际公开查询 HTTP 200,处理标识为 `contact_administrator`;不含本机命令标识、可执行维护命令、重置地址、令牌或 nonce,也不设置 Cookie。
- 第一台通过校验证书的回环 HTTPS 连接查询,返回同一通用处理标识;并未关闭 TLS 验证。
- 本次修复不改变本机受控重置权限,也不证明真实验证码发送和新密码登录通过。
### 未完成项
- 两服务器系统解析器查询 `web.solit.cn` 均返回 NXDOMAIN;第一台对公共解析器的额外查询超时,超时不计作 NXDOMAIN 证据。真实系统通知 SMTP 参数及实际投递仍缺证据。
- 继续执行 Linux 部署负向恢复、历史事务核查、数据库支持矩阵、备份恢复、清库首次初始化与完整业务验收;Windows 按用户要求暂不处理。
- [x] 本次页面和接口安全信息收敛及双机升级回归完成,等待复审。
- [!] 整体生产部署验收保持未放行。
## 最新执行记录(2026年10月1日,密码重置页面安全信息收敛)
- [x] 公开密码重置页面已移除本机管理命令、操作系统命令和 `Scert-admin` 入口名称。
- [x] 超级管理员查询接口不再在说明中返回可执行维护命令;重置会话、令牌和验证码仍不通过公开查询接口返回。
- [x] 后端全量测试、静态检查、构建、前端 241 项测试和生产前端构建通过。
- [~] 待使用最新候选包部署到 Linux 实机,清理浏览器缓存并复测公开密码重置页面;本项通过不代表 Smail 发信、密码重置闭环或最终生产门禁通过。
## 最新实机回归记录(2026年10月1日,可信恢复证据候选)
- 最新提交:`0e48dc9`,已推送远程仓库。
- 正式 Linux amd64 包:`build/recovery-evidence-20261001/Scert-20261001-V0.1.0-linux-amd64.tar.gz`。
- 包 SHA-256:`9b6748b6f25446adcc454fb9ecf88a31cbf70554bcbfcae3451670f9a81e4afe`。
- 使用密码 SSH 将候选包安装到 `45.136.13.241`;Scert、存储 socket、Nginx、MySQL 均 active。
- 通过 `https://odacm.cn/` 登录超级管理员,执行同一域名绑定两台服务器的正式部署两次;两次均为两台推送成功、配置校验及 reload 成功、站点 HTTPS 校验通过。
- 执行 `systemctl restart Scert.service` 后,服务保持 active,HTTPS 管理入口返回 200。
- 数据库只读核查:新事务进入 `committed`;现场保留两条缺少可信恢复证据的历史 `recovery_required` 事务,未删除锁、未修改状态、未伪造验收结果。
- 本记录仅证明候选安装、正常双目标部署和重启后的基础回归;异常中断、负向 HTTPS 恢复、三数据库、双平台、SMTP、密码重置、备份恢复和最终生产门禁仍未完成。
## 当前核查与开发记录(2026年10月1日,北京时间)
### 候选构建收尾
- 已提交并推送 `8899f7a`,从新的规范克隆构建,源码与依赖锁文件无变化,发布清单记录源码未修改、后端五百六十三个与前端一百零五个受跟踪文件均采用规范换行。
- 正式包:`build/rollback-state-20261001/Scert-20261001-V0.1.0-linux-amd64.tar.gz`,大小四千九百二十七万四千零三十六字节。
- 归档 SHA256:`69e6389bfedb297997017a608b38d82c5f8f4c2f0102efe2cb5a3c323ae2be24`。
- 逐项重算清单二十三项产物的大小与哈希通过;实际解包后核对二十二个文件的完整集合、内容和大小通过;五个执行入口归档权限为 `0755`。
- 尚未部署。当前启动恢复只判断锁目录存在,并未证明属于哪笔事务;也没有持久化旧站点指纹供重启后严格核验。不能以重启后锁消失替代可靠恢复验收,更不能直接删除现场锁。
- 本条更新本批早期候选未构建状态;本地成功不等于 Linux 原生构建、线上恢复或最终生产验收。
本批本地验证收尾:后端 `go test ./...`、`go vet ./...`、`go build ./...` 均通过;部署、应用、仓库三个包的竞态回归分别约八秒、五十九秒、三十二秒,全部退出零。条件跳过的外部数据库和 SSH 场景没有据此宣称通过,正式生产验收仍未完成。
### 只读现场核查
- 10:24 使用密码 SSH 并启用已登记主机指纹严格校验连接第二台服务器;Scert、存储 socket、Nginx 均为运行状态。
- 使用 SQLite 只读连接确认:最新任务仍为失败;两笔事务 `20260930-231313.144123222-ce71b1dfe14e`、`20260930-231310.231207974-5bf9d1e37542` 仍为需要恢复,提示旧文件已经恢复但等待站点恢复核验。
- 目标域名目录仍有三个部署锁;中断测试证据目录也有三个锁。本批没有清库、重启服务、删除文件或改变这些记录。本次只读诊断不是清库首次生产流程验收。
- 上轮报告中的两个域名、两台服务器严格 HTTPS 通过,属于当时站点状态证据;不能据此把当前失败任务判为成功。本批没有重新执行该四次 HTTPS 检查。
### 确认的代码缺陷与修复边界
- 新回归在修改前稳定失败:双目标恢复核验已经通过,事务终态却为需要恢复而非已回滚。
- 根因是终态判断先匹配所有失败结果,随后已回滚分支永远不可达。修改后,只有恢复核验成功且锁释放成功才记录已回滚,并复用现有中文恢复完成提示;部署任务仍保持失败。
- 补充双目标成功恢复、恢复核验失败、文件回滚失败、锁释放失败、部分目标连接失败场景。没有创建辅助验收脚本。
- 这不是旧事务的自动修复:旧数据不重写,现场锁不移除。崩溃后的站点基线验证、锁归属证明和历史现场接管仍需继续整改与实机验证。
- 修改前检查点为 `c96cd43`;第一次推送认证失败,第二次推送成功。最终测试、构建与部署状态以本批后续收尾记录为准。
## 最新任务状态(2026-09-30,双服务器候选部署完成)
- 本轮代码提交 `07229ad` 已推送;正式 Linux amd64 候选包 `build/acceptance-20260930-smail/Scert-20260930-V0.1.0-linux-amd64.tar.gz` 已构建并完成归档校验。
- 已使用密码 SSH 将候选包正式安装到 `8.137.23.95` 和 `45.136.13.241`;两台服务器的 Scert、`Scert-storage.socket`、Nginx 均为 active,管理入口 `/readyz` 与基础 HTTPS 校验通过,未关闭 TLS 校验。
- 本条只记录候选包和基础服务部署证据,不将其当作 Smail 发信、清库首次初始化、多服务器业务推送或最终生产门禁通过。
- 当前继续执行:清库并重新初始化、Smail SMTP 验证码和密码重置、多服务器多域名推送、失败恢复与重试幂等、三数据库和 Windows 服务矩阵。
## Smail 系统通知通道收敛专项(2026-09-30)
- 代码层识别 Smail `notify:` 系统通知账号,并固定使用 `notification+system@邮件域名` 作为 SMTP 发件身份;不使用管理员邮箱替代。
- 证书任务通知和超级管理员验证码在该账号模式下都只调用 SMTP,不调用普通 HTTP 发信接口;验证码收件人仍严格限定为当前绑定邮箱。
- SMTP 未启用时返回明确配置错误;未关闭 TLS 校验,未记录 SMTP 密码、验证码或其他真实凭据。
- 通知专项、后端全量测试、静态检查和构建均已通过;本记录仅证明代码候选通过,尚未证明远端 Smail SMTP 参数、验证码、密码重置和生产上线流程通过。
- 代码、构建和双服务器基础安装已完成;下一步按生产流程清理测试数据后验证发信与密码重置;总体验收继续保持 P0-03 完成、等待复审。
## 恢复标准 Nginx 重载命令后正式页面复测(2026-09-30 17:39 UTC)
- 通过正式服务器配置页面将临时测试用的重载命令 `/usr/bin/true` 恢复为标准 Nginx 参数 `nginx`、`-s`、`reload`,保存提示“服务器配置已更新”。
- 通过正式页面对 `scert-multi-20260930.apmmk.cn` 执行“部署证书”,结果为“证书部署完成:1 台推送成功,站点 HTTPS 校验通过”;服务器结果为证书部署、配置校验并 reload 成功。
- 本轮未创建辅助验收脚本,未改变 SSH 主机指纹校验,未清理业务数据;本地 Git 工作区保持干净。
- 结论:临时重载命令已恢复,单服务器标准部署路径复测通过;不代表后置 HTTPS 失败自动恢复、进程崩溃事务自动接管、真实多服务器和最终清库首次生产流程已经通过。
## 陈旧部署锁清理后正式页面复测(2026-09-30 16:55 UTC)
- 通过 SSH 核查 `scert-multi-20260930.apmmk.cn` 目标目录中的 `cert.pem.scert-lock`、`fullchain.pem.scert-lock` 和 `key.pem.scert-lock`:三个均为空目录,创建时间早于本次任务;同时确认没有活动 Scert、SSH 或 SFTP 部署进程,`Scert` 与 `nginx` 均为 active。
- 按允许的现场清理范围,仅使用 `rmdir` 删除上述三个已确认陈旧且为空的锁目录;未删除证书、私钥、Nginx 配置、业务数据或证据目录中的历史中断现场。
- 通过正式页面重新部署 `scert-multi-20260930.apmmk.cn`,页面显示“证书部署完成:1 台推送成功,站点 HTTPS 校验通过”;服务器结果显示证书部署、配置校验并 reload 成功,域名状态恢复为“正常”。
- `Scert`、`Scert-storage.socket`、`nginx` 均为 active,公网 `https://odacm.cn/readyz` 返回 `{"service":"scert","status":"ready"}`;两个测试域名 HTTPS 均返回 200。
- `scert-20260930.apmmk.cn` 目标证书和公网证书 SHA-256 指纹均为 `5DE99CD681FB8A84745CE4ED6091D76A7B86616421B3048D3C42844B8A4460ED`;`scert-multi-20260930.apmmk.cn` 两者均为 `43063C0726D34362B254CE28B66D7EB637E00B165F68EC0D07FCED0DF2C64AD2`。两个目标托管目录无 `.scert-lock`、`.scert-backup-*` 或 `.scert-tmp-*` 残留。
- 结论:陈旧锁人工核查、清理后的正常恢复路径通过;不代表后置 HTTPS 失败自动恢复、进程崩溃事务自动接管、第二台真实服务器、多服务器任务或最终清库首次生产流程已经通过。
## SSH 密码部署公钥、密钥回连及后续部署复测(2026-09-30 23:55)
- 通过正式服务器配置页面临时使用已授权的密码保存服务器配置;Scert 自动生成服务器专属托管密钥,通过密码 SSH 将公钥写入远端 `/root/.ssh/authorized_keys`,随后关闭密码连接并使用托管私钥执行 `true` 回连验证。
- 页面服务器状态由“已配置,待验证”更新为“已部署并验证”;托管私钥指纹为 `SHA256:iDWSQY7LgGdy6aYb47C0TMvZfktJxrfjVSGYLFrqhoc`,远端 `ssh-keygen -lf /root/.ssh/authorized_keys -E sha256` 查询到相同指纹。
- 远端授权目录权限为 700,`authorized_keys` 权限为 600;主机指纹仍为已核对的 `SHA256:cVoSXsYmhALjAdN2Gso3JvbnvYQCQBFuRv3IZjFvzsE`,未关闭 SSH 主机校验。
- 自动切换为私钥认证后,通过正式页面再次部署 `scert-multi-20260930.apmmk.cn`,页面显示 1 台推送成功、站点 HTTPS 校验通过;配置校验、reload 和两个域名 HTTPS 200 均保持正常。
- 结论:单服务器“密码首次接入—自动部署公钥—私钥回连—后续密钥部署”闭环通过;不证明第二台真实服务器、多服务器任务、后置 HTTPS 失败自动恢复或最终首次生产流程已通过。
## 3cc8fb6 正式候选双域名正常部署复测(2026-09-30 23:42)
- 使用密码 SSH 连接 45.136.13.241,未使用密钥登录,未创建辅助验收脚本;线上运行程序为 3cc8fb6,管理入口重新登录成功。
- 通过正式页面对 `scert-multi-20260930.apmmk.cn` 执行“部署证书”,页面结果为“1 台推送成功,站点 HTTPS 校验通过”;服务器结果为证书部署、配置校验并 reload 成功。
- `Scert.service`、`Scert-storage.socket` 和 `nginx` 均为 active,`/readyz` 返回 `{"service":"scert","status":"ready"}`,活动任务为零。
- `scert-20260930.apmmk.cn` 公网与目标证书 SHA-256 指纹均为 `5DE99CD681FB8A84745CE4ED6091D76A7B86616421B3048D3C42844B8A4460ED`;`scert-multi-20260930.apmmk.cn` 公网与目标证书 SHA-256 指纹均为 `43063C0726D34362B254CE28B66D7EB637E00B165F68EC0D07FCED0DF2C64AD2`。
- 两个域名真实 HTTPS 请求均返回 200;`/ssl` 下两个托管目录没有残留 `.scert-lock`,证书权限保持公有证书 644、私钥 600。
- 结论:本轮证明 3cc8fb6 的正常双域名部署闭环,不证明后置 HTTPS 校验失败自动恢复、进程崩溃事务自动接管、真实多服务器和最终清库首次生产流程已经通过。
## 固定诊断版本部署与锁测试收尾(2026-09-30 22:55)
- 6efc89061bc87fdcdc41182203749b82e598865a 干净源码正式构建部署;归档 SHA256 为 443cd7db2a98832c5b1d2eb5d27b0ee704192c5be97ddd0f7585531008c13b5c,主程序 SHA256 为 ee0a5a98c993eb8f512cb53936dd05e0fde079505d2ea78af8f2cde24cbe220f。
- 保留已有数据与证书,仅升级及负向复测,不作为最终清库首次流程。复用隔离证据中的空锁进行锁冲突测试,没有新增辅助脚本。
- 失败任务 d6b3be69-c4e1-4a10-b86d-70b8809fcfd8:页面与部署结果接口均显示固定中文核查提示;接口返回 200,不泄露底层错误或凭据。
- 空锁已移回原证据目录;22:52 确认活动任务为零,原六项证据仍保留。正式页面重新部署任务 179bb071-486d-4356-8ba2-e15ce1164eb5 成功,页面两域名均正常。
- 22:55 核对目标目录无锁;两个站点真实 DNS、可信 TLS 请求均返回 200,公网指纹分别为 5DE99CD681FB8A84745CE4ED6091D76A7B86616421B3048D3C42844B8A4460ED、43063C0726D34362B254CE28B66D7EB637E00B165F68EC0D07FCED0DF2C64AD2。
- 本节只证明诊断与正常重新部署,不代表崩溃事务自动恢复或后置 HTTPS 失败自动恢复已完成。
## 真实部署中断与人工恢复(2026-09-30 22:35)
- 候选仍为 91e1123。沿用已有证书和数据执行破坏性中断验证,没有清库,不能替代最终首次生产流程;未新增辅助验收脚本。
- 通过正式服务器配置接口临时设定校验命令为有界延迟后执行 nginx 配置校验,重载命令保持正常。22:25:00 从正式页面触发第二域名部署,任务 8aa3db95-ab7a-4559-a558-a1a9c8fa5f00、尝试 656a9679-33ab-458e-bcbe-2ed6694735eb 均为 running,远端可见 sleep 45 进程及三个部署锁和对应事务旧文件。
- 首次 systemctl 命令使用当前系统不支持的参数,未终止进程;确认任务与延迟进程仍活动后改用该系统支持的 --kill-who=main。22:25:30 SIGKILL 主 PID 81906,22:25:31 systemd 自动启动 PID 83070,NRestarts=1。不是空闲服务重启,也没有将首条失败命令记为成功。
- 任务恢复为 failed/service_restart,尝试为 failed,task_recovery 审计为 failed;三者旧提示均为“服务重启导致任务中断,请重新执行”。无自动重新签发或重放远端命令。
- 22:26:42 重新登录后通过产品接口恢复 nginx -t 与 nginx -s reload。延迟进程已结束;远端保留三个锁和三个事务旧文件,nginx worker 未变,新旧 cert/fullchain/key 逐一内容一致。
- 22:27:09 产品直接重试,任务 0d6dfdf0-65a0-4af5-8544-0f9c3b604018 失败,接口 502,没有误报成功;原有脱敏只显示“证书部署失败”,不能告知用户遗留锁原因。
- 确认活动任务为零、延迟进程结束、目录真实路径正确、三个锁为空且非符号链接、新旧文件一致后,将六个现场项目移到 /ssl/scert-interruption-evidence/8aa3db95-ab7a-4559-a558-a1a9c8fa5f00。受限目录权限 700,私钥旧文件 600;未删除文件、未自动抢占未知锁,也未覆盖另一域名文件。
- 22:28:14 正式产品接口部署已有证书,任务 800815bd-7fcd-4262-87c0-63fd6174dd29 成功,返回 verification_status=success。22:33 再查主服务、目录 socket、nginx 均 active,活动任务为零,目标目录无残留锁;两域名正常 TLS HTTPS 200,公网指纹各自保持与托管证书一致。
- 结论:启动任务恢复、遗留锁拒绝和人工核查后重新部署取得证据;远端自动回滚及自动解除崩溃事务未实现。已开发固定阶段安全诊断及中断核查提示,尚未部署,不能将本次人工恢复记为自动恢复通过。
- 22:41 开发回归完成:后端全量测试及静态检查退出零,部署、仓储、应用、接口四包竞态通过,前端 240 项通过;开始干净源码正式构建,构建通过不替代实机复测。
## 单台服务器双域名闭环(2026-09-30 22:18)
- 新域名 scert-multi-20260930.apmmk.cn,资源 ID fd0b6b70-9cca-48d7-9d9f-40551cb95fc5,与第一域名关联同一服务器 3cfe6b82-6807-421d-88ab-a2034370375b。
- 仅新增独立 A 记录,记录 ID 2105298673814337536,TTL 600,未改原有泛解析。DescribeSubDomainRecords 返回未授权,改用产品已有授权的 DescribeDomainRecords 精确组合查询,没有扩权;确认新名称没有已有记录后才新增。
- 正式页面保存域名返回 201、绑定返回 200。22:10:38 任务 603a01dc-1557-4976-ae56-501c944493a4 实际执行 DNS-01;22:11:11 验证通过并清理挑战,22:11:13 签发成功。两台权威服务器查询挑战 TXT 均无返回。
- 产品创建 /ssl/scert-multi-20260930.apmmk.cn/single,三个目标文件与版本化托管文件哈希逐一一致;公有证书文件 644、私钥 600。第一域名三个文件哈希前后不变。
- 首次任务未误报成功:新域名尚无已加载的 Nginx 站点,默认 DNS 仍命中旧泛解析缓存,后置 HTTPS 报 tls: unrecognized name,任务为 failed。随后新增独立 Nginx 站点配置,nginx -t 通过;未覆盖旧站点。仅清本机解析缓存不能消除上游旧缓存,等待实际 TTL 到期,22:15:27 默认解析变为授权目标,没有固定 IP 或禁用 TLS 验证。
- 22:13 通过产品接口精确解除第二域名关联:第二域名服务器列表为空,第一域名仍关联原服务器,服务器总数仍为一;随后重新绑定第二域名,返回 200。
- 22:15:43 正式页面部署已有证书,任务 745a66ff-3b75-47ca-88fa-e4f101a9f879 成功,页面报告一台推送成功、站点 HTTPS 校验通过。
- 第二域名公网、托管和目标证书指纹均为 43063C0726D34362B254CE28B66D7EB637E00B165F68EC0D07FCED0DF2C64AD2;第一域名仍为 5DE99CD681FB8A84745CE4ED6091D76A7B86616421B3048D3C42844B8A4460ED。服务器端两站点和外部 Windows 客户端第二站点正常 TLS 访问均为 200。
- 无残留部署锁;主服务、目录 socket 和 nginx 均 active。没有创建辅助验收脚本;独立 Nginx 配置是实际证书消费端配置,不是测试替身。
- 该结果证明一台服务器多个域名及精确关联隔离,不证明一个域名多台真实服务器;后者仍缺第二台服务器。
## 新 DNS 候选部署与旧有效证书负向恢复(2026-09-30 22:05)
本轮为保留数据升级复测,不是清库首次生产验收。没有新增辅助验收脚本,没有关闭 TLS 或 SSH 身份验证;通过现有密码 SSH 会话运维。
### 正式包和部署
- 源码:91e1123fa688ac3660b0c8d0276ee562c3ef9a79,source_dirty=false;规范构建记录后端 549、前端 105 个文件换行检查,23 项清单与 22 个归档文件核对通过,五个执行入口权限 0755。
- 归档 SHA256:7b84e363db748be19a6c932c9cd4d4cd9fc3a409f3d67c3763310e1d8eaeb49f。
- 主程序 SHA256:7e3f07ab9998a43e6d0a3f647026c994e896af93a62ebe717061352e6fce5deb。
- 21:55 后在新目录解包并通过 SHA256SUMS,停止主服务和目录 socket 后执行包内 install-linux.sh,再启动服务;线上程序哈希一致。安装器自行保留旧发布文件,未额外制作备份。
- Scert.service、Scert-storage.socket、nginx 均 active;公开 readyz 返回 ready,管理入口 200;浏览器忽略缓存刷新后重新登录成功。
### DNS 与旧证书负向
- 21:58 产品权限检查连续四次全部返回 200,报告 verified、writable、readable、cleanup 均为 true,随机后缀均为三十六位小写十六进制。该有限样本不证明所有网络超时已经消失。
- 22:00 临时将产品服务器重载参数设为 /usr/bin/true,配置校验仍为 nginx -t;22:01 通过正式页面申请同域新生产证书。
- ACME 复用已有有效授权,返回新证书;此次没有重新执行 DNS-01 挑战,不能把它算作新挑战全流程。DNS 写读清理由前述权限探测独立证明。
- 负向任务 d08caabd-ac44-4902-beea-a36f4f3d860c,尝试 9e23e034-0507-4b43-a3e9-91b99dac4469 均 failed;申请接口 502,页面显示“证书申请或部署失败”和域名错误。
- 新托管版本 20260930-V1.1 和目标文件指纹为 5DE99CD681FB8A84745CE4ED6091D76A7B86616421B3048D3C42844B8A4460ED;公网仍为 9A50D4A0E551EC728D4900E595029C52B4F5EDC9E24B518F123646289101FC53,worker 仍为 81412、81413。
- 服务日志明确六次 HTTPS 校验后仍为指纹不一致,而不是 DNS 或连接失败。旧有效证书未被判为本次部署成功。
### 恢复与边界
- 22:02 已通过产品接口恢复 nginx -s reload,再通过正式页面“部署证书”;任务 ed7770b0-bf23-41c9-b556-972d125c25cd 为 success,页面明确一台推送成功、站点 HTTPS 校验通过。
- worker 更新为 82219、82220;公网、目标、托管指纹均为上述新指纹;正常 TLS 的站点访问返回 200,无 .scert-* 残留。
- 本次证明后置 HTTPS 错误检测与恢复正常配置后重新部署,不证明自动运行态回滚;申请失败页面的通用错误尚未提供阶段诊断,不提前标记可观测性完整通过。
- 多域名、多服务器、任务恢复、数据库矩阵和最终清库首次初始化仍需完成。
## DNS 标签整改本地验证(2026-09-30 21:48)
- 修改前检查点 0864300 已推送。新增正式回归在旧代码下全部失败:随机后缀不是规定的三十六位安全编码,InvalidRR.Format 被误归入网络或权限通用提示。
- 记录名随机后缀改用十八字节密码学随机数的完整小写十六进制编码,TXT 随机值继续独立生成三十二字节,避免尾部连字符及大小写折叠问题,不依赖失败重试。
- 四类域名各三十二次回归检查后缀可还原十八字节、标签不超过六十三字节、范围不变、TXT 可还原三十二字节、调用顺序 add/describe/delete 不变。随机样本唯一性检查不是独立的熵证明;随机来源和完整编码由源码确定。
- DNS 包完整测试、静态检查和竞态通过;后端全量测试与静态检查退出 0。新修复尚未部署,不提前声明云端问题已复审解决。
## 重载失败恢复与新增 DNS 阻塞(2026-09-30 21:45)
- 21:33 正式页面部署已有证书成功,报告 status=success、verification_status=success,任务 fe00eb0d-dba8-400e-8c8a-852ab4abb451;页面区分推送与 HTTPS 验证。
- 21:38 通过服务器配置将重载命令设为 sh、-c、nginx -s reload && /usr/bin/false;不是新增辅助脚本。任务 76fd45fe-4495-4ae0-999a-c9107a274243 失败,Nginx 工作进程从 81047/81048 换为 81183/81184,证明重载已实际执行。
- 三个原文件 inode 2580557/2580558/2580559、大小、校验和与 644/644/600 权限恢复一致,无临时文件或锁;网站正常 TLS 返回 200。此处部署内容与原内容相同,仅证明文件回滚及错误状态,不证明新旧证书切换后的自动运行态恢复。
- 21:40 恢复正常重载命令并通过页面重新部署,任务 c0f2ad2c-7bdf-4ebc-8c93-71fe831c1869 成功;工作进程再次更新,公网指纹与托管证书一致。
- 为验证旧有效证书不能误通过,暂设不重载命令并尝试一次同域名签发;21:41 任务 3ba35861-151e-4157-9077-c35461582b0f 在 DNS 权限探测阶段失败,阿里云错误 InvalidRR.Format,未生成新证书,尚未进入 HTTPS 身份验证。
- 21:44 产品权限检查接口相邻两次返回成功与失败。失败记录名为 _scert-authorization-W_BjXXHEGzEC5CbAXQ6Tpw7-.scert-20260930.apmmk.cn.,首标签以连字符结尾;源码使用 RawURLEncoding 生成随机标签。继续整改该偶发格式问题,不能通过反复重试碰运气作为解决方案。
- 失败后已恢复服务器正常 nginx 重载命令并重新部署既有证书。没有修改受保护域或关闭 TLS 校验,未清库。
## 最新候选升级记录(2026-09-30 21:33)
- 源码 fc57b8e1da5f1e7e145ae191748451deabbea96c;规范克隆正式 Linux 构建成功,source_dirty=false,后端 548 个、前端 105 个受跟踪文件通过 LF 检查。构建后克隆无源码或锁文件差异。
- 清单 23 项大小和 SHA-256 通过;实际归档 22 个文件的名称集合、无重复项、内容哈希及五个执行入口 0755 权限通过。归档位于发布版本目录的父目录;首次核对命令误按版本目录定位归档,修正只读路径后通过,未修改产物。
- 归档 SHA-256:4efd67bccbb871f58ad5e0ec2197bc9131bdecfc1a215e070bfc5d7c1d89b23c;主程序 SHA-256:1d360b720f023f7511ee3f2d54f3a4e54ad74e9ac2fbb3a86b74cf664520d369。
- 密码 SCP 上传,严格主机指纹校验;远端归档哈希及 21 项 SHA256SUMS 通过。在 /tmp/scert-https-verification-20260930 使用正式安装器,先停止 Scert.service、Scert-storage.socket 和目录服务,升级后恢复主服务与目录 socket。
- 安装器保留旧发布文件于 /scert/.install-xPFV6M23;未新建独立远端备份或辅助验收脚本,未删除历史现场。升级后主程序哈希一致,两单元 active,公开 HTTPS readyz 返回 ready,浏览器忽略缓存刷新并重新登录成功。
- 上一批专项竞态全部退出 0:应用 98.960 秒、接口 231.215 秒、部署 13.850 秒。此前“竞态收尾中、未部署”属于历史记录,本节更新当前状态。
- 本轮是保留数据升级复测,没有清库,不代表首次初始化或完整生产门禁通过;正在验证新版本实际部署结果与失败恢复。
## 部署后 HTTPS 误报整改(2026-09-30,本地验证,未部署)
### 问题与修复
- 原验证器会验证证书有效期、域名与可信链,但工作流忽略其返回指纹;同域名旧证书仍有效时可直接成功。新增回归覆盖申请、部署已有证书、导入三条入口,旧指纹及空指纹均在修复前复现误成功。
- 原后置 HTTPS 报错只返回外层错误,report.status 仍为 success。修复前独立回归复现,修复后整体状态 failed,域名 error;单台推送结果保留原事实,不伪称文件推送失败或已回滚。
- 部署前校验本地证书、完整链与私钥,从实际文件获取指纹,不依赖 saved 元数据。远端仍沿用可信链校验,再比较指纹;不关闭 TLS 验证,不增加强制 IP 的生产验收绕过方式。
- 本地隔离测试使用临时证书与自有信任根启动真实 TLS 服务,旧有效证书触发重试,新证书才成功。该用例不是外部真实站点、多服务器或重载恢复验收。
### 部署报告补充约定
申请、部署已有证书、导入响应中的 deployment 新增可选 verification_status:success 表示站点校验通过且指纹相同,failed 表示校验失败或未能确认,省略表示未执行该阶段。旧响应无该字段时不能推断已经校验站点。
total、success、failed、results 继续记录每台服务器的文件推送结果;整体 status 还受后置校验影响。因此可能出现 success=1、failed=0,但 status=failed、verification_status=failed,这表示文件已推送但生效校验未通过,并不矛盾。前端需优先显示生效校验失败,不能单看成功数量或一台结果放行。
任务和尝试整体持久化为 failed;服务器结果表保留成功的文件推送记录。verification_status 当前随执行响应返回,不是新增数据库持久化字段,也不代表对每台服务器独立验证。
前端 239 项及生产前端构建、后端全量测试、静态检查通过。专项竞态结果待本批收尾;未部署本批代码。重载失败、后置 HTTPS 失败后的实际恢复、任务恢复、三数据库和首次清库流程继续保留待办。
## 最新构建验证记录(2026-09-30 21:07,未部署)
- 本轮沿用既有规范克隆,签出精确提交 9e89edae282d8c18ddf9588751961015d220efcf,正式发布入口输出到 build/review-release-eol,未覆盖历史包。
- Windows 和 Linux amd64 两个目标构建成功,清单 33 项产物大小及校验和通过;Windows 包 9 个文件、Linux 包 22 个文件,实际压缩包读取后的文件集合和内容哈希均与清单一致。Linux 主程序、三个运维程序及安装器权限均为 0755。
- source_dirty=false;后端 547 个、前端 105 个受跟踪文件通过 LF 策略检查;本轮完整后端测试、静态检查和前端 237 项测试通过,结束后源码及锁文件没有变化。
- 首次手工读取 SHA256SUMS 时未指定 UTF-8,导致 PowerShell 把中文文件名错误解码并报找不到路径;改为显式 UTF-8 只读检查后,Windows 8 项、Linux 21 项内容校验全部通过。未改写文件、未替换中文,也没有把核对命令错误当成产品缺陷。
- 21:03 在既有密码 SSH 会话只读核对,实际服务名 Scert.service 为 active,管理 readyz 为 ready;小写 scert 并不是该服务单元,不能据其 inactive 判断产品停止。
- 没有清库、重新初始化、升级服务器或注入新故障。本次仅完成本地正式包验证和线上健康只读检查,不代替实机生产流程。线上仍为此前验证的 0bb427d,新包身份见《构建版本与数据库支持矩阵》最新章节。
## 构建整改补充(2026-09-30 20:58)
本批只修改正式构建工具与回归,不修改应用业务代码,也未重新部署。修改前检查点 70f19d0 已推送。
- 原工作区正式工具链拒绝非规范换行,发生在依赖安装之前,没有自动改写中文文件。规范独立克隆则通过检查,后端 547 个、前端 105 个受跟踪文件采用允许的换行状态。
- 工具链 31 项、前端全量 237 项测试通过;新增真实 Git 夹具证明 Git diff 可忽略 CRLF,但正式入口仍退出 1,且不生成 node_modules 或锁文件。
- 20:58 管理 API 返回 401,明确提示重新登录,确认此前浏览器会话已失效;公开管理入口和就绪检查仍正常。未把未认证接口的错误当成有效任务列表。
- 后续需在规范克隆用本批提交完成正式发布构建。P0-03 目录边界复审结论不变;P0-04 整项及生产完整门禁仍未完成。
## 最新实机与复审记录(2026-09-30 20:53)
### 正式页面配置校验失败与恢复
- 20:06 确认 /etc/nginx/scert-acceptance-missing-20260930.conf 不存在后,通过服务器编辑表单将校验命令临时设为 nginx -t -c 加该路径;不创建辅助脚本、不修改真实 Nginx 配置,不停止站点。
- 20:07:25 正式页面部署已有证书返回 502,任务 434b370b-72a2-41fe-a507-fbc5abe955c9 为 failed,持久化结果记录尝试 10f6606c-3ce2-45bf-b5de-74164af80c11 失败。受保护服务日志明确记录“配置校验失败:退出码 1;文件已回滚”。页面使用脱敏通用失败提示,具体原因从受保护日志核对。
- 失败前后 cert.pem、fullchain.pem、key.pem 的 inode 分别保持 2580557、2580558、2580559,校验和均与成功签发时一致,权限为 644、644、600;目录仅剩这三个文件,没有部署锁、临时文件或备份残留。Nginx 主进程 823 和工作进程 80097、80098 未变,HTTPS 返回 200。
- 20:10:01 正式页面将校验命令恢复 nginx -t;20:10:44 再次部署返回 200、耗时 1375 毫秒,页面显示 1 台成功、域名恢复正常。文件 inode 更新为 2580560、2580561、2580562,内容校验和不变;工作进程变为 80397、80398,配置检查通过、HTTPS 为 200。没有再次申请生产证书。
- 20:49 再查服务 active、管理入口 ready、测试 HTTPS 200。浏览器会话随后已失效,读取任务接口未获得新的有效列表,未把此读取异常当成部署失败或伪造新的报告。
### P0-03 当前源码独立复审
- 修改前检查点 1857d72 首次推送认证失败,第二次推送成功。全程没有删除工作区既有文件。
- 20:48 从本地仓库独立克隆到 .runtime/source-boundary-review-20260930-2015,使用 no-hardlinks 并签出精确部署源码 0bb427d5f8e230e7f9b600dda5546ef7fdd59ddb;没有复制现有依赖、构建目录或运行配置。
- 正式 build-release.ps1 按锁文件安装前端依赖并构建 Linux amd64 发布包,source_dirty=false。Go 1.26.6、Node 24.19.0、npm 11.17.0;发布路径检查通过。
- 原工作区源码边界专项测试通过(1.251 秒),独立克隆发布模块完整测试通过(3.762 秒);测试核对仓库规则和实际索引,不依赖用户 Git 排除规则。
- 发布清单共 23 项 SHA-256 全部核对一致,Linux 目录共 22 个文件;包内仅正式可执行文件、安装器、服务与 Nginx 模板、中文部署说明及 Web 文件。未携带数据库、私钥、浏览器缓存、源码映射或依赖目录。首次检查误把克隆父目录 .runtime 算作包内容,已改按包内相对路径复查;这是核对命令修正,不是产品缺陷。
- 构建后独立克隆 git status 为空,锁文件差异为空。远端既有正式包 SHA256SUMS 再次全部通过,22 个文件;线上程序仍为此前已验证的 944f2f787089942dd33ffb8b6e1e721b9f624c37cec220c3ca4801ed4e637704。
- 独立克隆复审包归档 SHA-256 为 feb24b5876628e71ad6354063032b82845ef82628e7015e3be11a6697728980a,程序为 d854e343373c0ba15b50075f1b4a70470b413cfb46d11b7d59248b7c0ed13fc6。此包没有部署,不与线上候选混用。
- 结论:P0-03 的源码与产物边界复审通过;不外推为其他 P0 或生产功能全部通过。
### 新发现与剩余门禁
- 原工作区与独立克隆有 27 个前端输入原始字节不同,比较后全部仅为 CRLF/LF 差异;规范化比较没有其他内容差异。Git 的干净状态不能单独证明原始构建输入字节一致。P0-04 需增加构建前拒绝非规范换行的检查,不自动改写中文源码。
- 配置校验失败的文件回滚已验证,不代表重载部分执行失败、后置 HTTPS 校验失败或中途断电全部通过。
- 多域名、多服务器、任务恢复、三数据库与清库首次初始化仍待完成;当前仍不满足完整生产放行。
## 最新实机记录(2026-09-30 20:06,正式证书单台部署成功路径)
### 范围与发布身份
本轮保留失败现场进行正式升级针对性复测,不是清库首次初始化。复用已有服务器,没有创建重复服务器记录;测试域名和地址仅为本次输入,不硬编码为验收条件。未修改禁止操作的业务域名。修改前检查点 18bfd16 已推送。
- 正式源码:0bb427d5f8e230e7f9b600dda5546ef7fdd59ddb,构建清单 source_dirty=false。
- 发布归档 SHA-256:22b52ce1fb822c4e86ac91d27f207711331eeec820d21452870ea1ed68d39a34。
- 本地与远端主程序 SHA-256:944f2f787089942dd33ffb8b6e1e721b9f624c37cec220c3ca4801ed4e637704。
- 密码 SCP 上传、完整 SHA256SUMS 校验后使用正式安装器;服务、管理入口和就绪检查正常。SSH 严格核对已登记主机指纹,未关闭 TLS 验证。
### 签发、推送和消费端证据
- 测试域名:scert-20260930.apmmk.cn;域名 ID:dfcd80c0-1c58-4aa2-b156-d558d4d2b9a1;既有服务器 ID:3cfe6b82-6807-421d-88ab-a2034370375b。
- 19:54 第一次申请在权限探测阶段超时,HTTP 504,未进入 ACME;随后正式产品命令的 DNS 写入、读取、清理检查均通过,才执行下一次页面申请。不能把该超时解释成确定的 RAM 权限不足。
- 19:57:21 第二次页面申请返回 200,耗时 23219 毫秒;任务 fbefdb52-7fc0-4942-8512-850b0869c07d 为 success,消息为“证书申请和部署完成”。
- 正式任务部署结果接口返回成功,消息为“证书部署、配置校验并 reload 成功”,尝试 ID 为 538a69d5-b4e9-4ba9-9cbf-6b8db86d242e。
- 托管证书实际路径为 /ssl/scert/scert-20260930.apmmk.cn/single/20260930-V1.0/,不是无版本目录;目标为 /ssl/scert-20260930.apmmk.cn/single/。三个文件逐一 SHA-256 相同,私钥权限为 600。检查未发现残留部署锁或临时文件。
- 证书指纹:9A50D4A0E551EC728D4900E595029C52B4F5EDC9E24B518F123646289101FC53;托管接口、部署文件和实际 HTTPS 返回一致。
- 签发者为 Let's Encrypt YR2;有效期为 2026-09-30 10:58:47 UTC 至 2026-12-29 10:58:46 UTC;SAN 包含本次测试域名。
- Nginx 配置校验通过,11:57:20 UTC 存在 signal process started 重载日志;服务器和外部 Windows 客户端正常域名 HTTPS 均返回 200,没有使用忽略证书校验参数。
- dns21.hichina.com 与 dns22.hichina.com 的权威非递归查询均对本次 _acme-challenge 名称返回带 aa 的 NXDOMAIN;递归缓存的旧 TXT 不作为云端清理失败依据。
### 尚未通过的门禁
- [~] 正式页面失败回滚及恢复后重复部署。
- [ ] 一台服务器多域名、任务恢复、第二台真实服务器、三数据库及清库完整首次初始化。
- [!] DNS 偶发超时仍需评估;本次成功不证明所有网络失败场景通过,也不证明后置 HTTPS 校验失败时服务自动恢复。整体验收仍未放行。
## 最新实机记录(2026-09-30 19:43,授权探测及测试子域准备)
本轮继续保留现场的升级针对性复测,不是清库首次初始化验收。线上源码仍为 7147532,尚未部署本轮 DNS 修复。
- [x] 19:25:28 页面创建域名失败,HTTP 400,耗时 2661 毫秒,报权限验证超时。
- [x] 19:30:54 产品正式命令 dns check 在 scert-20260930.apmmk.cn 返回 writable/readable/cleanup/verified 均为 true。
- [x] 19:32:01 正式页面创建域名 HTTP 201,耗时 2395 毫秒;域名 ID dfcd80c0-1c58-4aa2-b156-d558d4d2b9a1,关联既有服务器 HTTP 200。
- [x] 实机发现初次请求遗留随机授权 TXT,创建时间与失败请求一致;精确核对后删除该测试记录,不修改其他 TXT。
- [x] 云端实测确认 COMBINATION 按 RR 和类型过滤,EXACT 忽略这两个参数;本地 SDK 文档同样明确该语义。
- [x] 通过阿里云官方 CLI 为新子域新增 A 记录;只修改明确授权测试子域,保留原有泛解析。CLI 来自官方发布并验证校验和,不是自建辅助验收脚本;临时凭据只在会话环境使用。
- [~] 真实证书尚未签发,本轮目录创建、文件推送、Nginx 和公网 HTTPS 未通过,不能标记完整生产验收成功。
## 最新实机结果(2026-09-30 19:20,SSH 自动部署通过一次复测)
### 候选身份与部署
- 源码:714753236f584a2bc705e271334c697dc7f9adbd,发布清单 source_dirty=false。
- 本地目录:build/release-ssh-reload-20260930/Scert-20260930-V0.1.0。
- 归档 SHA-256:716c5167bfb2532bc2276c84d1bf087cfd686e1cb736a2e18765a131f6c9855e,本地和远端一致。
- 主程序 SHA-256:a39c8b0beaf2dc31e3bdc3b2aa774651e6d8e934ab956058518b10f71d9daeb2,本地和远端一致。
- 密码 SCP 上传后在 /tmp/scert-ssh-reload-20260930 校验全部 SHA256SUMS,再使用正式 install-linux.sh。第一次因 Scert-storage.socket 仍在监听被安装器拒绝;停止相关服务后安装成功,Scert 与目录服务 socket 已恢复 active。
- 安装器自动保留旧发布文件于 /scert/.install-jD3pl0Pp,没有把该目录作为验收条件,也未擅自删除。未创建独立远端数据备份或辅助验收脚本。
### 页面、接口与目标机交叉验证
- 管理入口返回 200,healthz=ok、readyz=ready,正常域名 HTTPS 校验未关闭。浏览器忽略缓存刷新并重新登录,编辑既有服务器记录,没有新建重复资源。
- PUT /api/v1/servers/3cfe6b82-6807-421d-88ab-a2034370375b 于 11:18:58.026842 UTC 返回 200,耗时 1844 毫秒。
- GET /api/v1/servers 复读:auth_method=private_key,ssh_key_status=deployed,ssh_key_error 为空,ssh_key_deployed_at 和 ssh_key_tested_at 均为 2026-09-30T11:18:58Z。
- 目标机 SSH 日志:11:18:56.523794 UTC 密码认证成功,11:18:57.519436 UTC 收到重载信号,11:18:57.541562 UTC 恢复监听,11:18:57.890368 UTC 公钥认证成功;对应指纹存在于 authorized_keys。
- sshd -t 通过,公钥认证与密码认证均启用,Scert 与 ssh 均 active。Scert 只有在私钥连接执行 true 成功后才保存 deployed,本次页面结果和目标机日志一致。
### 验收边界
- 本批后端全量测试、静态检查、构建和三包竞态检查全部通过,不等同于外部数据库矩阵通过。
- 本轮保留现有数据库和失败记录进行针对性修复验证,不是清库首次初始化验收;未重新申请证书、未执行实际证书推送或 Nginx 重载/回滚,不重复沿用历史通过记录。
- 第二台真实服务器、域名服务器多对多生产推送、三数据库和最终生产门禁尚未完成。总体验收仍为 P0-03 完成待复审,不新增整体放行结论。
### 2026-09-30 19:10 现场证据补充
本地竞态检查已全部通过。远端密码 SSH 读取精确日志显示:SSH 于 10:27:40.559700 UTC 收到重载信号,Scert 于 10:27:40.566606 UTC 私钥连接被拒绝,SSH 于 10:27:40.583273 UTC 恢复监听。该次失败处于监听重建窗口;候选仍需补充公钥安装后有界重试,再构建部署复核。此处仅是历史失败日志的定位证据,不是重新通过生产验收。
## 最新状态(2026-09-30 19:08,SSH 修复候选本地验证)
- [x] 已用密码 SSH 和严格已登记主机校验连接授权目标;只读执行服务状态及日志查询,Scert 和 SSH 均为 active。
- [x] 本地默认单次协商回归通过;后端全量测试、静态检查及构建通过。
- [~] 竞态检查正在运行;候选尚未重新发布到远端。
- [ ] 自动密钥登录、真实证书推送与 Nginx 重载尚未取得本轮通过证据。
- [!] 下方历史记录保留原日期,不代表当前候选的验收状态。本轮未清库、未重启服务、未修改远端文件,不是首次初始化重测,不新增生产放行结论。
## 当前进展:发布说明修正与入口、依赖审计
2026-09-20 03:12 起接续,03:22 完成本节所列检查。本节为最新状态,下文按各自时间保留历史证据。实际仓库仍为 `D:\Desktop\Scert`,目标为 `45.136.13.241`,本次管理域名为 `odacm.cn`;这些测试参数不硬编码进产品,也不构成验收依据。本轮未登录服务器、上传产物、修改 DNS/Nginx、修改系统信任库或清空共享数据库,完整生产部署验收尚未通过。
### 需求与任务状态
- [✓] 接回 Windows 与 Linux 目标的两项 `govulncheck`,均已正常退出;没有重复启动未结束任务。
- [✓] 修改前检查点 `71fe48c` 已推送,暂存区事先为空;旧补丁、二进制、脚本及历史构建目录全部保留。
- [✓] 修正 `deploy/README.md`:生产探测不再跳过 TLS,分别说明回环上游和正常 DNS 公开入口验证;统一标准 `443` 示例,旧 `8003/8004` 仅保留为自定义端口示例。
- [✓] 按当前实现修正 PostgreSQL 备份恢复说明,注明官方工具、版本、严格 TLS、同目标同对象清单限制及独立保护的集群资源,不扩大为灾难恢复已验收。
- [✓] 复核 Go/Wails 的 `-trimpath` 和前端禁用源码映射,发布检查及 CLI 定向回归通过,已有 Linux 产物路径扫描再次通过。
- [!] SSH 新指纹尚未通过可信控制台核对;没有发送密码、更新 `known_hosts` 或接受新主机身份。
- [!] 两家 HTTPS DNS 查询仍指向旧地址,当前入口 TLS 检查失败;不可用固定解析探测替代域名生产验收。
- [!] 本机 PowerShell 有效策略仍为 `Restricted`,没有修改或绕过;最新正式发布脚本、Linux 双层集成与部署流程仍未完成。
### 入口与主机身份
本机 `Resolve-DnsName odacm.cn -Type A -DnsOnly` 返回 `198.18.1.249`。严格 HTTPS DoH 查询 Cloudflare 与 Google 均返回 `odacm.cn A 47.108.78.12`、TTL 600,不是本次目标 `45.136.13.241`。这是两个递归解析器的当前观察,不宣称已直接验证权威 DNS;本机普通 DNS 的结果不能替代公共解析结论,也没有据此更改网络或 DNS 设置。
以下三项均已实际执行,无跳过证书验证:
```text
curl.exe --noproxy "*" --fail --silent --show-error --max-time 15 https://odacm.cn/readyz
curl.exe --noproxy "*" --fail --silent --show-error --max-time 15 --resolve odacm.cn:443:45.136.13.241 https://odacm.cn/readyz
curl.exe --noproxy "*" --silent --show-error --max-time 15 --resolve odacm.cn:80:45.136.13.241 -I http://odacm.cn/
```
前两项返回 curl 错误 35:`schannel: failed to receive handshake, SSL/TLS connection failed`;第三项返回错误 52:`Empty reply from server`。只能说明本机所经网络路径上的请求失败,不能证明目标 Nginx 配置、服务状态或证书根因;透明代理等网络因素仍未排除。
03:21 左右,Paramiko 无认证握手读取到 Ed25519 指纹 `SHA256:cVoSXsYmhALjAdN2Gso3JvbnvYQCQBFuRv3IZjFvzsE`,`authenticated=False`。它与历史可信值 `SHA256:XN9mJ75b4CJsFPZ7fbkAmU1yFxKZ4Tw01z8TN2EJMlk` 不同;网络上反复读到同一新公钥不构成可信确认。须在服务商可信控制台执行 `ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub` 后核对,再决定是否认证。
### 依赖与回归证据
```text
go mod verify
govulncheck ./...
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 govulncheck ./...
govulncheck -show verbose ./...
npm.cmd audit --omit=dev --json
go test -trimpath -count=1 -p=2 ./internal/releasearchive ./internal/cli
git diff --check
```
Go 命令在 `backend` 执行,npm 命令在 `frontend` 执行;Linux 环境变量一行是目标配置的简写,实际通过 PowerShell 子进程环境设置。`go mod verify` 返回 `all modules verified`。两平台漏洞扫描均报告调用路径零项、导入包零项、依赖模块一项;Windows 详细报告将其定位为 `GO-2026-5932`,涉及 `golang.org/x/crypto@v0.56.0` 中不再维护的 `openpgp` 包,无修复版本,当前代码未导入受影响包或调用其符号。没有为消除模块提示而移除仍需要的加密依赖,也不能将本结果描述成所有依赖完全没有已知风险。
前端生产依赖审计报告零漏洞,不包含开发依赖,也不代表未来漏洞保证。发布检查包与 CLI 回归均通过;真实 PostgreSQL 集成入口未提供官方工具和隔离环境参数,本轮不算新增真实数据库演练。部署说明的两个 JSON 配置块通过解析检查,差异检查无错误;没有在 Linux 上执行新文档中的 Nginx 配置或健康探测示例。
### 本地路径要求与产物边界
重新调用 `scert-release --verify-only`,对 `.runtime/clean-path-artifacts-20260920-0243/` 的三个 Linux 程序与整个资源目录,以及 `.runtime/app-integration-20260920-0307/app.test` 执行构建标记和路径扫描,均通过。扫描参数包含当前与旧工作区、用户目录、实际 Go 工具链及缓存、Go/Node 安装目录与临时目录;这些参数不写入产物。已有 Web 产物未找到 `.map` 文件。
这两处是前序已生成的核验产物,本轮没有重新编译完整发布包,也没有把新部署说明复制到旧产物中;此前记录的摘要及源码版本边界继续适用。正式候选必须从最终提交重新运行完整发布脚本,生成清单和校验文件并通过同样检查后再部署。模块导入路径、相对文件名和运行时部署路径不等于本机构建绝对路径,裁剪要求不删除这些运行所需信息。
### 待解除的验收阻塞
- [ ] 通过可信控制台确认当前 SSH 主机身份;确认后先只读检查现场,不能沿用旧服务器 PID、端口或配置结论。
- [ ] 核对 `odacm.cn` 的实际 DNS 配置与迁移范围,使其指向授权目标;分别验证权威/公共解析、本机解析及正常 DNS 的严格 HTTPS 入口。
- [ ] 在获准的构建环境运行完整发布脚本,并在隔离 Linux 单元执行五个应用与 Nginx 双层场景,确认没有跳过。
- [ ] 完成 root 辅助服务/systemd、中断恢复、最新候选部署、管理证书续期、目标推送、通知送达、升级回退与容量验收;未完成前不能宣告生产部署通过。
## 当前进展:真实应用与 Nginx 双层切换集成用例
2026-09-20 02:54 起接续。本节为最新状态,后续章节保留为历史证据。本轮没有登录服务器、部署程序、修改系统信任库、修改正式 Nginx 或清空共享数据库。完整生产部署验收尚未通过。
### 需求与任务状态
- [✓] 核对实际仓库 `D:\Desktop\Scert`,保留旧补丁、未跟踪二进制及历史产物;修改前中文检查点 `53ed3d6` 已推送。
- [✓] 延续用户要求,所有本轮 Go 测试及交叉编译均使用 `-trimpath`;最新 Linux 测试程序通过构建标记及本机构建路径扫描。
- [✓] 新增真实应用重建回归,验证 HTTPS、SQLite 和凭据存储重建后的就绪状态与数据库内容保留。
- [x] 新增五个真实应用与 Nginx 双层集成场景,源码复审、交叉编译及 Linux 目标静态检查通过,等待 Linux 实际运行复审。
- [✓] 测试提交 `c7d5344` 已推送;本节更新前已检查暂存区,仅包含三个新增测试文件。
- [!] SSH 主机身份变化仍未获得可信确认,没有发送密码或接受新指纹;远端部署继续等待身份核对。
- [!] 本机无可用 WSL、Docker 或 Podman;PowerShell 有效执行策略仍为 `Restricted`,没有修改或绕过策略,完整发布脚本仍待执行。
### 集成范围与边界
`backend/internal/app/management_integration_test.go` 复用正式 `NewWithConfigPath`、`HTTP.ListenAndServe`、SQLite 迁移和敏感信息存储,使用独立临时目录、随机回环端口和测试证书。跨平台用例关闭并重建应用,严格 TLS 探测 `/readyz`,同时检查预置数据库记录保留;没有访问真实 DNS 或申请证书。
`management_nginx_linux_test.go` 与 `management_nginx_fixture_linux_test.go` 通过正式应用适配器和 Unix IPC 连接独立 Nginx,覆盖以下场景:
1. 正常双层切换,证书路径持久化,应用重建后保持已提交候选。
2. 暂存已完成但应答丢失,回退代理,应用保持旧证书。
3. 提交已完成但应答丢失,通过事务与实际双层证书核验确认成功。
4. 暂存后恢复查询不可达,保留 `prepared` 与代理 `staged`,重建应用后恢复旧证书。
5. 上游配置持久化并切换后,提交与恢复查询不可达,保留 `upstream`,重建后完成提交。
故障通过客户端包装层注入,不是真正丢弃网络帧或杀死辅助进程。应用关闭后由配置重建,使用实际恢复工作循环处理持久化事务;不等同于进程强杀、断电、固定生产 socket 或 systemd 服务重启验收。测试辅助服务与应用使用同一隔离 UID,不替代此前不同 UID/root IPC 边界用例。
每个 Linux 场景在独立子进程中通过 `SSL_CERT_FILE` 加载测试 CA,避免系统根证书缓存影响;没有给生产 API 增加信任注入接口,也没有关闭证书验证。独立 Nginx 开启上游可信链验证,只使用本轮配置、PID、版本目录和回环端口。重建前后均检查真实两层叶证书、协调记录、配置未被无关修改及数据库内容。
### 本轮执行证据
以下 Windows 本地命令已执行通过:
```text
go test -trimpath -count=1 -p=2 ./...
go vet -p=2 ./...
go test -race -trimpath -count=10 -p=1 -run '^TestApplicationManagementListenerRestartsWithPersistentReadiness$' ./internal/app
go test -race -trimpath -count=1 -p=1 ./internal/app
```
新增真实应用重建测试首跑通过;连续 10 轮竞态测试包内用时 10.034 秒,应用包全量竞态测试包内用时 19.710 秒。Windows 全量测试不包含 Linux 专用用例。本轮未修改前端,未重复浏览器或前端构建验收。
Linux 目标 `go vet -p=2 ./internal/app ./internal/nginxactivation ./cmd/scert-nginx` 通过;最终测试源码调整后再次运行应用包 Linux 目标 vet 与交叉编译,均通过。最新测试程序为 `.runtime/app-integration-20260920-0307/app.test`,Go 1.26.6、`GOOS=linux`、`CGO_ENABLED=0`、`-trimpath=true`,SHA-256:
```text
ce3a89d9450c88ffcdb0802234a757bbe0d8888e88f737a21e9427d5f19fca77
```
扫描覆盖当前与旧工作区、用户目录(包含实际 Go 工具链、模块缓存、构建缓存与用户临时目录)、常见 Go/Node 安装目录及系统临时目录,结果通过。该文件是测试程序,不是正式安装包,也没有 Linux 执行通过的证据;旧 `.runtime/app-nginx-20260920-0303.test` 保留,不作为最新用例产物。
### 下一步验收
- [ ] 通过服务商可信控制台核对 `45.136.13.241` 的 SSH 主机公钥,再只读检查实际服务、DNS、端口、数据库和容量。
- [ ] 在非特权隔离 Linux 单元中显式指定 `SCERT_TEST_NGINX_BINARY`,执行应用双层集成测试,确认五个场景均实际运行、没有跳过,并重复执行检查稳定性。
- [ ] 验证独立 root 辅助服务初始化与 socket/service 模板,补齐进程强杀及 systemd 恢复证据。
- [ ] 执行完整发布脚本,生成具备清单、校验和且路径扫描通过的最新正式候选,再完成管理证书续期、目标推送、通知送达、升级回退与容量验收。
## 当前进展:辅助服务初始化、真实监听回归与干净源码路径复验
2026-09-20 02:25 至 02:47 接续。本节为最新状态,后续各节保留为历史证据;不把旧服务器记录当作当前环境。本轮仅完成本地修改、交叉构建和测试,没有登录服务器、部署程序、修改 Nginx 或清空数据库。
### 需求与完成状态
- [✓] 路径裁剪提交 `a212401` 已推送;本轮再次复审发布入口与扫描器,确认 Go/Wails 使用 `-trimpath`,前端关闭源码映射,归档前检查构建信息及本机构建路径。
- [✓] 修改前检查点 `aa5d669` 与辅助服务提交 `d8b4850` 已推送。补齐 root 策略、安全初始化入口、systemd socket/service、中文部署说明与打包接线。
- [✓] 新增真实监听回归提交 `549d6a832eec0cbc8dfeb01a43eea2eecfdba2fc` 已推送,远程 main 已核对一致;该提交也是更新本节前的检查点。
- [✓] 使用独立干净克隆重新构建三个 Linux 程序与 Web,全部通过路径扫描,构建标记为 `vcs.modified=false`。
- [!] 完整 PowerShell 发布脚本仍受有效执行策略 `Restricted` 限制,本轮未改变或绕过执行策略;直接工具构建不等于发布脚本已验收。
- [!] SSH 主机身份变化仍未获可信确认,不能发送 root 密码或继续远端部署。完整生产部署验收尚未通过。
### 辅助服务与真实监听测试
`scert-nginx --initialize` 验证输入文件、可信路径、权限、证书链、两层实际 TLS 证书、就绪响应与 `nginx -t`,只在专用目录生成私有版本及引用。已有状态、链接、不可信路径或宽权限私钥均拒绝;初始化不覆盖、不重载,也不安装服务。普通安装器仍不启用该 root 辅助服务。
新增 socket/service 保持本地 Unix IPC 与唯一允许 UID,不给应用进程增加权限;辅助单元仅保留 `CAP_DAC_OVERRIDE CAP_CHOWN`,并依赖、跟随 `nginx.service`。模板与初始化已完成源码和交叉构建检查,尚无本轮目标 Linux/systemd 执行证据。
`backend/internal/httpapi/management_session_integration_test.go` 通过真实 `Server.ListenAndServe()`、临时 SQLite 及迁移、受保护凭据存储和严格 HTTPS 就绪探测覆盖三项:
1. 独占会话中切换证书,只修改配置中的证书路径;监听器重建后保留新证书与数据库内容。
2. 模拟证书配置已落盘而内存快照尚旧的中断窗口,拒绝继续协调;服务重建后从持久化配置恢复候选。
3. 数据库关闭后,即使 HTTPS 仍可连接,就绪探测也必须失败。
三项首跑及连续 20 轮通过。HTTP API 全包竞态测试 `go test -race -trimpath -count=1 -p=1 ./internal/httpapi` 完成并通过,包内用时 67.667 秒。这是 Windows 真实上游监听测试,不是完整 Nginx 双层协调、进程强杀或 systemd 重启验收。
### 干净源码与产物证据
独立克隆 `.runtime/clean-path-20260920-0243/` 对应提交 `549d6a8`,构建前后 Git 状态均干净。执行以下检查全部通过:
```text
go test -trimpath -count=1 -p=2 ./...
go vet -p=2 ./...
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go vet -p=2 ./internal/app ./internal/nginxactivation ./cmd/scert-nginx
npm test
npm run build -- --outDir <独立新建目录>/web --emptyOutDir false
```
前两项为 Windows 本地执行;第三项仅为 Linux 目标静态检查。前端 54 项测试通过,生产构建无 `.map` 文件;本轮未修改页面,也未重复浏览器渲染验收。
`.runtime/clean-path-artifacts-20260920-0243/` 保留三个 Linux 程序、Web、安装器、服务模板、Nginx 模板和说明,共 18 个文件。三个程序均由 Go 1.26.6 构建,`-trimpath=true`、`CGO_ENABLED=0`、`GOOS=linux`,内嵌提交为 `549d6a8`、`vcs.modified=false`。整目录扫描覆盖当前与旧工作区、干净克隆、产物目录、用户目录、Go/Node 安装目录、Go 缓存和临时目录,结果通过。
| 程序 | SHA-256 |
| --- | --- |
| `scert` | `aadc2c3d08fc1ab0e466ac7f332dbe024bb17009553901bdb5d91f108d65c038` |
| `scert-admin` | `f41cdc58721231512656bf1a41bc25faa671d7aef6d05955e0553111ff4604f0` |
| `scert-nginx` | `42d957f5f0263419d8c3b0a935b5882b80b6be1ec81e11d2d82c6bdb83c5fca8` |
该目录没有正式发布脚本生成的 `manifest.json` 和 `SHA256SUMS`,仅用于核验,禁止当作正式安装包。路径扫描不会删除模块导入路径、相对源码文件名或运行时必需部署路径,也不保证识别源码中手写的任意其他机器路径。
三类 Linux 测试程序从同一干净克隆以 `go test -trimpath -c` 生成,保留在 `.runtime/clean-tests-20260920-0246/`:`httpapi.test`、`nginxactivation.test`、`scert-nginx.test`。仅交叉编译通过,尚未在 Linux 运行。前轮 `.runtime/path-audit-20260920-0236/` 及其他旧产物全部保留,其 `vcs.modified=true` 标记不再作为新干净产物的证据。
### 连接边界与待验收项目
02:47 左右,无认证 SSH 握手取得 `45.136.13.241` 的 Ed25519 指纹 `SHA256:cVoSXsYmhALjAdN2Gso3JvbnvYQCQBFuRv3IZjFvzsE`,与历史可信值 `SHA256:XN9mJ75b4CJsFPZ7fbkAmU1yFxKZ4Tw01z8TN2EJMlk` 不同。Windows 自带 `ssh-keyscan` 因不支持服务器选择的 KEX 未取得公钥,随后使用 Paramiko 只完成握手与公钥摘要读取;没有认证、写入 `known_hosts` 或传送业务数据。网络上读取到的公钥不能自证可信,仍须在服务商可信控制台执行 `ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub` 核对。
- [ ] 获准执行完整发布脚本,生成带清单和校验文件的正式候选;不得以核验目录替代。
- [ ] 确认主机身份后只读检查现场服务、域名、端口、数据库及容量,不沿用历史部署参数。
- [ ] 在隔离 Linux 环境执行应用适配器、真实 Nginx 双层协调、root IPC、systemd、应答丢失及中断恢复测试。
- [ ] 完成最新候选部署、真实管理证书续期、目标机推送、通知送达、重启、升级与业务回退及容量验收。
## 当前进展:路径裁剪与本地发布检查
2026-09-20 接续。本节为最新状态,后文为各阶段历史记录。实际仓库位于 `D:\Desktop\Scert`,原 `D:\Desktop\SSL` 已不存在。本次修改前检查点 `ba6c6d1` 已推送;本节产物来自修改中的工作区,仅用于构建验证,不是正式可部署发布包。
### 需求与完成状态
- [✓] 按用户最新要求,Go 与 Wails 发布入口均使用 `-trimpath`,手动构建示例同步补齐;前端明确关闭源码映射。
- [✓] 发布检查读取每个 Go 二进制的构建信息,缺少 `-trimpath=true` 时拒绝归档;扫描发布目录的全部文件,发现本机构建路径时拒绝归档。
- [✓] 扫描覆盖正反斜杠、JSON 转义、UTF-8、UTF-16 和跨读取边界;单元测试覆盖未裁剪二进制、静态资源残留、读取错误与缺失文件。
- [✓] 三处安装器及服务模板测试不再依赖编译时 `runtime.Caller` 路径,支持 `go test -trimpath`。
- [✓] Linux 发布入口补入 `scert-nginx`,归档可执行权限由实际二进制列表生成;单元测试和实际归档检查通过。普通安装器仍不安装或启用 root 辅助服务。
- [!] 当前主机禁止运行 PowerShell 脚本,完整发布脚本未实际执行通过;没有修改执行策略。语法检查通过,实际构建和扫描通过直接调用工具验证。
- [!] SSH 主机身份变化尚未确认,没有发送密码、上传文件、变更 `known_hosts`、重载 Nginx 或清空数据库。
### 本轮验证证据
- Windows 后端 `go test -trimpath -count=1 -p=2 ./...` 全部通过。
- Windows `go test -race -trimpath -count=1 -p=1 ./internal/releasearchive ./internal/admin` 通过;桌面模块 `go test -trimpath -count=1 -p=2 ./...` 通过。
- Windows 与 Linux 目标的发布检查、安装管理包及发布命令 `go vet` 通过;Linux 额外检查 `cmd/scert-nginx`。这是静态检查,不是 Linux 测试运行。
- 前端 54 项测试通过,Vite 生产构建成功,无源码映射文件。
- `.runtime/path-check-20260920-0151/` 保留本轮 Linux 的 `scert`、`scert-admin`、`scert-nginx`,Windows 的 `scert.exe`、`scert-admin.exe`、`ScertDesktop.exe` 和 Web 资源。
- Wails 2.11.0 使用 `-trimpath -nopackage` 完成 Windows 产物构建。为保留已有生成文件且避免依赖文件改写,本轮先构建前端,再传入 `-s -skipbindings -m -nosyncgomod`;这不是完整发布脚本的桌面步骤验收。
- 六个二进制均通过构建信息检查;包含部署说明、安装器和服务模板的目录通过路径扫描,额外覆盖旧工作区和当前 Go/Node 安装目录。扫描检查本次指定的构建环境路径,不等于识别所有可能手写的任意机器路径。
- `.runtime/path-check-linux-20260920-0158.tar.gz` 为直接调用归档工具生成的验证包,确认三个 Linux 程序和安装器均为 0755。该包没有完整发布脚本生成的清单及校验文件,不用于安装。SHA-256:`cda83659c0b8a6382efda4907b1adaa26170e35e4232e9bffc8c494407db7f4a`。
- 新交叉编译的 `.runtime/nginx-request-trimpath-20260920-0158.test` 构建信息包含 `-trimpath=true`,SHA-256 为 `ecfe71a2562fde94755339270988910e01b0ac76e2e21a2472e7e1484fc972cd`。未在 Linux 执行;旧测试程序保留,不作为后续部署产物。
### 前序交接与下一步
接手时的源码检查点 `822e204` 已包含请求取消栅栏、请求编号恢复、辅助服务能力检查及应用 HTTPS 监听保护。本轮未重复将交接中的 Linux 交叉编译结果算作真实执行,也未重跑历史服务器夹具。
待从可信控制台核实目标 SSH 主机身份后,先核对现场服务、端口、DNS、数据库和容量,再执行隔离的 Linux/Nginx/Unix IPC 测试。仍需完成辅助服务安装策略与 socket 模板、真实应用配置持久化协调验收、正式候选构建、管理证书续期、目标机推送、通知、重启及升级回滚。当前未达到生产部署验收条件。
## 当前进展:受限本地激活服务与身份边界验证
2026-09-18 02:20 起接续,修改前检查点 `cc94a61` 已推送。本轮继续验证独立辅助服务,没有替换正式二进制、安装正式 socket、重载正式 Nginx 或清空共享数据库。代理模式续期仍在申请前拒绝,完整生产验收尚未完成。
### 已完成的修改
- [✓] 补齐受限 Unix socket 服务、客户端及独立 root 策略的测试;仅开放 `ready/state/stage/commit/reconcile`,请求不能指定命令或路径。
- [✓] 修复监听器异常关闭被误报为正常退出;请求校验失败也清理已解析的私钥切片,并清理编码、读取持有的帧缓冲。
- [✓] 策略加载增加 socket 父目录信任校验,覆盖非法 UID、地址、域名、权限、符号链接、硬链接、FIFO、超限及错误 JSON。
- [✓] 激活等待改为连续三次新连接的严格 TLS/就绪探测;补测新旧证书交替时需重新累计成功次数。
- [✓] 实际不同 UID 子进程验证授权与拒绝;伪装服务端即使将 socket inode 改为 root 所有,客户端仍通过内核身份拒绝,不发送请求。
- [✓] 继承 FD3 的子进程验证合法 Unix socket、错误 PID、错误 FD 数量、错误路径及 TCP 监听器拒绝,关闭不删除托管 socket。
### 验证证据
本地 `go test -p=2 ./...`、`go test -race ./internal/nginxactivation -count=1` 通过;竞态测试仅覆盖 Windows 通用代码。Linux 目标 `go vet ./internal/nginxactivation ./cmd/scert-nginx`、辅助命令构建及两类测试程序交叉编译通过。
远端测试程序位于 `/scert/acceptance-20260917-1044/`,同名本地文件位于 `.runtime/`;上传前后 SHA-256 一致:
| 文件 | SHA-256 |
| --- | --- |
| `nginx-ipc-20260918-0240.test` | `0c41d5a0f0fd7990bb33377280af651054198529eef076adeedf7d0a3bd5d497` |
| `nginx-listener-20260918-0230.test` | `c99dc7ef748ec8c6b3219859d7c070935c77312449b4338ce9e8539e28e3ea1e` |
`scert-nginx-ipc-20260918-0240.service` 以 Scert 用户运行,私有网络与临时目录、只读系统、隐藏用户目录、禁止提权,限制 192 MiB 内存、禁用 Swap、CPU 50%、64 个任务。通过 `InaccessiblePaths` 隐藏 `/scert/conf /scert/data /scert/log /scert/backups /ssl-scert-acceptance -/ssl`。显式使用 `/usr/sbin/nginx`,以 `-test.timeout=150s -test.count=10` 连续十轮通过,退出零,运行 111.123 秒、CPU 7.418 秒、峰值内存 21.9 MiB。不同 UID 用例在非 root 单元中按设计跳过,在下列独立 root 单元中执行。
`scert-nginx-listener-20260918-0230.service` 使用同类非特权隔离,继承监听器五个场景连续三轮通过,退出零,运行 170 毫秒、峰值内存 6.1 MiB。
`scert-nginx-identity-20260918-0242.service` 是仅执行身份边界用例的临时 root 隔离单元,保持私有网络、私有临时目录、只读系统和正式数据不可访问,能力限制为 `CAP_CHOWN CAP_SETUID CAP_SETGID CAP_DAC_OVERRIDE` 并显式设为环境能力。设置 `SCERT_TEST_ROOT_IPC=1`,以 `-test.run=^TestRootPeerIdentity$ -test.count=3` 验证实际 UID 65534 的授权请求、UID 65533 的拒绝及非 root 服务身份伪装,全部通过,运行 310 毫秒、峰值内存 32.6 MiB。没有向正式 Scert 服务增加能力。
最终留档单元 `scert-nginx-ipc-final-20260918-0244.service` 使用相同最终测试程序再运行一轮,31 项顶层测试通过,仅 root 身份用例按设计跳过;该用例已在上面的独立单元通过。最终单元运行 11.094 秒、峰值内存 20.3 MiB,详细日志保存在 systemd journal,可使用 `journalctl -u scert-nginx-ipc-final-20260918-0244.service` 复核。
2026-09-18 02:42 结束检查:正式 Scert PID `589832`、Nginx PID `578671`、MySQL PID `589785` 均未变化;正式程序 SHA-256 仍为 `f5b08bf0fb88f7b19281e91f942aa2995866f96420e422c015890b841bae6366`,Nginx 主配置及 Scert 站点摘要与本轮开始一致。最终测试与身份测试单元均已退出,PID 为零。`nginx -t` 通过,使用管理域名严格验证回环 TLS 后 `/readyz` 返回 200 和 `ready`。本轮不包含浏览器或新增公网验收。
### 失败与修复记录
初始测试单元因误写不存在的 `/scert/logs` 在执行测试前出现 `226/NAMESPACE`;核实正式日志目录为 `/scert/log` 后修正隔离参数。旧测试程序 `nginx-ipc-20260918-0230.test` 重复运行暴露应答丢失用例的抢锁竞争,以及真实 Nginx 单次探测成功后仍遇到旧工作进程。前者改为先确认暂存已获得锁再模拟应答丢失;后者加强连续探测并增加回归测试,而非简单增加测试超时。旧程序和失败记录均保留。
不同 UID 单元最初只设置能力边界,子进程在执行前报 `operation not permitted`;独立诊断确认有效能力中缺少 `CAP_SETUID`,`setpriv` 同样失败。显式设置所需环境能力后,同一测试程序通过,不将首次未进入校验逻辑的失败计为安全验证。
### 仍需完成
- [ ] 安装 socket/service、专用版本目录和目标站点 include;当前正式站点仍直接引用原证书文件。
- [ ] 应用持久化双层协调与启动恢复、完整生命周期串行化、应答丢失及中断恢复;目前不能自动处理任意应用崩溃窗口。
- [ ] 最新完整候选构建部署、管理证书续期闭环、目标机真实推送、通知送达、完整灾难恢复和容量验收。
## 前序进展:Nginx 证书激活事务组件实机验证
2026-09-18 01:45 至 02:03 本轮接续,修改前检查点 `10645ea` 已推送。本节为最新状态;本轮没有替换正式程序、重载正式 Nginx 或清空正式数据库,组件验证通过不代表完整生产验收完成。
### 已完成的修改与复审
- [✓] 探测仅允许规范化回环 IP、端口和具体管理域名;每次建立新 TLS 连接,严格验证可信链、域名和预期叶证书,并检查受限大小的 Scert 就绪响应。不使用环境代理,不跟随重定向。
- [✓] 激活器专用目录及父路径检查所有者、权限和符号链接;专用目录要求当前激活器用户所有且权限为 0700。仅例外允许 root 所有、带粘滞位的共享父目录,不允许其他可写父目录。
- [✓] 状态读取使用禁止跟随符号链接的文件打开方式,检查普通文件、文件身份、大小、所有者、写权限及硬链接数。新证书、私钥和事务快照以 0600 写入并同步。
- [✓] 切换前验证代理、上游和专用目录内旧材料一致;候选证书必须符合两侧信任配置、覆盖管理域名、处于有效期内并延长实际有效期。
- [✓] 专用 include 仅接受固定格式和随机版本目录,事务记录验证版本编号、引用和证书。切换、配置检查和就绪探测后重新核对 include,发现外部修改则拒绝覆盖或报告成功。
- [✓] 暂存只切 Nginx;上游切换后双层证书一致才允许提交。取消、检查失败、重载失败和探测失败进行有界回退;未完成事务可恢复,已提交事务不自动回退。日志写入失败不会被误报为成功。
### 验证证据
本地 `go test -p=2 ./...` 通过,未变更包使用 Go 缓存;`go test -race ./internal/nginxactivation -count=1` 通过,Windows 只覆盖通用探测代码。使用 Linux 编译目标执行 `go vet ./internal/nginxactivation` 通过。Linux 测试覆盖目录、文件、事务和受限命令实现。
最终测试程序为 `.runtime/nginx-activation-20260918-0202.test`,远端 `/scert/acceptance-20260917-1044/nginx-activation-20260918-0202.test`;两端 SHA-256 一致:`26db07a3ed15f4324af4e742cd252e0e8700253b0543bee3757a13cc5845f796`。此前两版测试程序保留,没有覆盖上传。
最终单元 `scert-nginx-final-20260918-0202.service` 以 Scert 用户运行,私有网络、私有临时目录、只读系统、隐藏用户目录、禁止提权;正式数据、配置、日志、备份及证书目录禁止访问。限制内存 192 MiB、禁用 Swap、CPU 50%、64 个任务和 120 秒运行上限。通过 `SCERT_TEST_NGINX_BINARY=/usr/sbin/nginx` 显式启用真实 Nginx 用例,执行 `-test.v -test.timeout=90s -test.count=3`:16 项顶层测试连续三轮通过,无失败、无跳过;运行 4.492 秒,CPU 1.102 秒,峰值内存 13 MiB,Swap 为零。
真实 Nginx 用例使用独立配置、PID、临时目录与随机回环端口,启动独立非特权进程;验证暂存重载、上游切换后提交、下一轮未提交事务恢复及实际入口证书。退出时等待独立 Nginx 正常终止,不操作正式 Nginx 服务。测试还覆盖跨进程锁互斥及释放后重新获取、候选错误、双层不一致、日志失败、模拟重启阶段、外部 include 或材料变更、符号链接、硬链接、FIFO 和可写父目录。
最初单元 `scert-nginx-unit-20260918-0155.service` 在测试开始前因 `/ssl` 不存在而出现 systemd `226/NAMESPACE`;调整为 `InaccessiblePaths` 中 `-/ssl` 后,缺失路径可忽略,而现有正式目录仍强制隔离。后续单元 `scert-nginx-unit-20260918-0156.service`、`scert-nginx-real-20260918-0159.service` 及最终单元均退出零。
结束时正式 Scert PID `589832`、Nginx `578671`、MySQL `589785` 与本轮起点一致。正式二进制摘要仍为 `f5b08bf0fb88f7b19281e91f942aa2995866f96420e422c015890b841bae6366`;Nginx 主配置和 Scert 站点摘要未变。`nginx -t` 通过,按管理域名严格 TLS 验证的回环 `/readyz` 返回 200 和 `ready`。最终隔离单元为 inactive、PID 为零;本轮未进行浏览器或独立公网探测验收。
### 接线前必须完成
- [ ] 独立受限辅助进程、受保护本地 IPC、请求身份鉴别及固定本地策略;不能将 Nginx 重载权限赋予 Scert 主进程,也不能通过 HTTP 接收任意命令或配置路径。
- [ ] 安装时将当前证书复制到激活器专用版本目录并生成 include;现有线上配置仍直接引用原证书目录,不满足组件专用目录契约,不能直接切换。
- [ ] 应用协调者持久化双层激活状态,处理上游已持久化但代理未提交、提交应答丢失、调用者退出及超时恢复;组件 Recover 只回退 Nginx,不替代上游配置恢复。
- [ ] 确认上述接线后再解除代理模式申请前保护,部署最新候选,继续真实目标机推送、通知送达、灾难恢复、容量及完整生产验收。
## 当前进展:Linux 测试隔离与跨平台路径复验
2026-09-18 本轮修改前检查点 `5a96623` 已推送。下方旧阶段的“当前进展”和待办按各自记录时间理解,以本节为最新状态;本轮没有替换正式程序,也不表示完整生产验收通过。
### 问题与修复
- [✓] 定位上一阶段管理任务测试的只读数据库错误:Linux `config.Default` 固定使用正式安装路径,HTTP 测试辅助函数仅传入临时目录并不能隔离数据库。现显式设置临时安装、配置、数据、日志、证书、备份目录及 SQLite DSN,不改变生产默认路径。
- [✓] 新增路径边界与数据库隔离测试,使用 SQLite `PRAGMA database_list` 核对实际打开的文件,并验证两个新数据库均为空且可分别写入相同任务 ID。
- [✓] 扩大全量测试后发现设置接口的保存失败用例以 Windows 反斜杠拼接路径,Linux 上未真正制造目录阻塞。该文件的路径拼接已改为 `filepath.Join`;重新验证了保存失败时凭据回退与令牌不消费。
- [✓] 已提交代码 `c8c7121` 包含独立管理证书续期任务、调度接入、审计及通知;当前仍只接通直连激活,代理模式会在申请前拒绝,不能标记 Nginx 自动续期完成。
### 本地验证
```sh
go test -p=2 ./...
go test -race -p=2 ./internal/httpapi -run 'TestReadyTestConfig|TestTaskAPI|TestManagement|TestTLSCertificate' -count=1
go test -race -p=2 ./internal/httpapi -count=1
go vet -p=2 ./internal/httpapi
```
以上均通过。后端全量测试在隔离辅助函数修改后执行;随后设置测试路径修改由 HTTP 全量竞态测试覆盖,耗时 63.647 秒。格式检查无待格式化文件。
### Linux 实机验证
测试以 `Scert` 用户运行,不清空或复用正式数据库。开启私有临时目录、私有网络、只读系统、隐藏用户目录和禁止提权;额外将 `/scert/data`、`/scert/conf`、`/scert/log`、`/scert/backups`、`/ssl`、`/ssl-scert-acceptance` 设置为不可访问,防止测试再次读取正式数据。测试只访问独立临时数据和本机隔离网络,不调用真实 DNS 或 ACME。
- 定向复验单元 `scert-http-isolation-20260918-0126.service` 成功退出,覆盖原失败管理任务、任务 API、管理激活、TLS 与新隔离用例。
- 首次全量单元 `scert-http-full-20260918-0128.service` 暴露上述 Windows 路径拼接问题,退出 1,原日志与测试程序保留。
- 修复后的全量单元 `scert-http-full-20260918-0132.service` 退出 0:169 项顶层测试通过、失败零、跳过零。运行 12.976 秒,CPU 时间 6.416 秒,峰值内存 24.1 MiB,Swap 为零;限制为内存 192 MiB、CPU 50%、64 个任务、180 秒运行上限。
最终测试程序为 `.runtime/http-isolation-20260918-0130.test`,远端路径为 `/scert/acceptance-20260917-1044/http-isolation-20260918-0130.test`。两端 SHA-256 一致:`2af3d17ba5bca2be1dd2052ddab8e5a836777caee1569a471d398f8cec8c1a5e`。使用新文件名上传,没有覆盖原失败证据。
结束时测试单元为 `inactive`、PID 为零。正式 Scert PID `588768`、Nginx `578671`、MySQL `588722` 与本轮检查起点一致;Scert 二进制摘要仍为 `f5b08bf0fb88f7b19281e91f942aa2995866f96420e422c015890b841bae6366`。这些 PID 与更早阶段不同,不能沿用旧记录声称跨阶段始终未重启。本轮没有执行正式服务重启。
`nginx -t` 通过,回环地址按管理域名严格验证 TLS 后,`/readyz` 返回 200、`status=ready`。Nginx 主配置与 Smail 站点摘要未变。入口仍为 `web.apmmk.cn` 的 80 跳转 443,443 反代本地 HTTPS 9004;本轮没有浏览器验收。
### 尚未完成的验收
- [✓] 本轮 HTTP 测试隔离和跨平台路径复审通过,不代表所有包的 Linux 全量测试已通过。
- [ ] 受限 Nginx 管理证书激活器、双层 TLS 就绪确认及失败回退。
- [ ] 包含管理存储隔离与续期调度的最新候选部署,以及真实目标机证书推送、通知送达、灾难恢复、容量和最终生产验收。
## 当前进展:管理证书存储隔离
2026-09-18 接续优化,修改前检查点 `6d7972f`、代码提交 `ec432f0` 均已推送。已修复同名业务证书更新可能清理管理入口引用目录的问题;正式程序尚未替换,本节不表示整体验收完成。
### 已验证的存储边界
- [✓] 新申请的管理域名证书存入 `<证书根目录>/management-domain/<域名>/<类型>/<版本>`,管理备份也使用独立的 `management-domain` 子目录;域名来自配置,不写死测试域名。
- [✓] 单域名、泛域名和 SAN 的管理申请与同名业务签发互不覆盖;业务当前证书读取不会选中管理候选。
- [✓] 兼容既有配置:通过服务启动配置中的管理证书或私钥路径保护旧业务目录中的引用,不迁移、不删除已部署的管理证书。
- [✓] 引用保护识别符号链接,不把相同字符串前缀的相邻目录误认为子目录;引用无法解析时,在备份或候选写入之前返回错误。
- [✓] 既有管理引用经过同名业务签发和导入仍保持全部文件字节不变;无管理引用的业务旧版本继续按原单当前版本规则清理。
- [✓] 无效管理存储根目录在凭据读取和外部调用之前被拒绝;阿里云保护域名检查仍优先执行。
### 验证证据
Windows 工作区执行以下命令全部通过:
```sh
go test -p=2 ./...
go test -race ./internal/certificate -run 'TestManagement|TestFileStore|TestServiceIssuesSetup|TestAliyun' -count=1
go vet -p=2 ./internal/certificate ./internal/httpapi ./internal/app ./internal/bootstrap
git diff --cached --check
```
从提交 `ec432f0` 对应源码交叉编译 Linux 测试程序,本地 `.runtime/certificate-isolation-ec432f0.test` 与远端 `/scert/acceptance-20260917-1044/certificate-isolation-ec432f0.test` 的 SHA-256 均为 `e4828059a3deff60d86e4b39c1bc0f310785e395c185cf5c8b9ebf373f9465c8`。
在真实 Linux 上,以 `Scert` 用户运行独立单元 `scert-isolation-ec432f0.service`:内存上限 128 MiB、禁用 Swap、CPU 50%、最多 48 个任务,开启私有临时目录、隔离网络、只读系统、禁止提权及隐藏用户目录。测试匹配上述定向表达式,包含 Linux 符号链接用例,全部通过且无跳过;单元退出零,运行 1.861 秒,峰值内存 7.8 MiB、Swap 为零。日志可通过 `journalctl -u scert-isolation-ec432f0.service` 查询。所有测试使用新建临时证书与目录,不访问正式数据库、真实 DNS 或 ACME。
结束时隔离单元 `inactive`、PID 为零;正式 Scert PID `579364`、Nginx `578671`、MySQL `549584` 均未变。通过本机回环地址访问 Nginx 的管理域名、严格验证 TLS 后,`/readyz` 返回 200 和 `status=ready`,`nginx -t` 通过。此项是部署机回环复核,不替代上一轮独立 Linux 公网探测,也不代表新程序已正式部署。
### 剩余验收项
- [x] 管理证书存储隔离及旧引用保护已完成本地与 Linux 组件复审,等待包含该提交的正式候选部署验证。
- [ ] 将管理证书纳入续期调度,补齐持久化任务、审计及通知;当前调度仍只处理业务域名。
- [ ] 在不扩大 Scert 服务权限的前提下接通 Nginx 证书更新,完成双层 TLS 激活、就绪等待和失败回退。
- [ ] 最新候选构建与 Linux 部署、真实目标机推送、通知送达、完整灾难恢复及容量验收。
## 前序进展:管理证书热更新
2026-09-18 接续优化,代码提交 `9074d83` 已推送;修改前检查点为 `230cbaa`。本节是续期基础能力的本地和 Linux 隔离验证,不是线上自动续期已完成。
### 已通过的组件检查
- [✓] HTTPS 服务使用不可变证书快照,新握手可加载更新后的证书,不需要重启 HTTP 服务。
- [✓] 新证书需通过私钥配对、有效期、管理地址覆盖和可信链校验;缺失文件、错误域名、过期、尚未生效及不可信链均拒绝,旧证书继续可用。
- [✓] 先持久化证书配置再切换内存;配置写入失败不修改内存证书和内存配置。
- [✓] 并发握手、无 SNI 的 IP 握手、显式回退和服务重启后读取新证书通过严格 TLS 验证。
- [✓] 管理证书申请保留旧版本目录,重复申请和初始化回退不会破坏先前引用的文件;普通业务证书仍保持原来的单当前版本规则。
- [✓] 修复新增证书解析步骤暴露的关闭顺序回归:已关闭服务再次启动优先返回关闭状态,不能重新监听。
热更新入口仅供进程内部调用,没有增加上传证书、任意路径或远程执行命令的 HTTP 接口。证书读取状态不再依赖业务域名列表,可供后续管理续期调度使用。
### 验证证据
Windows 工作区与提交内容一致,执行以下命令全部通过:
```sh
go test -p=2 ./...
go test -race ./internal/httpapi ./internal/certificate -run 'TestTLSCertificateRotation|TestManagementCertificatePreserves|TestShutdownBeforeStart' -count=1
go vet -p=2 ./internal/httpapi ./internal/certificate ./internal/app ./internal/bootstrap
git diff --cached --check
```
使用 Go 1.26.6 交叉编译两个 Linux 测试程序,上传前后 SHA-256 一致。远端文件均在 `/scert/acceptance-20260917-1044/`:
| 文件 | SHA-256 |
| --- | --- |
| `httpapi-tls-rotation-9074d83.test` | `a3859533e83f7502f711c172481ebfb5f812ef3282c14d3b547e7eaa4b2e377e` |
| `certificate-management-9074d83.test` | `bed93ab19132f762ab013d5ca58f9f4354dd4f187f86b8a25df5cb5e3b6bd1a0` |
两个独立 systemd 测试单元以 `Scert` 用户运行,内存上限 128 MiB、禁用 Swap、CPU 50%、最多 48 个任务,并开启私有临时目录、只读系统保护及禁止提权。TLS 更新与关闭生命周期测试、管理存储与业务存储回归全部退出零;峰值内存分别约 8.7 MiB 和 7.2 MiB。测试使用全新临时证书与配置,不访问真实 DNS、ACME 或业务数据库。
线上 Scert 未替换二进制、未重启,仍运行此前版本。只读复核时 Scert PID `579364`、Nginx `578671`、MySQL `549584`、8002 上的 Smail `574584` 均未变化,管理 HTTPS 就绪响应仍正常。未清空共享数据库,未删除既有补丁、构建产物或服务器文件。
### 继续验收的门槛
- [ ] 将管理证书纳入续期调度,并补齐持久化任务、审计及通知;当前仍不是自动续期闭环。
- [ ] 管理候选证书与业务存储隔离,防止同名业务证书操作清理管理入口引用的路径。
- [ ] 在不扩大 Scert 服务权限的前提下接通 Nginx 更新,完成双层 TLS 激活、就绪校验及失败回退。
- [ ] 从最新已验证源码构建候选版本,再进行 Linux 部署、真实目标机推送、通知送达、完整回退与容量验收。
## 已通过:标准域名入口
2026-09-18 00:01 起,按用户确认改为复用已开放的 80/443,通过 Nginx 域名站点转发。以下结果覆盖旧 8003/8004 方案的公网入口阻塞,不改变管理证书续期等尚未完成事项的结论。
- [✓] `http://web.apmmk.cn` 返回 308,跳转到 `https://web.apmmk.cn`,保留路径及查询参数。
- [✓] `https://web.apmmk.cn` 转发到 `https://127.0.0.1:9004`,入口和上游均严格验证证书。
- [✓] 应用 `server.proxy_https_port` 已改为 443;未改变上游监听端口、域名或数据库。
- [✓] 独立 Linux 主机 `8.137.23.95` 通过正常公网 DNS 访问 `/readyz` 返回 200、`status=ready`,HTTP 跳转同样通过。未使用指定解析、隧道或跳过证书验证。
- [✓] 新域名地址的桌面浏览器登录、刷新、退出及 401 检查通过。此项使用独立 SSH 隧道和严格 TLS,证据目录 `.runtime/apmmk-browser-20260918-standard/`,与上一项公网协议测试分开记录。
- [✓] 原 8003/8004 已无监听;9004 仍仅回环监听。此前 UFW 放行规则暂保留,本轮没有删除规则或文件。
- [✓] 共享 Nginx 主配置和 Smail 站点内容未变;Smail HTTPS 返回 200、TLS 验证结果为零。
备份位于 `/var/backups/scert-standard-20260918-0005-v2`,含受限配置备份及原站点。只修改 Scert 站点,不新增重复默认站点。首次切换时立即探测收到 SNI 拒绝,脚本自动恢复旧配置;随后加入平滑重载后的有界就绪等待,第二次切换成功。Scert 为加载对外端口配置经过重启,旧会话需重新登录;未重启同机 Smail、MySQL 或 Nginx 主进程。
完成时 Scert PID `579364`、重启计数零;Nginx 主进程 `578671`、Smail `574584`、MySQL `549584` 均与本轮开始一致。Nginx 配置语法检查通过,1440/1024 桌面测试无横向溢出、损坏图片或页面脚本错误。
Windows 本机直接访问仍出现 HTTPS 握手错误及 HTTP `Server: Beaver` 403,与独立 Linux 公网测试结果不同;该本地网络路径尚未定位,不宣称所有客户端网络均验证通过。本轮没有更改本机代理设置。
部署模板已改为共用端口的命名虚拟主机,外部完整地址使用 `__MANAGEMENT_ORIGIN__` 占位符,默认测试值不再包含 8004。管理证书自动续期与双层 TLS 更新、真实目标机推送及完整故障回退仍需后续完成,本次仅完成用户要求的标准入口调整。
## 历史结论:管理入口接续验收
以下为 2026-09-17 23:16 至 23:35 的接续结果,覆盖后文早期阶段关于域名未确认、初始化未完成和入口未部署的旧状态。不能将各阶段的通过项混为完整生产验收。
### 需求与任务状态
已确认管理域名可配置,本次使用 `web.apmmk.cn`,Nginx HTTP 8003 跳转 HTTPS 8004,上游为 `https://127.0.0.1:9004`。实际仓库为 `D:\Desktop\Scert`,不使用已不存在的旧目录。
- [✓] 复核真实初始化与正式证书,上游严格 TLS 校验下就绪。
- [✓] 独立 Nginx 站点部署、主机防火墙放行、未知域名拒绝、HTTP 跳转及双层 TLS 校验。
- [✓] 隧道访问线上站点的桌面浏览器登录、刷新会话、退出及接口鉴权测试。
- [✓] 六组跨域写入拒绝、退出后旧 Cookie 重放拒绝。
- [✓] Scert 开机自启已启用;未重启共享主机,不能视为重启演练通过。
- [x] 通用反代配置样例和部署说明已补充,等待整体生产复审。
- [!] 公网 8003/8004 在独立 Linux 主机访问超时,需核对云安全组或上游网络。
- [!] 管理证书未进入续期队列,且尚无上游 TLS 与 Nginx 的证书更新生效机制。
- [ ] 真实目标机证书推送、完整故障回退、通知送达与容量验收仍需完成。
### 已核对的运行版本
正式程序来自已推送提交 `b6d1b96426437df412b508e2bfff06b1dde02871`。其阿里云修复使用限定域名及精确 TXT 记录查询,以兼容当前 RAM 权限,同时对 DNS 主机名进行不区分大小写比较,记录 ID 和 TXT 内容仍严格匹配。本阶段没有再次替换正式二进制。
23:30 重新计算正式程序 `/scert/scert` 的 SHA-256 为 `f5b08bf0fb88f7b19281e91f942aa2995866f96420e422c015890b841bae6366`;升级前快照 `/var/backups/scert-pre-dns-b6d1b96/snapshot.tar.gz` 的摘要为 `5dae6d9444768243a9440e69dfaf41870cbbc2a85702d2d8923579763ca77715`。
当前管理证书为 Let's Encrypt YR2 签发,主题 `web.apmmk.cn`,有效期截止 `2026-12-16 14:14:04 UTC`。上游及 Nginx 探测均使用公共 CA 和主机名验证,没有使用跳过证书验证选项。
本阶段延续已完成初始化的独立测试数据集,没有清空数据库或重跑初始化。管理员密码仅保留在管理机 root 可读的受限凭据文件中,不进入代码、报告和截图。
### 反向代理部署
新增 `/etc/nginx/conf.d/scert-apmmk.conf`,保留 `/etc/nginx/nginx.conf` 与 `smail.conf`,部署前后内容摘要一致。配置备份位于 `/var/backups/scert-nginx-20260917-2320-v2`。第一次配置因本机参数传递导致空字符串引号损坏,被 `nginx -t` 拦截并移出有效加载目录,未重载;修正后语法检查成功才平滑重载。
8003 对已配置域名返回 308,示例路径 `/login?acceptance=1` 正确跳转至 `https://web.apmmk.cn:8004/login?acceptance=1`;未知域名返回 404。8004 的未知 SNI 拒绝握手。已配置域名通过 `proxy_ssl_verify on` 和 `proxy_ssl_name web.apmmk.cn` 验证 9004 上游,保留 `Host $http_host` 中的外部端口。
UFW 新增 TCP 8003、8004 的 IPv4/IPv6 入站规则;9004 仍只监听回环地址。`Scert.service` 此前为 disabled,现已执行 enable;Nginx 原已启用。没有重启 Scert、Smail、MySQL 或主机。
23:30 状态:Scert PID `576928`、重启零;Nginx 主进程 PID `575746`;Smail PID `574584`、重启零;MySQL PID `549584`、重启一。应用进程均与本阶段开始时一致。
### 浏览器与安全测试
本轮没有可调用的浏览器 MCP,使用独立 Playwright Chromium 上下文作为替代。新建上下文并禁止 Service Worker,不复用旧缓存。通过本机回环 SSH 隧道连接线上 Nginx 8004,浏览器 URL、TLS SNI 和证书校验仍使用真实管理域名;没有忽略 HTTPS 错误。这是线上应用测试,不是公网入口测试。
通过真实表单登录、页面跳转、刷新后会话保持、设置和用户等只读接口访问、退出、退出后 401 检查。Cookie 为 `Secure`、`HttpOnly`、`SameSite=Lax`、路径 `/`。1440 和 1024 桌面宽度没有横向溢出、损坏图片或页面脚本错误;截图仅代表这些桌面尺寸,不代表移动端验收或完整视觉复审。
本地证据目录 `.runtime/apmmk-browser-20260917-2330/` 含 `result.json`、`login-1440.png`、`dashboard-1440.png`、`dashboard-1024.png`,保持 Git 忽略。脚本不把管理员密码或 Cookie 写入结果。
远端协议脚本 `apmmk_security_acceptance.py` 使用严格 TLS 测试三个错误 Origin:异域、缺失外部端口、错误协议。对设置写入及退出各测试一次,共六次均为 403,且原会话仍有效;正常退出后重放原 Cookie 为 401。
100 次就绪请求、并发四,全部返回正常;中位约 28.57 毫秒、P95 约 38.91 毫秒。测试前主机可用内存约 779 MiB、Swap 使用约 155 MiB。这仅是短时只读探测,不替代签发、部署、数据库写入或持续负载的容量测试。
### 当前阻塞与续期风险
Windows 本机 DNS 返回代理软件虚拟地址 `198.18.0.51`,默认访问失败,不能据此单独归因服务器。另从目标机 `8.137.23.95` 只读访问管理机公网,两个端口均连接超时;指定真实 IP 的 HTTPS 再测也超时。同期管理机监听与 UFW 正常,短时抓包未捕获该外部连接。当前证据提示云安全组或其他上游网络限制,尚未读取云安全组规则,不能断言已定位唯一原因。本次在目标机只执行网络探测,没有部署、清库或重启操作。
续期 API 实际返回 `checked=0`、`selected=0`;域名列表为空。源码核对表明 `IssueForSetup` 只保存管理证书,而当前调度器遍历托管域名仓库;HTTP 服务调用静态 `ListenAndServeTLS`,Nginx 也引用固定版本路径。因此证书有效不等于管理证书能够自动续期。必须补齐续期调度、证书事务切换、双层 TLS 更新、失败保留旧证书和恢复验证后再解除该门槛。
## 历史阶段记录
## 范围与任务状态
2026-09-17 在管理机 `47.108.78.12` 执行当前版本的受控测试。`8.137.23.95` 仅为推送测试机,本记录不包含对它的操作。不运行固定安装器,不切换正式服务,不清空既有数据库,不修改共享 Nginx 或业务证书。
- [✓] 密码 SSH 登录并通过已有主机密钥校验。
- [✓] 核对管理机服务、工具、空间和健康接口。
- [✓] 独立干净克隆构建 Linux 包,本地与远程归档、测试程序 SHA-256 一致。
- [✓] PostgreSQL 18 第一轮超时后完成资源故障取证、残留集群停止及受限七场景重测。
- [✓] Linux Bootstrap 测试包及服务配置测试通过;修复地址族白名单后,候选程序加固启动、HTTPS 协议及重启失效检查通过。
- [✓] 初始化前三步的桌面浏览器检查、提示修复和 SMTP 输入框裁切复测通过;未完成整个初始化流程。
- [!] 授权 DNS 签发、初始化完成、正式切流和业务推送未验收。
## 发布来源
候选源码提交:`8990710ee145809eb3a937d3b4eb578adbdeb3fe`。发布清单 `source_dirty=false`,两个程序均内嵌相同提交及 `vcs.modified=false`。构建工具链为 Go `1.26.6`,目标 `linux/amd64`,`CGO_ENABLED=0`。
本地发布目录:`build/linux-production-20260917-1040/`。远程独立目录:`/scert/acceptance-20260917-1044/`。前端 39 项自动化测试通过,依赖安装审计为零漏洞;这些不是浏览器验收。
| 产物 | SHA-256 |
| --- | --- |
| `release.tar.gz` | `3eee5a62403d6dadcc1f36e105de8cfa1684f65b688af7a1e898c7269e475344` |
| `cli.test` | `7bf731c5dade9ceb24f385dd0bb4c54d1809e3e6a9400c710ecfe871c9de1d1f` |
本地发布清单 14 项与包内校验清单 12 项通过。远程两项上传文件哈希及解压后的 12 项校验通过,安装器 `bash -n` 通过,候选程序返回 `Scert 0.1.0`。归档成员清单只含相对路径下的普通文件及目录,可执行程序和安装器为 `0755`。
## 实机初查
10:43 至 10:44,通过可信 SSH 通道获得:
- Linux `7.0.0-29-generic`,`x86_64`,根分区剩余约 28 GiB。
- `Scert.service` 为 `active/running`,用户 `Scert`,PID `530794`,监听 `127.0.0.1:9004`。
- 已有 PostgreSQL `18.6` 官方工具,既有数据库端口 `5432` 不作为测试目标。
- `/healthz` 为 HTTP `200`;`/readyz` 为 HTTP `503`,原因 `setup_incomplete`。
- 正式 ACME 目录正常 TLS 校验下为 HTTP `200`,TCP 约 `0.206` 秒、TLS 约 `0.484` 秒。此前网络超时本次未复现,但目录可达不代表签发通过。
- 在用 `/scert/scert` SHA-256:`ce2438fdddb1e471184d42ec98374abe1e96f74d3687ec60ad493eb659d5eb43`。
## 测试执行与异常
远程测试命令先在新目录解压,执行包内校验、安装器语法检查、版本检查,然后以 `Scert` 用户运行 `TestPostgresRealBackupRestore`。测试使用 `/usr/lib/postgresql/18/bin`,产物根目录为 `/scert/acceptance-20260917-1044/postgres-artifacts`,测试超时为五分钟。
该测试自行创建回环随机端口集群、每场景全新数据库、SCRAM 密码和严格 TLS;不接受业务 DSN,完成时停止自己的集群并保留产物。
10:50 左右尚未收到测试命令结果,另建 SSH 连接在协议 banner 阶段超时。约 10:53 原连接收到测试退出码 `2`:成功恢复与证书失败回滚场景打印检查通过;第三个 `cancel` 场景尚在初始备份清单检查时触发总超时,栈位于 `validateBackupManifest` 的 `Lstat`,还未进入取消注入阶段。其余四场景未执行。不能将该结果归因为取消回滚逻辑故障,也不能报告整轮通过。
测试产物:`/scert/acceptance-20260917-1044/postgres-artifacts/scert-postgres-integration-2406225956/`。总超时导致测试清理回调不保证执行,随后只读检查请求也出现 `Timeout opening channel`。已请求仅停止该目录中的隔离集群,停止结果仍待确认。未向既有 PostgreSQL 服务或正式 Scert 发送停止命令。
恢复连接后必须先核对本轮测试进程、`postmaster.pid`、端口、系统负载及原服务状态,再决定是否重试。保留所有产物,不重跑解压命令覆盖已有目录,不在连接不稳定时继续启动额外测试负载。
## 资源故障恢复与受限重测
以下为 11:04 之后的续测结果,补充并更新上节当时尚未确认的状态。
- 主机约 1607 MiB 内存、无 Swap。内核在 11:00:20 记录全局 OOM,MySQL 被杀死;同时存在 journald、logind watchdog 超时。该证据确认系统发生内存故障,但不能仅凭受害进程或单次堆栈认定某个测试是唯一来源。
- MySQL 自动恢复为 PID `549584`,重启计数为 1;正式 Scert PID `530794` 未变。未重启这些服务,未调整 Swap、MySQL 配置或其他项目的资源设置。
- 核对首轮隔离集群目录、PID `549227` 及端口 `44387` 后,使用该集群的 `pg_ctl -m fast -w -t 30 stop` 正常停止;数据库与测试产物保留。既有 PostgreSQL 端口 `5432` 未操作。
- 测试辅助配置降低至 `shared_buffers=16MB`、`work_mem=1MB`、`maintenance_work_mem=16MB`、`max_connections=32`,禁用并行 worker。该变更只作用于新建隔离集群,不调整业务 PostgreSQL。
- 使用独立 systemd 临时单元串行运行:`MemoryMax=180M`、`MemorySwapMax=0`、`CPUQuota=50%`、`TasksMax=64`、`RuntimeMaxSec=300`、`KillMode=control-group`、`OOMPolicy=stop`,并设置 `GOMEMLIMIT=32MiB`、`GOMAXPROCS=1`。这些是本轮小数据演练参数,不是生产容量建议。
- 11:08:00 至 11:08:07,七个真实场景全部通过,用时 7.23 秒,单元退出码为 0。产物目录为 `postgres-bounded/scert-postgres-integration-710949348`;测试程序 SHA-256 为 `634666af045474b7dcec8f4b6c9eb325ef3fd95a28b628a2c388bbd304472f2e`。
- 此后内核另有其他项目测试单元的 `CONSTRAINT_MEMCG` OOM,不能记为本轮 Scert 测试或新的全局 OOM。共享机器仍需协调测试负载,不能因本轮通过就认为容量问题已消失。
## 加固启动缺陷与新候选
旧候选在保留地址族白名单的真实 systemd 环境中启动失败:`route ip+net: netlinkrib: address family not supported by protocol`。原因是初始化临时证书读取网卡地址需要 `AF_NETLINK`,原 unit 只允许 `AF_UNIX AF_INET AF_INET6`。此前单元测试只校验原有文本,未发现实际系统调用受限。
修复提交 `5573c64b209eae391c77f2a476ccc6784fd5b2e2` 同步更新生成式 unit 和 `deploy/systemd/Scert.service`,仅增加 `AF_NETLINK`;新增发布 unit 的安全属性断言。主服务仍为 `Scert` 用户,capability 集为空,保留 `NoNewPrivileges` 和其他加固;超级管理员恢复服务仍限制为本地 IPC,未增加网络地址族。
新候选从独立干净克隆构建:
- 本地:`build/linux-netlink-20260917-1119/Scert-20260917-V0.1.0-linux-amd64.tar.gz`。
- 远程:`/scert/acceptance-20260917-1044/release-netlink/`。
- 归档 SHA-256:`d592bf5753e88326de9b7ab7986338f07e92028743e75f4c1a9e9e2eb2f8a544`。
- 清单 `source_dirty=false`,程序内嵌 `vcs.modified=false` 和上述提交;本地清单、远程上传哈希及包内 12 项校验通过,安装器语法检查通过。
- 本地 CLI 与 servicehost 回归通过;Linux 原 Bootstrap 测试包通过,峰值内存约 16 MiB。新增 servicehost Linux 测试(含发布 unit 断言)也全部通过。Bootstrap 测试内部的 DNS/签发替身不代表真实外部签发。
- 11:22:21 至 11:22:27,使用新候选 CLI 再跑七个 PostgreSQL 真实场景,全部通过,用时 6.91 秒,退出码 0;新产物为 `postgres-bounded/scert-postgres-integration-1008171600`。取消场景仍采用测试程序进程内确定性注入,其他 CLI 场景使用新包中的可执行文件。
- 本地追加执行 Bootstrap、servicehost、管理员恢复和归档模块回归,全部通过;`git diff --check` 无格式错误。
## 候选程序实际协议检查
使用新候选程序、全新隔离配置和 SQLite,监听 `127.0.0.1:19017`,保持非 root 与系统加固。由于 Linux 初始化地址固定写入 `/run/scert/bootstrap-url`,测试单元使用私有 `/run` 及 `BindPaths` 将独立目录映射到该路径,不写正式运行目录。不运行固定安装器、不替换 `/scert/scert`。
通过项目:
1. 从可信 SSH 日志取得当前启动证书指纹,与实际 TLS 证书匹配;之后所有业务请求严格验证证书及 IP。探测证书时不发送令牌、Cookie 或业务请求。
2. 初始化页面和构建 JS/CSS 返回有效内容;这只是协议与资源检查,不是浏览器渲染验收。
3. 未登录的设置接口返回 401;跨域令牌兑换返回 403;正确令牌兑换成功后重复兑换返回 401。
4. Cookie 具有 `Secure`、`HttpOnly`、`SameSite=Strict`,路径限定为 `/api/v1/bootstrap`;令牌文件为 `0600`,URL 查询参数不携带令牌。
5. SQLite 写权限验证通过;缺少 DNS 令牌返回 400,随机无效 DNS 令牌返回 422;配置字节不变,不伪造初始化完成。
6. 重启本轮候选单元后,临时证书和一次性令牌均轮换,旧令牌及旧 Cookie 返回 401,新令牌可兑换。
7. 停止本轮候选单元后,端口不再监听,程序正常清理自己的初始化地址文件。隔离配置、密钥及测试产物保留在受限目录,禁止提交 Git。
协议检查脚本保留在远程 `bootstrap_protocol_netlink_v2.py`,不含固定密码或 Token;测试使用的临时令牌和随机管理员密码仅在内存中处理。第一次脚本把缺少 DNS 参数的预期码误写为 422,修正为 400 并单独增加无效令牌 422 检查后整轮通过,未因此修改应用校验规则。
11:24 收尾确认:没有本轮 PostgreSQL 进程残留,`19017` 无监听;正式 Scert 仍为 PID `530794`、重启次数 0,MySQL 仍为 PID `549584`、重启次数 1;正式程序 SHA-256 与初查一致。正式就绪接口仍报告 `setup_incomplete`。当时 `MemAvailable` 约 215 MiB,主机容量风险仍需持续监控。
## 桌面浏览器修复与最终候选复测
11:36 起继续使用独立本地配置和远端隔离目录验证,未清空业务数据库。浏览器测试连接本机 Windows 候选,Linux 实机测试通过 SSH 执行真实协议请求;二者不能混称为远程浏览器全流程验收。
修复与证据:
1. `4993199`:邮箱每 250 毫秒自动同步会清除全局错误,导致数据库未验证提示一闪即逝。自动同步现在保留错误,手动编辑仍可清除;新增回归先失败后通过。同步修复用户名与邮箱输入框上边缘错位,补齐数据库字段标识。
2. `f0c735a`:SMTP 固定四列在窄桌面裁切密码框,改用自适应网格,并补齐 DNS 明文复选框标识。1024×768 与 1440×900 的控件边界测量、实际截图检查通过。
3. `0bee64c`:真实浏览器发现数据库验证成功后仍显示此前的数据库步骤错误。只在验证成功时清除两类数据库提示,不清除密码等其他错误。新增三项回归后前端共 44 项全部通过;真实浏览器确认验证前错误持续显示、成功后错误消失且成功提示保留。
最终候选源码为 `0bee64ce8607831d025df8ea1ef52e39b57d026f`,从独立干净克隆构建,清单 `source_dirty=false`。本地归档为 `build/linux-db-message-20260917-1220/Scert-20260917-V0.1.0-linux-amd64.tar.gz`,SHA-256 为 `5a0b74167961cae3cbab9f7e47052df61ecf47c2c96584255c7c3842e470cb2a`。新 JS 为 `index-DYAdDhBP.js`,CSS 为 `index-B53m06GR.css`。
最终本地浏览器使用全新配置、数据库路径和 Chrome profile,监听 `https://127.0.0.1:19027`。先核对日志证书 SHA-256,再仅允许匹配 SPKI 的临时证书;没有全局忽略证书错误。页面实际加载新 JS,SQLite 在线写权限验证通过。两种桌面宽度下所有 SMTP 输入和下拉框均在标签与面板内,无整页横向溢出。截图保留于 `.runtime/browser-20260917-1223/smtp-1024.png` 和 `smtp-1440.png`,均已实际查看。此前版本的 SMTP/HTTP 必填提示与 DNS 缺凭据阻断也已检查;没有提交真实 DNS 凭据或发起 ACME 签发。浏览器另记录一条表单字段缺少标识的非阻断提示,未将其宣称为已解决。
最终 Linux 归档上传至 `/scert/acceptance-20260917-1044/release-message-0bee64c.tar.gz`,解压到独立 `message-0bee64c/release`。归档哈希、包内 12 项校验、安装器语法和程序版本检查通过。候选单元 `scert-message-0bee64c.service` 保留发布加固,限定 `MemoryMax=96M`、`MemorySwapMax=0`、`CPUQuota=25%`、`GOMEMLIMIT=48MiB`、`GOMAXPROCS=1`、`RuntimeMaxSec=900`,使用私有 `/run` 映射,不替换正式程序。
首次紧接启动执行协议脚本时未及时读到证书指纹而退出;候选就绪后重跑全部通过,没有为此放宽证书校验。通过项包括当前构建资源、严格 TLS 指纹、未登录 401、跨域兑换 403、一次性令牌不可复用、安全 Cookie、SQLite 写权限、重启证书与令牌轮换、旧会话失效及配置字节不变。脚本分别保留为远端 `linux_message_candidate.py` 和 `linux_message_protocol.py`。
约 12:22 收尾确认:最终 Linux 候选已停止、PID 为 0、19017 无监听;正式 Scert PID 仍为 `530794`、重启次数 0,MySQL PID 仍为 `549584`、重启次数 1;正式程序哈希不变。`MemAvailable` 约 320 MiB。正式 `/readyz` 仍为 `setup_incomplete`,共享机器的历史 OOM 和容量风险尚未解除。
## 尚未满足的生产准入条件
桌面浏览器前三步已部分验收,但合法授权域名与 RAM 的 DNS 写入清理、ACME 签发、初始化完成到正式入口的切换、登录与多用户、升级回退、目标机证书推送及最终业务访问仍未完成。本轮没有操作 `8.137.23.95`,没有改动 `pcux.cn` 及其子域。继续外部签发前需明确可写测试域名与凭据授权范围,SSH 密码不等同于 DNS 授权。正式服务仍返回 `setup_incomplete` 时不可正式切流;受控隔离测试通过不能替代上述验收。
## 账户边界修复与续测
12:30 至 12:50 继续审查用户管理、登录和修改密码,完成以下项目:
- [✓] `60b00f1` 修复用户管理可修改主超级管理员数据库行却不更新配置的问题。新增回归先复现用户名、邮箱、密码修改返回 200,以及新增第二个超级管理员返回 201,修复后均拒绝。
- [✓] 按已确认的单超级管理员需求,禁止通过用户管理新增或提权为超级管理员;禁止通过该接口修改主账号。既有历史账号不自动删除,不修改正式数据库。
- [✓] 主账号登录只接受配置凭据,数据库残留凭据不能取得随后立即失效的会话。保留持当前密码的自助改密;真实 SQLite 回归检查配置与数据库摘要一致、旧会话失效、旧密码拒绝、新密码登录及重建服务后的会话有效。
- [✓] 本地后端 HTTP API、仓库、认证、CLI、归档五个包回归通过;前端共 47 项测试通过。
- [✓] Linux 执行账户相关 9 个顶层测试及 5 个字段子场景全部通过,退出码 0。独立 systemd 单元限制 96 MiB、CPU 25%、无交换、单 Go 线程配额;运行 5.556 秒,峰值内存 17.7 MiB。测试程序 SHA-256 为 `24e779f320228b2c4afe2abcc5c781c2bcc0a490f502a6c49371d2a53a9226dd`。这是实机运行的自动化测试,不是生产账号全流程验收。
- [✓] 真实浏览器发现用户管理依赖父组件的作用域样式,弹窗实际落在普通文档流中且按钮无样式。`a3d0f6c` 在用户管理自己的 CSS 中补齐遮罩定位、表单、按钮和响应式约束。
- [✓] 最终发布资源使用本地只读界面夹具、独立 Chrome profile、端口 19028 验证。1024×768 和 1440×900 下弹窗为 620×439,8 个控件和按钮均在弹窗边界内;主账号没有编辑、禁用和删除按钮,新增角色没有超级管理员选项。两张修复后截图均已查看:`.runtime/users-browser-20260917/create-fixed-1024.png`、`create-fixed-1440.png`。夹具不是正式后端,不能据此宣称浏览器完整登录或写入通过。
最终候选从独立干净克隆构建,源码为 `a3d0f6c`,清单 `source_dirty=false`。本地归档 `build/linux-users-ui-20260917-1245/Scert-20260917-V0.1.0-linux-amd64.tar.gz`,SHA-256 为 `92b5d22b44a1b1a91ef6ba62eea9d48faebace26cc04e892aa230368e32f30ec`;资源为 `index-DsUM7ZTg.js` 和 `index-C7wT0wtw.css`。远程归档及包内 12 项校验通过,独立目录为 `/scert/acceptance-20260917-1044/users-ui-a3d0f6c/release`。
最终候选在发布 systemd 加固基础上使用私有运行目录、96 MiB 内存上限和 CPU 25% 配额,实际启动通过。严格 TLS 指纹、新构建资源、未登录 401、跨域 403、一次性令牌、安全 Cookie、SQLite 写权限、重启证书及令牌轮换、旧会话拒绝、配置字节不变全部通过。未发起 DNS 或 ACME 操作。辅助脚本为远端 `linux_users_ui_candidate.py` 和 `linux_users_ui_protocol.py`。
12:50 收尾确认:本轮两个 Linux 候选单元已停止,19017 无监听;本地夹具及专用 Chrome MCP 已停止,SSH 已关闭。正式 Scert PID `530794`、重启次数 0,MySQL PID `549584`、重启次数 1,正式程序哈希未变;正式 `/readyz` 仍为 `setup_incomplete`,可用内存约 313 MiB。升级回退、真实 DNS 签发、完整初始化与切流、目标机推送及业务验收仍未完成。原有测试产物全部保留。
## 安装失败回退修复与发布包续测
12:54 至 13:11 审查安装升级流程,修复提交为 `8c3d8786808cd142229af4b1c6422e9d638aa0c0`。修改前检查点 `9d3bd50` 和修复提交均已推送。没有在宿主机直接运行固定路径安装器,没有停止或替换正式服务。
### 缺陷与修复
旧包在真实 Linux 文件系统上复现四类问题:既有 Web 符号链接在二进制已经替换后才拒绝;二进制目标为目录时错误返回安装成功;第二个 unit 写入失败与 daemon-reload 失败均留下新旧发布文件混用。旧包四场景测试整体退出码为 1,不是成功验收。
新安装器提前检查文件和目录类型,保留 root 所有、`0700` 的 `/scert/.install-*` 副本;捕获安装失败后恢复发布文件并重新加载 systemd,不启动服务。Web 改为整目录替换,旧文件和失败现场只移动保留、不删除。回退自身失败时明确提示禁止启动服务。业务目录权限规范化、用户创建、断电和 SIGKILL 不在自动回退保证内,安装成功后的数据库迁移也不由此机制恢复,详见运维说明 4.2。
### 隔离方法与结果
使用仓库 `deploy/tests/install-linux-test.py`,在私有挂载命名空间中将新建测试目录映射到固定的 `/scert`、`/etc/systemd/system` 和本机既有 `/ssl-scert-acceptance`。源码以只读挂载保留,执行前核对挂载 inode。systemctl 被限定为测试桩,只允许状态检查和 daemon-reload,不能操作宿主服务。使用已有 Scert 用户,不创建新的宿主账号。
第一版脚本因宿主 `/ssl` 不存在在预检阶段退出,未执行安装器;随后显式指定已有证书挂载点,并把产物放在被覆盖路径以外的 `/var/tmp/scert-installer-20260917-1303`。所有产物保留。受控单元限制 `MemoryMax=96M`、`MemorySwapMax=0`、`CPUQuota=25%`、`TasksMax=32`、`RuntimeMaxSec=180`,与候选服务顺序执行。
最终发布包八场景均通过,整体退出码 0:
| 场景 | 安装器预期退出码 | 最终产物子目录 |
| --- | --- | --- |
| Web 符号链接拒绝 | 1 | `web-link-y4ivfj_n` |
| 二进制目标目录拒绝 | 1 | `binary-directory-o3gvmqpe` |
| unit 写入失败后恢复旧版 | 43 | `unit-failure-4c4c8ou_` |
| reload 失败后恢复旧版 | 42 | `reload-failure-bffej3sp` |
| 回退 reload 再失败,明确禁止启动 | 42 | `rollback-reload-failure-qrwm_v37` |
| 升级成功,旧 Web 资源退出生效目录 | 0 | `success-cgz9m71a` |
| 首次安装失败,发布文件回到缺失状态 | 43 | `fresh-failure-y_2axidy` |
| 首次安装成功 | 0 | `fresh-success-e7foazs_` |
每场景检查业务文件内容与证书文件不变、旧发布文件恢复、服务文件恢复或缺失状态、升级副本权限及服务操作白名单;各目录包含 `report.json` 和 `output.txt`。这是隔离文件安装与故障注入回归,不能等同于真实 systemd 升级、正式数据库迁移或完整业务回退验收。
### 新包与通用检查
- 独立干净克隆:`C:/Users/ikun/AppData/Local/Temp/scert-installer-20260917-1306`,源码 `8c3d878`,清单 `source_dirty=false`。
- 发布包:`build/linux-installer-20260917-1306/Scert-20260917-V0.1.0-linux-amd64.tar.gz`。
- SHA-256:`9c89140467264ee620bcd1ab4ba4fcea6a43430a4ea87fa6c7f91fa3be97c6ea`。
- 远程:`/scert/acceptance-20260917-1044/installer-8c3d878/release`;归档哈希、包内 12 项校验、安装器语法及版本检查通过。
- Windows 本地执行 `go test ./...`、`go test -race ./...`、`go vet ./...`、`go mod verify` 全部通过;前端 47 项测试通过。不能将这些表述为 Linux 全量测试。
- `govulncheck ./...` 及详细扫描退出码 0:调用符号和导入包无已知漏洞,依赖模块包含 `GO-2026-5932`,涉及未维护的 `golang.org/x/crypto/openpgp`,当前代码没有导入或调用该漏洞包,公告没有可用修复版本。不能写成依赖模块零漏洞。
- 新候选 `scert-installer-8c3d878.service` 使用发布加固、私有运行目录和 96 MiB 限制启动;严格证书指纹、当前资源、未登录 401、跨域兑换 403、令牌不可复用、安全 Cookie、SQLite 验证、重启证书与令牌轮换、旧会话失效和配置字节不变均通过。没有 DNS 或 ACME 操作。本轮不涉及页面修改,未重复浏览器渲染测试。
13:09 收尾检查:候选服务 inactive、PID 0、19017 无监听;正式 Scert PID `530794`、重启次数 0,MySQL PID `549584`、重启次数 1,正式程序 SHA-256 未变。可用内存约 314 MiB。正式回环端口 9004 使用 HTTP,先用 HTTPS 探测返回协议错误,改用实际 HTTP 后 `/readyz` 明确返回 `setup_incomplete`;这不是正式 TLS 验收。SSH 已关闭,所有测试进程结束。
- [✓] 安装器缺陷复现、修复、新发布包八场景复测及受限候选协议检查。
- [✓] 本地全量测试、race、vet、模块完整性及漏洞调用路径扫描。
- [!] 真实 systemd 升级与业务回退、DNS 签发、完整初始化、正式切流和目标机推送仍未完成。继续外部签发需要用户明确授权测试域名及 DNS 凭据范围,当前不可宣称具备正式切流条件。
## 安装前停服状态核验补充(2026-09-17)
### 现场恢复与需求
- [✓] 原工作目录 `D:/Desktop/SSL` 已不存在;当前项目在 `D:/Desktop/Scert`,起始提交仍为 `cf1fe15`,已跟踪文件无改动。未移动、删除或覆盖既有未跟踪产物。
- [✓] 修改前创建并推送中文检查点 `f04f873`。
- [✓] 管理机只读复核:正式 Scert PID `530794`、重启次数零;MySQL PID `549584`、重启次数一;候选服务停止;正式程序摘要与前轮一致。
- [!] 正式 `/readyz` 仍为 HTTP 503、`setup_incomplete`。管理机 443 由 Smail 占用,最终入口方案需要协调,不可直接占用或停止该服务。
### 缺陷与复现
旧安装器将 `systemctl is-active` 的所有非零退出都当成已停服;不能区分过渡状态和查询故障。使用旧包 `8c3d878` 在私有挂载命名空间注入启动中、停止中、主服务查询失败和管理服务查询失败,四个场景都错误返回零并替换隔离目录中的发布文件。
首次测试因产物目录不存在在预检阶段退出,没有执行安装器;创建全新产物目录后完成上述四场景复现。证据目录 `/var/tmp/scert-install-state-before-20260917-1530`,测试整体退出码一。路径中的时间后缀仅为标识,不代表实际执行时刻。
### 修复与验证
安装器改为显式检查六个 systemd 属性,未知状态和查询失败均拒绝。回归覆盖原八场景以及 active、activating、deactivating、reloading、failed 但仍有进程、排队任务、masked、首次不存在、failed 且已停服、查询失败、空输出、缺失字段和管理服务拒绝分支,共 22 场景。
- [✓] 本地 `go test -count=1 ./internal/admin` 通过,`git diff --check` 通过。
- [✓] 使用旧发布文件的独立副本加上新安装器,Linux 22 场景全部通过;退出码零,运行约 33.6 秒。
- [✓] 拒绝路径对整个安装目录快照(内容、类型、权限、所有者)不产生变化,unit 和证书保持不变,未执行 daemon-reload。
- [✓] 其余场景继续验证首次安装、旧资源退出生效目录、失败发布文件恢复、回退失败提示以及业务文件保护。
产物 `/var/tmp/scert-install-state-after-20260917-1535`。测试通过 systemd 临时单元限制内存 96 MiB、无 Swap、CPU 25%、32 任务、240 秒;systemctl 在安装器命名空间内仍为状态/故障注入桩。宿主 systemd 仅管理测试资源,正式 Scert、MySQL 和 Smail 不停止、不重启。
这证明安装器的状态分支和文件行为,不证明完整真实 systemd 升级、业务回退或最终发布包已验收;最终包验证需单独补记。未使用 DNS、ACME 或业务数据库。
### 发布与收尾
- [✓] 修复提交 `6b6fc126868731003e70e1225f6ae455f26fb235` 已推送,远程 main 已核对一致;本地 `go test -race -count=1 ./internal/admin` 通过。
- [✓] 本地与远程实测安装器 SHA-256 一致:`08b7b04411b9250414d0429b10e99a4642c750163febf3da65281b445eb7fa57`。
- [!] 干净克隆位于 `C:/Users/ikun/AppData/Local/Temp/scert-state-20260917-1532`。调用仓库发布脚本时被 PowerShell 执行策略拒绝,未绕过策略,未生成或上传本次新发布包。此前 `8c3d878` 发布包不包含本次停服检查修复,不能用于宣称最新版本验收。
- [✓] 15:32 收尾核查:测试 unit 为 inactive、PID 零、结果 success;正式 Scert PID `530794`、重启次数零,MySQL PID `549584`、重启次数一,正式程序摘要未变。可用内存约 320 MiB,19017 无监听,SSH 随后关闭。
- [!] 正式初始化、维护窗口内停服升级、业务回退、DNS 与 ACME、目标机业务推送及最终入口仍未验收。继续需要发布脚本按本机管理策略获准执行,并确认维护窗口、域名及 DNS 操作范围、与既有 Smail 不冲突的入口方案。
## 初始化探针修复与正式升级复验(2026-09-17 晚间)
### 需求与结论
- [✓] 接续升级后的在用服务,核对 HTTPS 身份、健康与就绪状态。
- [✓] 修复首次初始化阶段健康探针缺失,保持未初始化就绪门禁。
- [✓] 干净提交构建、发布包校验、Linux 隔离协议及正式停服升级。
- [✓] 升级前后受保护文件一致性、完整快照及独立目录解包验证。
- [!] 正式初始化、公共信任证书、外部入口、业务推送与真实故障回退仍未通过,不能正式切流。
### 缺陷与本地验证
接手时正式进程 PID 为 `570938`,监听 `127.0.0.1:9004`,处于新的 Bootstrap HTTPS 模式。通过当前 systemd 启动日志取得指纹,核对网络证书后,再使用该证书作为专用信任根检查证书名称和有效期。页面与状态接口返回 200,设置接口返回 401,但 `/healthz` 和 `/readyz` 均为 404。旧记录中的 HTTP 503 不适用于这个新启动链路。
修改前检查点 `48c2996` 已推送;首次推送认证失败,重试成功。修复提交 `c1ac3e91c07447243ead56fb0d8eca0b6ca8a938` 已推送,仅修改 Bootstrap 处理器及测试。新增回归先复现两个 404,再验证健康返回 200、就绪返回 503 和 `setup_incomplete`,拒绝错误方法,不缓存、不创建会话,也不提前打开业务数据库。
以下本地验证均通过:
```text
go test -p 1 -count=1 -timeout=3m ./...
go test -count=1 -timeout=3m ./internal/bootstrap ./internal/httpapi
go test -race -count=1 -timeout=3m ./internal/bootstrap
go vet -p=1 ./internal/bootstrap ./internal/httpapi
npm test
git diff --check
```
前端共 47 项通过。本轮没有修改前端,也没有重新进行浏览器渲染验收;没有执行新的全量竞态检查或漏洞扫描。
### 发布与隔离实机
从独立干净克隆构建,发布清单为 `source_dirty=false`,工具链 Go 1.26.6。首次因克隆缺少前端依赖而构建失败,保留失败目录;执行锁定依赖安装后改用全新输出目录构建成功。
- 本地归档:`build/linux-health-20260917-2205/Scert-20260917-V0.1.0-linux-amd64.tar.gz`。
- 归档 SHA-256:`b44f5b2a1125af08fd15356477f25ea4e17f8cf3a29d4f07ccbe8d63467d76b0`。
- 主程序 SHA-256:`b91c1a88382b29b6016488e340087cb25a205f684273c0d10d473803b10b9a41`。
- 本地清单 14 项、远端包内 12 项均核对通过。目录时间后缀是标识,不代表实际执行时间。
远端脚本及包位于 `/scert/acceptance-20260917-1044/`,最终隔离目录为 `health-c1ac3e9-v2`。候选限制内存 96 MiB、无 Swap、CPU 25%、32 任务,使用全新 SQLite 路径与私有运行目录,监听回环 19017。通过项包括 HTTPS 指纹与完整验证、新探针、构建资源、未登录拒绝、跨域拒绝、一次性令牌、安全 Cookie、SQLite 写权限、缺少 DNS 验证拒绝提交、重启证书及令牌轮换、旧会话失效、配置字节不变。
第一次协议检查通过后,附加断言错误地要求 SQLite 文件不存在。实际写权限测试会留下空数据库,现场查询无业务表且完整性正常;修正测试断言后,在新目录重跑全部通过,退出码零。没有为通过测试而修改业务实现,也没有删除第一轮产物。
同一源码交叉编译的 Bootstrap 测试程序在 Linux 执行,探针、路由隔离和取消清理通过。另执行 `TestHTTPSBootstrapCompletesSQLiteAndStartsApplication` 的两个场景,严格 TLS、SQLite 初始化、正式服务切换、登录、用户接口及夹具 `/readyz=200` 全部通过。两组受限测试峰值内存分别约 52.4 MiB 和 53.4 MiB。该集成夹具不执行真实云端 DNS 或 ACME,不能替代在用服务初始化。
### 备份与正式升级
21:50 左右核验此前快照 `/var/backups/scert-pre-upgrade-20260917-211804/snapshot.tar.gz`,SHA-256 为 `a9ba56f7e7e9dd3f6ac2ae2b13d11f00a3ae5ff7a749493aa09d52f99a198288`。配置、凭据、密钥和 SQLite 与快照逐项一致,业务数据库只读完整性检查为 `ok`。之后解包至全新 `0700` 目录 `/var/backups/scert-restore-check-c1ac3e9`,29 个文件与归档逐项一致,恢复副本 SQLite 完整性正常。未启动恢复副本,不把文件恢复验证称为完整业务回退。
约 22:00 正式更新到 `c1ac3e9` 前,再次停止 Scert 并创建完整快照 `/var/backups/scert-pre-health-c1ac3e9/snapshot.tar.gz`,逐文件核对通过,SHA-256 为 `7d6c39afbd43cb22ae95a98f94b5a2a4042aa86057b7ce7623698569c1e1bac9`。快照目录权限 0700,文件按 0600 创建。安装器退出码零,主程序摘要与新包一致,随后启动正式服务。
升级脚本检查以下结果全部通过:
- 正式 Scert 为 active/running,PID `572525`,重启次数零。
- 当前进程证书与可信 SSH 日志指纹一致,随后完整 TLS 验证下 `/healthz=200`、`/readyz=503 setup_incomplete`。
- Bootstrap 状态与页面 200,未登录设置 401,业务用户接口 404。
- 配置、数据库、密钥及证书文件的内容、权限、UID/GID 升级前后完全一致。
- MySQL PID `549584`、重启次数一,与升级前一致;443 的 Smail PID `570415` 不变。
- 所有本轮候选测试已停止,没有清空正式数据库、修改 DNS 或操作推送目标机。
### 剩余生产门槛
本轮正常 TLS 连接正式 ACME 目录已返回 HTTP 200,证书验证结果为零,旧的出站超时暂未复现;这仅证明当时目录连接成功,不代表签发成功或网络长期稳定。
在用配置仍是 `setup_ready=false`,后台域名仍为 `scert-accept-20260914.epwn.uno`,未对其发起签发。下一步需要用户明确管理域名、对应 DNS 写入和精确清理授权,以及不影响 Smail 443 的入口与维护范围,才能继续正式初始化、生产签发、外部 HTTPS/SNI、浏览器登录及真实业务推送。真实故障回退仍未完成,机器可用内存约 300 MiB,容量风险也未解除。
22:17 最终只读复核:Scert PID 仍为 `572525`、重启次数零,MySQL 状态不变;四个本轮测试单元均 inactive、PID 零,19017 无监听。备份摘要及 0700/0600 权限再次通过。此时 Smail 的 443 进程已经变为 PID `571735`,系统有 4095 MiB Swap、可用内存约 634 MiB,与升级时状态不同;本轮未操作 Smail 或 Swap,变更来源尚未核查,不能把升级时的“保持不变”延伸为整个时段。容量风险需依据新环境另做负载验证。
## 双 Linux 正式候选健康复核(2026-09-30 21:12)
本轮使用密码 SSH 进行只读检查,未创建辅助验收脚本,未清理或修改远端业务数据库,未操作 Smail。检查目标为正式候选安装后的服务恢复状态,不替代完整生产业务验收。
| 检查项 | `8.137.23.95`(`ikun1`) | `45.136.13.241`(`yc`) |
|---|---|---|
| `Scert` systemd 状态 | `active` | `active` |
| `nginx` systemd 状态 | `active` | `active` |
| `Scert-storage.socket` 状态 | `active` | `active` |
| `https://127.0.0.1:8443/readyz` | HTTP `200` | HTTP `200` |
| Nginx 监听 | `80/443` | `80/443` |
| Scert 内部 HTTPS 监听 | `*:8443` | `127.0.0.1:8443` |
两台服务器均未发现候选安装覆盖 Nginx `80/443` 监听端口。第二台的 `8443` 仅绑定回环地址仍可由本机 Nginx 代理使用,但证书推送和业务流量尚未通过管理页面完成真实验证,不能据此记录多服务器部署验收通过。
### 管理域名只读检查
- `https://odacm.cn/` 返回 HTTP `200`,能够读取 Scert Web 首页。
- 当前本机 DNS 将 `odacm.cn` 解析为 `198.18.0.109`,不是本轮两台测试服务器地址。
- 因此本轮不把首页 200 记录为公网 DNS、HTTPS/SNI、ACME 或生产入口验收通过;仍需确认真实 DNS 目标、入口归属和证书链路。
### 当前结论
- [x] 双 Linux 正式候选服务安装后健康状态复核通过。
- [~] 一域名多服务器、单服务器多域名、证书推送、Nginx reload、失败回滚、恢复接管和重试幂等仍在复核中。
- [ ] 三数据库生产矩阵、Windows 服务、清库首次初始化、备份恢复、公网 DNS/ACME 和最终生产门禁尚未完成。
- [!] 本轮不新增 `[✓]`,不以 `/readyz=200` 或首页 200 替代完整生产部署验收。
## 代码整改复核记录(2026-09-30)
- [x] 证书部署工作流已改为按目标服务器逐台执行 HTTPS 基线、部署后证书指纹和恢复校验。
- [x] 生产应用已接入目标服务器地址拨号,域名仍作为 TLS SNI 和证书校验名称。
- [x] 已移除远端 HTTPS 校验的跳过证书验证配置,改用受信任根证书进行严格 TLS 校验。
- [x] `go test ./...`、`go vet ./...`、`git diff --check` 通过;新增指定地址与域名 SNI 回归测试。
- [~] 尚未重新部署本轮最新候选包;因此本节代码结果不能替代两台服务器的实机业务验收。
- [!] 第一台服务器仍需确认并通过真实 Nginx 站点配置、证书加载和独立 HTTPS 指纹校验;完整生产门禁继续未通过。
## 2026-10-01 部署锁获取失败事务终态修复记录
### 本轮结果
- 修复 `deployOne` 在创建部署事务后、锁获取阶段提前返回而未写入最终状态的问题。
- 锁获取失败、锁归属写入失败、锁阶段持久化失败现在都会执行锁清理并写入 `rolled_back`;若锁释放无法证明或发生释放失败,则写入 `recovery_required` 并保留人工核查现场。
- 新增两类真实代码回归测试:无锁持有时的锁冲突、部分锁持有时的锁冲突。
- `go test ./...`:通过。
- 代码提交:`1fad8db`;测试提交:`d315179`。
### 当前限制
- 本轮尚未使用最新提交重新构建并部署两台 Linux 服务器,因此本轮本地测试不能替代实机验收。
- `8.137.23.95` 的 `recovery_required` 现场仍需重启候选服务后复核;`45.136.13.241` 的历史空锁目录只有在再次确认无 owner、无活动进程且属于历史测试现场后才允许清理。
- 双服务器真实业务部署、失败恢复、重试幂等、三数据库、Windows、公网 DNS/ACME、SMTP、备份恢复和最终生产门禁仍未完成。
### 下一步
1. 构建包含本轮修复的最新 Linux amd64 候选包。
2. 使用密码 SSH 部署到 `8.137.23.95` 和 `45.136.13.241`。
3. 重启服务并复核恢复事务、锁 owner、Nginx 校验、reload 和 HTTPS。
4. 通过正式页面重新执行一域名多服务器、多域名单服务器部署验证。
5. 更新实机证据后再决定是否推进总体验收位置。
本轮未创建辅助验收脚本,未以辅助脚本作为验收条件。
## 最新实机复核(2026-10-01,最新候选包双服务器业务部署)
本轮使用密码 SSH 和正式管理接口复核,未创建辅助验收脚本,未修改远端数据库内容,未关闭 TLS 校验。Windows 环境按当前上线范围暂不处理。
- [x] 最新候选包已安装到 `8.137.23.95` 和 `45.136.13.241`;两台服务器的 `Scert`、`Scert-storage.socket`、Nginx 均为 `active`。
- [x] 两台服务器的 `https://127.0.0.1:8443/readyz` 均返回 HTTP `200`。
- [x] 已验证域名 `scert-multi-20260930.apmmk.cn` 同时绑定两台服务器;服务器端反向查询也能看到该域名。
- [x] 已验证服务器 `45.136.13.241` 同时托管 `scert-20260930.apmmk.cn` 和 `scert-multi-20260930.apmmk.cn`;满足一台服务器多个域名的关系模型要求。
- [x] 使用最新候选服务重新执行证书部署,返回 `total=2`、`success=2`、`failed=0`,两台服务器均报告“证书部署、配置校验并 reload 成功”,`verification_status=success`。
- [x] 两台服务器目标证书文件均已写入 `/ssl/scert-multi-20260930.apmmk.cn/single/`,证书 SHA-256 指纹均为 `43:06:3C:07:26:D3:43:62:B2:54:CE:28:B6:6D:7E:B6:37:E0:0B:16:5F:68:EC:0D:07:FC:ED:0D:F2:C6:4A:D2` 的同一证书(记录中保留冒号格式时以 OpenSSL 输出为准)。
- [x] 两台服务器均执行 `nginx -t` 成功,并通过本机 SNI HTTPS 请求返回 HTTP `200`。
- [x] 已确认历史失败部署任务的失败结果持久化了各服务器状态和备份标识;最新候选服务启动后未再产生新的空归属 `acquiring` 事务。
- [!] 现有历史 `recovery_required` 事务仍缺少可信恢复证据,未直接改库伪造成功;相关现场保留人工核查状态。
- [~] 失败回滚的真实双服务器故障注入、任务级重试幂等、真实公网 DNS/ACME、Smail 系统账户 SMTP、三数据库矩阵和备份恢复仍需继续。
### 当前任务看板
- [x] T1 主清单、执行清单和 Linux 实机记录已同步。
- [x] T2 部署锁获取失败、空锁恢复和事务终态修复已完成并部署复核。
- [x] T3 最新 Linux amd64 候选包双服务器安装及基础健康检查完成。
- [x] T4 一域名多服务器、单服务器多域名关系和最新候选证书部署成功。
- [~] T5 真实失败回滚、重试幂等和历史恢复证据统一复审。
- [~] T6 公网 DNS/ACME、Smail SMTP、数据库生产矩阵和备份恢复。
- [ ] T7 最终生产门禁和上线结论。
总体验收位置仍为 **P0-03:完成,待复审**,本轮不得标记全量生产验收通过。
## 2026-10-01 反向代理端口收敛记录
本节补充当前部署要求,不覆盖历史记录。当前仅处理 Linux;本轮是保留数据的端口配置复核,不代表清库首次初始化验收。
- [x] 已完成,等待复审:两台服务器通过密码 SSH 核验,Scert 均仅监听 `127.0.0.1:8443`;没有 `0.0.0.0:8443` 或 `[::]:8443` 监听。
- [x] 已完成,等待复审:Nginx 对外监听 `80/443`,两台服务器的 `nginx -t` 均通过;未修改邮件及其他既有站点。
- [x] 已完成,等待复审:第二台管理入口 `odacm.cn` 的 HTTP 请求返回 301 并跳转 HTTPS,反代 `/readyz` 和回环上游 `/readyz` 均返回 200;上游启用 SNI 和证书校验。
- [x] 已完成,等待复审:从第二台服务器按正常 DNS 访问管理域名,连接地址为 `45.136.13.241`,HTTPS 就绪检查返回 200;该结果不是独立外部客户端的完整生产验收。
- [x] 已完成,等待复审:静态 systemd 模板已提交并推送 `48d10fe`;动态生成的 Linux unit 同步回环监听约束,并补充模板与生成器回归断言。
- [x] 已完成,等待复审:本地后端 `go test ./...`、`go vet ./...` 和 `git diff --check` 通过;本机 Windows 测试不代替 Linux 专属用例执行。
- [~] 进行中:使用干净提交构建 Linux 候选包,核对安装后的默认 unit 和动态生成配置,并补充 Linux 专属用例证据。
- [!] 暂缓或存在阻塞:历史恢复事务、双服务器真实失败回滚与重试幂等、Smail SMTP、数据库生产矩阵、备份恢复及最终生产门禁仍未完成。
端口边界:不新增公网业务端口,不为内部 `8443` 添加防火墙或安全组放行规则。SSH 运维端口与其他已有服务不属于本次端口收敛的修改范围。总体验收保持 **P0-03:完成,待复审**,不新增全量验收通过结论。
### 反向代理端口收敛复核结果(2026-10-01 16:35,北京时间)
- [x] 已完成,等待复审:构建提交 `51f7f3e55cee16d3b747bea30e0382fca4b25dd1`,发布清单 `source_dirty=false`;Linux 包位于 `build/release-20261001-loopback/Scert-20261001-V0.1.0-linux-amd64.tar.gz`。
- [x] 已完成,等待复审:包 SHA-256 为 `2953f52d252194fda7865e1ee06e548204fdfdb818dab28ce10611087ba94443`;两台服务器上传后校验一致,包内 `SHA256SUMS` 全部通过。
- [x] 已完成,等待复审:两台服务器均经官方安装器安装成功并启动,安装退出码均为 0;主服务、存储 socket 和 Nginx 均正常,主服务重启计数为 0。
- [x] 已完成,等待复审:两台服务器 `/scert/scert` 的 SHA-256 均为 `beeade4c38e1be7f8af29b42d08896ca4f9efcb782c3f0a278f58fd0abc9baae`,与本地候选二进制一致;已安装的主 unit 自身包含回环监听设置,不再仅依赖 drop-in。
- [x] 已完成,等待复审:在第二台 Linux 上执行仓库 `internal/servicehost` 的编译测试程序,9 项全部通过,覆盖动态 unit 和发布模板回环约束;没有创建辅助验收脚本,此结果仅作为代码回归证据。
- [x] 已完成,等待复审:本地前端 241 项测试通过,正式前端及 Linux 后端构建通过。
- [x] 已完成,等待复审:升级后两台 Scert 均监听 `127.0.0.1:8443`,Nginx 仍对外监听 `80/443`,`nginx -t` 通过;未改动已有站点或新增公网放行规则。
- [x] 已完成,等待复审:第二台回环上游按管理域名 SNI 和系统 CA 严格验证返回 200;第一台作为独立客户端按正常 DNS 访问 `https://odacm.cn/readyz` 返回 200,连接地址为 `45.136.13.241`。
- [x] 已完成,等待复审:两台服务器交叉探测对方公网 `8443` 均超时;结合实际监听地址确认该内部端口未对外监听,未据此声称其他已有服务端口全部关闭。
- [!] 边界说明:第一台内部管理服务沿用原有 IP 自签证书,使用测试域名和系统 CA 直连会失败;通过密码 SSH 核查证书及 IP SAN 后,以该证书作为显式信任文件访问回环上游返回 200,未关闭 TLS 校验。这仅是该机内部健康核验,不是第一台管理入口的公网受信 TLS 验收。
- [!] 边界说明:本轮保留数据库,没有执行清库首次流程;未额外制作数据备份,官方安装器自身保留升级前发布文件以支持失败回退。完整生产验收仍为 **P0-03:完成,待复审**。
### 部署重试阻塞修复进度(2026-10-01 16:51,北京时间)
本节为本轮当前状态说明,前文保留为历史记录。P0-03 的局部代码复审状态不代表整个项目的生产验收状态;本轮上线范围仅 Linux,不将 Windows 列为阻塞。
- [✓] 复审通过:双机服务保持回环监听 127.0.0.1:8443,由 Nginx 80/443 对外提供服务;本轮未新增公网业务端口。
- [x] 已完成,等待复审:通过正式部署接口制造第一台 Nginx 校验命令失败,两台部署报告整体失败,恢复报告为 success;随后已恢复正常校验命令,双机 nginx -t 通过。该验证使用同一证书重部署,不代表不同新旧证书切换验收。
- [x] 已完成,等待复审:定位部署重试自冲突。HTTP 重试已创建尝试并标记原任务运行,但应用装配再次调用创建任务的工作流,将自身判为同类活动任务。新增真实应用登录与路由装配回归,修复前稳定返回 502。
- [x] 已完成,等待复审:拆分普通部署与已有任务执行回调,普通部署继续创建任务,重试只执行原始证书部署流程,保留权限、审计、尝试记录与事务关联。
- [x] 已完成,等待复审:定向回归验证唯一父任务、失败后成功的两次尝试、原任务事务关联、并发重试 409、成功后重复重试 409;后端 go test ./... 与 go vet ./... 通过。
- [~] 进行中:竞态测试、干净提交构建、双机升级和真实失败任务重试复核。当前尚未取得修复后的实机成功证据。
- [!] 暂缓或存在阻塞:历史恢复事务、Smail SMTP、数据库生产矩阵、备份恢复与最终清库首次部署门禁仍未闭环,不宣称生产验收通过,不给缺乏依据的完成百分比。
现场失败任务标识:683ef23a-c912-4f46-b2a6-3ec11908808e。本轮保留其数据用于复现与修复验证,不属于清库首次流程;没有直接改库伪造状态,也没有创建辅助验收脚本。
本轮补充回归:应用装配测试同时核对双服务器结果归属于第二次尝试,以及开始和成功审计的操作者关联;缺少已有任务执行器时返回 501,不回退到创建任务入口,不改变原任务状态。后端全量测试与 vet 再次通过;app/httpapi/repository 竞态测试通过,补充用例的定向竞态测试通过;前端 241 项通过。实机复测尚待新包部署。
### 部署重试双机实测复核结果(2026-10-01 17:02,北京时间)
以下为本轮最新结果,更新前述“尚待实机复测”的状态;只关闭部署重试自冲突这一子项,不等同于完成全部生产门禁。
- [✓] 复审通过:修复提交 302111c8affd6c31c6d616eed45a2a8cee42843c 已推送,发布清单 source_dirty=false;使用正式构建流程生成 Linux 包,并通过官方安装器升级两台测试服务器,安装及启动退出码均为 0。
- [✓] 复审通过:发布包 build/release-20261001-deploy-retry/Scert-20261001-V0.1.0-linux-amd64.tar.gz,SHA-256 为 0ad94de03e625eb5fd6789e1fa84f8cad8298e2487876f55d34e36f14df7c878;双机上传和包内 SHA256SUMS 校验均通过。
- [✓] 复审通过:两台 /scert/scert 的 SHA-256 均为 393ae5a1cfaf8e4442ec30e47950f020e6ab47adb47af7fdca45a36eb9486ed2;Scert 与 Nginx active,NRestarts=0。
- [✓] 复审通过:复用真实失败任务 683ef23a-c912-4f46-b2a6-3ec11908808e,通过正式重试接口同时提交两次请求,一个返回 200、双服务器部署成功并通过 HTTPS 校验;另一个返回 409,没有执行第二次部署。成功后再重试返回 409。
- [✓] 复审通过:原任务保留,未产生嵌套父任务;尝试记录只从 3 增加到 4,第 4 次为 success,两台部署结果均关联该次尝试 b9ca3a51-30af-4d2c-9655-758509140f5e。旧失败记录保留用于追溯。
- [✓] 复审通过:只读核对数据库,首次失败的两个事务均为 rolled_back;本次两个事务 20261001-085936.117414575-5eecfbb86709、20261001-085940.320149826-09d1cf34f629 均为 committed;开始及成功审计均关联原任务和实际操作者。没有直接修改数据库状态。
- [✓] 复审通过:两台证书目录均无残留锁及 scert-tmp 文件;nginx -t 通过,证书和证书链文件哈希一致。第一台通过带 SNI 和主机名校验的 TLS 连接核对第二台站点证书指纹,仍与部署前同一正式证书一致。
- [✓] 复审通过:升级后 Scert 仍仅监听 127.0.0.1:8443,Nginx 通过 80/443 提供 Web 服务;管理入口前端与 readyz 返回 200,第二台回环上游仍使用系统 CA 严格校验。没有开放额外公网业务端口,未关闭 TLS 或 SSH 主机校验。
- [!] 仍需处理:历史事务 20260930-231310.231207974-5bf9d1e37542、20260930-231313.144123222-ce71b1dfe14e 的恢复证据字段为 null,服务保留 recovery_required 并拒绝自动接管;本轮没有伪造历史证据或强改终态。
- [ ] 未开始:本轮修复后的最终清库首次部署验收。Smail SMTP、数据库矩阵及备份恢复等其余上线门禁仍需按原任务清单验证。
本轮运维使用密码 SSH,没有新建辅助验收脚本;没有额外制作远端数据备份。安装器自行保留升级前发布文件,未擅自删除。以上域名、服务器及凭据仅用于测试,不作为产品验收规则。
## 真实进程崩溃恢复与原任务重试(2026-10-01 17:15,北京时间)
### 范围与操作边界
沿用已部署的源码提交 302111c8affd6c31c6d616eed45a2a8cee42843c 及正式候选包,本轮没有修改产品代码或重新构建。双机二进制版本沿用上一节证据,第二台再次核对 SHA-256 为 393ae5a1cfaf8e4442ec30e47950f020e6ab47adb47af7fdca45a36eb9486ed2。本轮保留数据连续复测,不代表最终清库首次流程。
使用已有正式证书重部署,未重新签发;测试域名 scert-multi-20260930.apmmk.cn。人工操作使用密码 SSH,产品部署仍使用其托管密钥并校验主机指纹。未创建辅助脚本、未直接写库、未手工删锁、未额外备份远端数据。
### 崩溃注入及自动恢复
- 正式域名部署接口创建任务 72f4222a-3706-419c-847c-0fcb55b436f2。为得到可控中断窗口,通过正式服务器 API 将第一台重载命令临时设置为 nginx 重载后延迟 18 秒,校验命令始终为 nginx -t。
- 只读确认两个事务均为 reload_attempted/pending 后,于 UTC 09:07:09 对第二台 Scert.service 主进程发送 SIGKILL;未停止 Nginx。
- 原进程 PID 112721,systemd 自动启动新进程 PID 113012,NRestarts=1。
- UTC 09:07:29 第二台事务已回滚,第一台仍恢复中;此时管理 readyz 暂时返回 502,第一台业务站点本机严格 HTTPS 仍为 200。此管理短暂不可用如实保留,不宣称零中断。
- UTC 09:07:42 两条事务均 rolled_back,消息为“服务启动已完成中断部署事务回滚”;原任务为 failed/service_restart,管理 readyz 已恢复 ready。
- 第一次尝试 a680e853-2d2b-40c6-a68f-2dea91b36a79 保留 failed。对应事务:20261001-090648.928412564-d8bedbb5d1d1、20261001-090653.896206069-a567d37db475,均为 rolled_back。
- 浏览器中原同步请求最终返回 Nginx 502,符合主进程被杀后的连接中断;没有将该响应伪报为成功。
### 配置恢复和正式重试
- 重新登录后,通过正式服务器 API 将第一台 reload_command 恢复为 ["nginx","-s","reload"],PUT 返回 200,随后 GET 再次核对一致。
- 正式接口 POST /api/v1/tasks/72f4222a-3706-419c-847c-0fcb55b436f2/retry 返回 200,双机 total=2、success=2、failed=0,verification_status=success;结束时间 UTC 09:12:11。
- 原任务更新为 success,第二次尝试 e52b6717-46b8-4072-9931-eafc7f8fa817 为 success;双机部署结果均关联此次尝试,没有新建嵌套父任务。
- 第二次尝试的事务 20261001-091153.272958781-00baba19c6a9、20261001-091159.467779056-dc1f13c75a80 均为 committed。
- 只读审计依次存在 certificate_deploy/running、task_recovery/failed、certificate_deploy_retry/running、certificate_deploy_retry/success,均关联原任务和 primary-super-admin 操作者。
- 成功后再调用正式重试接口返回 409。
### 文件、服务与网络复核
- 双机 nginx -t 成功,证书目录使用不限制文件类型的 find 检查,无名称包含 lock 或 scert-tmp 的残留。
- 恢复后双机 cert.pem 和 fullchain.pem 的 SHA-256 均为 ecf815ca6ba91abb43985663f7ff2df4ee7987362131e068b6ff32e4657f90bc。
- 双机本机 SNI 严格 HTTPS 请求均为 200;第一台访问第二台公网严格 HTTPS 为 200。第一台本机和第二台公网证书指纹均为 43:06:3C:07:26:D3:43:62:B2:54:CE:28:B6:6D:7E:B6:37:E0:0B:16:5F:68:EC:0D:07:FC:ED:0D:F2:C6:4A:D2。
- 双机 Scert 均仅监听 127.0.0.1:8443,Nginx 对外监听 80/443;第二台管理 HTTP 返回 301,跳转 HTTPS,readyz 正常。没有开放额外公网业务端口。
- 单独发现的公网限制:第二台多次访问第一台公网测试域名 HTTPS 均为连接重置,本地 Windows 客户端同样握手失败。第一台抓包看到 TCP 建连后入站 RST;公网 HTTP 返回 Server: Beaver、403,正文标题为 Non-compliance ICP Filing,并指向阿里云拦截页面。第一台本机同域名 HTTPS 仍为 200;不把本机成功代替公网通过,也不通过开放其他端口或关闭证书校验绕过此限制。
- 无 SNI 的公网握手仅用于定位网络差异,返回其他默认站点证书;该诊断不计入测试域名 TLS 验收,未修改产品校验策略。
### 结论
- [✓] 该崩溃窗口的可信事务自动回滚、原任务正式重试和重试后状态关联复审通过。
- [!] 第一台该测试域名公网 HTTP 有明确拦截页面,公网 HTTPS 失败,公网访问验收不通过;第二台管理及测试站点正常。
- [~] 仅同证书重部署,其他负向窗口、新旧证书切换和历史证据缺失事务尚未全部闭环。
- [ ] 最终清库首次流程及其他原清单生产门禁未完成;不宣称整体生产部署验收通过。
## 数据库实机矩阵与新阻塞(2026-10-01 17:45,北京时间)
### 范围与环境
- 沿用第二台 Linux 测试机,正式运行主程序仍为 302111c 对应产物,SHA-256 为 393ae5a1cfaf8e4442ec30e47950f020e6ab47adb47af7fdca45a36eb9486ed2。
- 测试使用项目原有 Go 集成测试及正式 CLI,不使用新建辅助脚本。非 root 测试进程由 systemd 临时单元以 mysql 用户启动,隔离数据库仅监听回环地址。
- 测试根目录 /var/tmp/scert-db-matrix-20261001-1720;MySQL 数据根目录 /var/lib/mysql-files/scert-20261001。保留 AppArmor 策略,不放宽 /scert 权限。
- 未重建或清空业务库,未额外备份业务数据;当前不是最终清库首次部署验收。
### 已结束结果
| 测试 | 单元或日志 | 结果 |
|---|---|---|
| 原 MySQL 数据库矩阵 | scert-mysql-matrix-20261001-1722 / mysql-database.log | 退出 1;模式 15 中断后 1、2 条语句及模式 16、17 建表后中断,共四个边界因表已存在失败 |
| MySQL 8.0.46 仓储 | scert-mysql-repository-20261001-1732 / mysql-repository.log | 退出 0;9 个主测试、92 个子场景通过,无失败或跳过 |
| PostgreSQL 14.24 首次尝试 | scert-postgres-matrix-20261001-1723 / postgres-cli.log | 七场景均因备份工具版本不满足要求失败;不计通过 |
| PostgreSQL 16.15 | scert-postgres16-matrix-20261001-1727 / postgres16-cli.log | 退出 0;七场景全部通过,无跳过;总耗时 14.52 秒 |
| SQLite 旧断言 | scert-sqlite-matrix-20261001-1726 / sqlite-cli.log | 失败,测试将当前迁移版本写死为 14,实际已为 18 |
| SQLite 修正断言及历史样本 | scert-sqlite-history-20261001-1740 / sqlite-history-cli.log | 退出 0;historical_backup、upgraded_backup 全部通过;总耗时 0.79 秒 |
PostgreSQL 的 success、certificate、permission、inventory、authentication、tls 使用独立正式 CLI;cancel 使用进程内确定性取消和真实工具回退,仅据实记录该范围。16.15 工具位于 /var/tmp/scert-pg16-tools-20261001/extracted,包来自经官方公钥签名验证的独立 PGDG 索引。测试后无残留 PostgreSQL 服务进程。
SQLite 新测试程序 SHA-256:4c81656fae63a2eebad999d58e6a79e63e6f441810e02ce29b2a9ce3706ae7f6;上传后核对一致。新增样本覆盖 task_idempotency、task_attempts、task_deployment_results、deployment_transactions 及 recovery_evidence_json;恢复前实际修改样本,恢复后全表快照核验。
### 当前修复与边界
- [~] 原 MySQL 15~17 迁移使用非事务建表,已落盘后再次执行报表已存在。不能简单忽略冲突:模式 15 的历史幂等回填还需避免重复写入,同时拒绝内容不一致的数据。
- [~] 正在实测可重入建表、字段和索引校验、回填与版本标记同事务提交,以及部分回填、冲突数据、异常结构负向场景。待后续结果更新,不能根据运行中的单元 Result=success 提前判定通过。
- [✓] 双机端口复核符合 Nginx 80/443 反代要求;Scert 与 MySQL 均未新增公网监听。
- [!] 第一台测试域名公网平台拦截、无可信证据的历史事务及此前剩余生产门禁继续保留,不以数据库子项通过替代。
## MySQL 中断修复、正式构建与双机升级复核(2026-10-01 18:02,北京时间)
### 修复和实库证据
- 修复提交:c6982ab827038c2cb16bc1432fb393f68a5296e2,已推送。首次推送遇到认证失败,重试成功;没有修改远程认证设置。
- 第一轮修复测试单元 scert-mysql-reentry-20261001-1743 退出 0,106 个子场景通过;此后补充默认空字符串、排序规则和大小写冲突保护,没有用第一轮结果替代最终代码验证。
- 最终单元 scert-mysql-reentry-guards-20261001-1751:inactive、Result=success、ExecMainStatus=0。日志 /var/tmp/scert-db-matrix-20261001-1720/mysql-reentry-guards.log。
- TestMySQLRealDeploymentMigrationRecovery:18 个子场景通过,92.25 秒;覆盖建表前后、部分及完整回填、重复历史任务、不同域名相同键、空键排除、错误任务及时间、孤立映射、大小写冲突、错误列类型或默认值、允许空值、缺失唯一约束、缺失或逆序索引、额外字段、排序规则和非 InnoDB 表。
- TestMySQLRealMigrations:91 个子场景通过,385.84 秒;覆盖 0~18 版升级、各语句中断边界、现有数据、作用域账户、非法迁移历史和重启持久化。总计 109 个子场景,无失败或跳过。
- 最终测试程序 SHA-256:243a42a3826d5e77547346bb95ed56394ca67bab7334568d3202b193a3c31019,上传后核对一致。
### 本地门禁与正式包
- go test -count=1 ./...、go vet ./... 退出 0;go test -race -count=1 ./internal/database ./internal/cli 退出 0;前端 241 项通过、0 失败、0 跳过。未启用的本地外部数据库场景不计实库通过。
- 正式入口 build/build-release.ps1 -Target linux-amd64,包目录 build/release-20261001-mysql-reentry,manifest 标记 source_commit=c6982ab827038c2cb16bc1432fb393f68a5296e2、source_dirty=false。23 项产物大小及哈希核验一致。
- 归档 SHA-256:c075297e9f8f76f4ebb8fa28d43a4099ca853a3619212d5e7f51e78d4b0e9e4d。
- 主程序 SHA-256:39b70a1a11148982baeb699bae749f7e2988a2fba02b001c0da740ba8b86f36d。
- 新正式程序用于 SQLite 再测:scert-sqlite-release-20261001-1754,两个子场景通过,1.34 秒,退出 0。
- 新正式程序用于 PostgreSQL 16.15 再测:scert-postgres16-release-20261001-1754,七场景通过,14.49 秒,退出 0;六场景独立正式 CLI,取消场景为进程内入口,不扩大证明范围。
### 双机升级与健康复核
- 运维通过密码 SSH 连接,保留严格主机指纹检查。发布包上传双机后,归档哈希及包内 21 项 SHA256SUMS 全部通过。
- 发布目录 /var/tmp/scert-release-mysql-reentry-20261001;安装日志 /var/tmp/scert-install-mysql-reentry-20261001.log。
- 两台首次运行安装器时主服务仍在运行,被保护检查拒绝;停止主服务后仍因 Scert-storage.socket 在监听再次被拒绝。随后完整停止 Scert、Scert-admin、Scert-storage.service、Scert-storage.socket,官方安装成功,再启动目录 socket 和主服务,最终退出 0。所有失败记录保留在同一追加日志中;未将拒绝事件计为成功安装。
- Nginx 未停止;管理上游在停服窗口不可用,本次不宣称零停机。安装器按自身事务流程保留发布文件,没有额外制作业务数据备份,也没有手工删除历史文件。
- 两台 /scert/scert 哈希均与上述新正式包一致;第一台 PID 2091132,第二台 PID 158279,均 active、NRestarts=0,目录 socket active。
- 双机 nginx -t 成功。第二台管理首页 HTTP 200,readyz 返回 ready,HTTP 入口 301;第一台测试域名本机带 SNI 且严格校验证书的 HTTPS 为 200,不能代替其公网访问通过。
- 双机 Scert 均仍监听 127.0.0.1:8443,MySQL 监听 127.0.0.1:3306;第二台 MySQL X 协议也仅监听 127.0.0.1:33060。Web 仍由 Nginx 80/443 对外,未添加公网规则。
- 只读核查双机迁移版本均为 18,升级前后任务及事务状态计数一致,没有活动任务被中断。第二台两条 recovery_required 历史事务保留,不伪造证据,不强改状态。
### 下一项正在运行
- 单元 scert-mysql-cli-20261001-1801,日志 mysql-cli-release.log,运行项目现有 TestMySQLReal 维护测试。测试程序 SHA-256:73cd220dfcb898d452ca9de752c2e19b40d5951c693d6fab28e2e6c00c9ea7b9。
- 官方 mysqld、mysql、mysqldump 在系统中位于不同目录;仅在隔离目录 mysql-tools 创建指向原程序的符号链接,以满足夹具同目录要求,没有编写包装或辅助验收脚本,没有修改系统程序。
- 当前运行中,不提前判定通过。该套测试包含进程内正式命令入口及实际工具互操作,不等同于所有独立发行 CLI、完整 Web 和最终生产流程通过。
### 最新接续状态与任务确认(2026-10-01 18:15,北京时间)
本节更新前文历史快照;旧章节中“当前有效”仅指其记录时点,不代表本轮状态。沿用原始四十四项需求,本次仅验收 Linux,Windows 不在本轮范围;不以历史 P0-03 局部状态代表全部生产门禁。
- [x] 已完成,等待复审:MySQL 8.0.46 备份恢复矩阵单元 `scert-mysql-cli-20261001-1801` 已真实结束,退出零;日志 `mysql-cli-release.log` 中二十一个主测试、一百三十六个子场景通过,零失败、零跳过。服务器日志开始时间为北京时间 18:01:26,结束为 18:06:49。
- [x] 已完成,等待复审:本批矩阵覆盖备份快照、官方导入导出互操作、视图、触发器、例程、事件、权限和失败回退。主要调用进程内正式实现,不冒充独立发行程序全量验收。
- [x] 已完成,等待复审:双机服务正常;Scert 仅监听回环 8443,MySQL 仅监听回环 3306;第二台的 33060 同样仅监听回环。Web 对外使用 Nginx 80/443,不新增公网业务端口,不修改第一台现有邮件端口。
- [!] 暂缓或存在阻塞:只读检查确认双机 Scert 均未启用 SMTP,主机、系统通知用户名、凭据引用均为空;缺少可用系统通知配置,不能宣称真实发信与邮件密码重置通过,不使用普通邮箱替代。
- [~] 进行中:复用仓库现有 MySQL 隔离实库测试,增加独立正式发行程序调用;保留严格 TLS、凭据加密存储、成功恢复及 SQL/证书失败回退断言,不创建辅助验收脚本,不接触业务数据库。
- [ ] 未开始:将新增测试编译到 Linux,在第二台隔离数据库中验证当前正式发行程序,再记录实际通过或失败结果。
- [!] 暂缓或存在阻塞:MySQL 5.7、完整三库 Web 流程、Linux 原生构建、其余部署负向场景、历史事务核查、系统发信与最终清库首次部署等仍未闭环;整体生产验收未放行。
### MySQL 独立发行程序验收结果(2026-10-01 18:22,北京时间)
- [x] 已完成,等待复审:新增仓库测试 `TestMySQLRealReleaseBackupRestoreCommands`,复用现有成功、SQL 失败、证书恢复失败场景;通过独立进程调用正式发行程序,不再依赖测试进程的 TLS 注册表。未创建辅助验收脚本。
- [x] 已完成,等待复审:第二台 Linux 上单元 `scert-mysql-release-cli-20261001-1818` 真实结束,退出零;三个子场景全部通过,耗时 33.85 秒,日志为 `/var/tmp/scert-db-matrix-20261001-1720/mysql-release-cli.log`。
- [x] 已完成,等待复审:验证对象为已部署候选同源的正式发行程序 `/var/tmp/scert-release-mysql-reentry-20261001/scert`,SHA-256 为 `39b70a1a11148982baeb699bae749f7e2988a2fba02b001c0da740ba8b86f36d`;测试程序 SHA-256 为 `88ff0fec3f2ab9a14f89dd52f384a677ecfb450f679b077ec47e83649644eb09`。
- [x] 已完成,等待复审:隔离 MySQL 8.0.46 仅监听回环,使用严格 TLS;只在子进程中信任隔离证书,数据库密码通过加密凭据引用读取,未更改系统信任库。断言覆盖业务数据、审计数据、配置、主密钥、加密凭据、证书及失败后恢复前快照。
- [x] 已完成,等待复审:本地后端全量测试、全量静态检查、CLI 竞态测试和差异检查通过。本地条件跳过不计实库通过;本批只修改测试,没有更换线上程序。
- [~] 进行中:准备在隔离目录使用固定工具链执行 Linux 原生正式构建,避免将 Windows 交叉编译包冒充 Linux 原生证据;不新增公网监听,不更改生产工具链或业务配置。
- [!] 暂缓或存在阻塞:上述三个独立 CLI 场景不代表全部数据库、Web 或生产流程验收通过;既有 SMTP 配置缺失、历史事务核查及其他未完成门禁继续保留。
### Linux 原生包双机升级与 Nginx 端口门禁(2026-10-01 19:27,北京时间)
- 构建单元:`scert-linux-native-release-20261001-1852`,结果为 success;原生工具链为 Go 1.26.6、Node.js 24.19.0、npm 11.17.0,目标为 `linux/amd64`。
- 发布清单:源码提交 `be6e2059f4ce949f7e1b6c0dacb3ff0bdea9ea52`,`source_dirty=false`;归档 `Scert-20261001-V0.1.0-linux-amd64.tar.gz` SHA-256 为 `08ba7769bf9225b88ea84f104720fb166b583a494737627523179c0527e83130`。
- 第二台 `45.136.13.241`:使用官方安装器完成停服升级后启动 `Scert.service` 和 `Scert-storage.socket`;Nginx 未停止;`https://odacm.cn/readyz` 返回 ready,首页和 HTTP 到 HTTPS 跳转正常。
- 第一台 `8.137.23.95`:使用同一归档和官方安装器完成停服升级后启动 `Scert.service` 和 `Scert-storage.socket`;Nginx 未停止;既有站点配置和证书保持不变。
- 端口检查:双机 Scert 仅绑定回环 `8443`;MySQL 仅绑定回环 `3306`;第二台 `33060` 仅绑定回环。绕过本机代理直连公网时,三个内部端口均超时;对外项目入口仅为 Nginx `80/443`。未关闭或修改第一台现有邮件业务端口。
- 结果:双机原生包部署和 Nginx 反代端口门禁完成,等待复审;不代表整体生产部署验收通过。SMTP、密码重置、完整 Web 流程、历史事务、DNS/ACME 和最终清库首次部署仍未闭环。
## 2026-10-01 SSH 私钥登录能力安全复核
当前总体状态仍为:**P0-03:完成,等待复审**。本轮安全整改不代表生产部署整体验收通过。
- [x] 后端服务器模型、SSH 连接工厂、服务器 API 和服务器仓储已收敛为密码认证,未保留 SSH 登录私钥字段或部署路径。
- [x] 新增数据库迁移 19,清理历史服务器记录中的 SSH 私钥引用和 SSH 密钥状态;历史表结构迁移文件仅用于兼容既有数据库,不代表当前能力重新启用。
- [x] 敏感信息存储测试已改为 DNS 凭据场景,不再创建或读取 `secret://ssh/key-*` 测试引用。
- [x] TLS 证书业务的 `private_key_path`、证书上传、部署和回滚逻辑保持不变;该字段只用于证书私钥,不用于 SSH 登录。
- [x] `go test ./...`、三个 Go 命令构建和 `git diff --check` 已通过。
- [!] 仍未完成清库后的三数据库 Web 首次初始化、Smail 系统账户真实发信、验证码重置闭环、P0-10 负向矩阵、双机失败重试和最终生产门禁,因此不得标记为整体验收通过。
## 2026-10-01 SSH 密码认证候选包实机复核补充
### 本轮范围
本轮只验证安全整改后的正式 Linux amd64 候选包在 `45.136.13.241` 的安装和入口安全边界。使用密码 SSH,未使用密钥,未创建辅助验收脚本;未清空业务数据库,未执行真实发信或密码重置流程。
### 结果
- 候选包 `Scert-20261002-V0.1.0-linux-amd64.tar.gz` 的安装包清单校验通过。
- 安装器在发现 `Scert-storage.socket` 仍监听时拒绝覆盖;停止相关服务后重新安装成功,未强制覆盖运行中的存储服务。
- `Scert`、`Scert-storage.socket`、Nginx 均为 `active`,`nginx -t` 通过。
- Scert 监听 `127.0.0.1:8443`,MySQL 监听 `127.0.0.1:3306` 和 `127.0.0.1:33060`,公网服务入口仍为 Nginx `80/443`;既有邮件服务端口未在本轮修改。
- SSH 服务端复核为 `pubkeyauthentication no`、`passwordauthentication yes`、`authenticationmethods password`,满足密码唯一认证要求。
- 从本机按 `odacm.cn` SNI 严格访问 `https://odacm.cn/readyz` 返回 `{"service":"scert","status":"ready"}`;从服务器回环按同一域名严格访问也返回 ready;没有使用 `-k` 或关闭 TLS 校验。
- HTTP `odacm.cn` 返回 `301` 并跳转到 `https://odacm.cn/`。
### 尚未完成的门禁
本轮证据不能替代 SQLite、MySQL、PostgreSQL 清库后的 Web 首次初始化、Smail 系统账户真实 SMTP 发信、超级管理员验证码重置密码、修改密码后重新登录、P0-10 负向矩阵、双服务器失败重试与历史 `recovery_required` 证据核查。当前总体状态仍为 **P0-03:完成,等待复审**,不能标记为生产整体验收通过。
### 服务重启后的恢复窗口复核
在 `45.136.13.241` 使用密码 SSH 执行 `systemctl restart Scert`。重启后 `Scert`、`Scert-storage.socket`、Nginx 均保持 `active`;约等待 8 秒后,回环和 `odacm.cn` 的严格 HTTPS `/readyz` 均返回 `{"service":"scert","status":"ready"}`。
重启后的首个约 2 秒请求返回 HTTP `502`,随后 Scert 完成 `127.0.0.1:8443` HTTPS 监听并恢复正常。该短暂恢复窗口已记录为生产门禁观察项,不能宣称无感重启;本轮没有开放内部端口、关闭 TLS 校验或修改 Nginx 为不安全转发。
同次启动日志继续报告两条历史 `recovery_required` 事务因缺少可信恢复证据而拒绝自动接管。系统保留现场等待人工核查,本轮未删除锁、伪造恢复证据或修改历史事务。
## 2026-10-01 双机密码认证候选包部署实测
### 测试范围
- 目标服务器:`8.137.23.95`(`ikun1`)和 `45.136.13.241`(`yc`)。
- 部署方式:仅使用 SSH 密码认证和官方 Linux 安装器;未使用 SSH 私钥、公钥、`authorized_keys` 或辅助验收脚本。
- 候选包:`Scert-20261002-V0.1.0-linux-amd64.tar.gz`。
- 包 SHA-256:`4090D6D614C359A4A6C7FF92634189AA136099324DD53B2D4557038954DCD33E`。
### 实测结果
- [x] 两台均完成归档校验和官方安装器停服升级,升级前发布文件由安装器保留;未额外制作远端备份。
- [x] 两台 `Scert`、`Scert-storage.socket`、Nginx 均为 active,`nginx -t` 通过。
- [x] 两台 Scert 均仅监听 `127.0.0.1:8443`,数据库仅监听回环地址;公网项目入口仍为 Nginx `80/443`。
- [x] 两台 SSH 服务端均为 `pubkeyauthentication no`、`passwordauthentication yes`、`authenticationmethods password`。
- [x] 第二台 `https://odacm.cn/readyz` 在正常 TLS 校验下返回 `{"service":"scert","status":"ready"}`。
- [!] 第一台不是 `odacm.cn` 当前证书域名,使用该域名探测发生证书名称不匹配;该结果不作为第一台公网域名验收通过依据。
- [!] 第二台启动日志仍报告两条历史 `recovery_required` 事务缺少可信恢复证据;本轮保留现场,未删除锁、伪造证据或自动接管。
- [!] 第一台 `php-fpm-82.service` 的既有 failed 状态与 Scert 无关,单独记录,不作为 Scert 服务失败依据。
### 结论
本轮通过了双机候选包安装、密码唯一 SSH、Scert 回环监听、数据库回环绑定和 Nginx 入口边界复核;没有通过生产整体验收。清库后的三数据库 Web 首次初始化、Smail 系统账户真实发信、密码重置完整闭环、P0-10 负向矩阵、历史恢复证据核查以及重启瞬间短暂 502 观察项仍需继续处理。当前总体状态保持 **P0-03:完成,等待复审**。
## 2026-10-01 当前日期候选包部署和安全边界复核
### 构建与部署
- 候选包:`Scert-20261001-V0.1.0-linux-amd64.tar.gz`。
- SHA-256:`56426C1BBCECA68B32D122E813A950D2EE345349F89C3178080BD313F6F77E8E`。
- 来源提交:`79bae3bf2b52720591279f78f5cbc340f82c4f36`,`source_dirty=false`。
- 双机均通过远端归档校验、包内 `SHA256SUMS` 和官方安装器升级;未使用 SSH 私钥、公钥、`authorized_keys` 或辅助验收脚本。
- 双机运行中的 `/scert/scert` 摘要均为 `3422abf0a5fb2c0254fb382118424733534fd00548eba4930a0e90857cd874b8`;`/scert/scert-admin` 摘要均为 `ee52d429a88b3942147e1acf67da3653f61e076c11d67bf3dd20b254b4321722`。
### 安全回归
- [x] `TestSSHKeyAPIIsUnavailable` 通过,`GET /api/v1/ssh-keys` 返回 404。
- [x] 前端 241 项测试全部通过;测试确认服务器认证提示仅显示密码配置状态,SSH 私钥入口未挂载。
- [x] 两台 SSH 服务端均为 `pubkeyauthentication no`、`passwordauthentication yes`、`authenticationmethods password`。
- [x] 两台 Scert 仅监听 `127.0.0.1:8443`,数据库仅监听回环地址,Nginx 仅承担公网 `80/443` 入口。
### 当前候选包重启复核
在第二台对当前日期候选包执行 `systemctl restart Scert`,第 0、1 秒通过 Nginx 访问 `https://odacm.cn/readyz` 返回 502,第 2 秒恢复 200;随后服务保持 active,Scert 仍为回环监听,严格 TLS 校验没有关闭。该短暂恢复窗口继续作为生产门禁阻塞项记录。
### 结论
当前日期候选包部署和安全边界复核完成,但生产整体验收仍未通过。历史 `recovery_required` 事务、清库后的三数据库 Web 流程、Smail 系统账户真实发信、密码重置闭环和 P0-10 负向矩阵仍需继续处理。总体状态保持 **P0-03:完成,等待复审**。
## 2026年10月1日 Linux上线加速复核(当前执行记录)
本节是本轮目标的当前状态入口,只记录非敏感证据,不记录服务器口令、RAM 密钥、数据库密码、授权码、令牌或其他认证信息。Windows 环境不在本轮范围;公网邮件服务端口属于既有 Smail 服务,不作为 Scert 管理入口处理。
### 本轮需求清单
- [x] 仅验证 Linux 双机:第一台 `8.137.23.95`,第二台 `45.136.13.241`。
- [x] Scert 服务器连接只使用 SSH 密码认证,不使用或新增 SSH 私钥、公钥、`authorized_keys` 和密钥登录流程。
- [x] Scert 公网入口只经 Nginx `80/443`;Scert `8443` 和数据库端口保持回环监听。
- [x] 不创建辅助验收脚本,不把辅助脚本作为验收依据。
- [~] 继续完成 P0-10 负向恢复矩阵和历史 `recovery_required` 事务的可信证据核查。
- [~] 继续完成 SQLite、MySQL、PostgreSQL 三数据库的清库后完整 Web 首次流程。
- [!] Smail 系统通知 SMTP 当前未启用且未配置系统账户凭据,真实发信、验证码密码重置和改密登录闭环暂不能完成;不得猜测 SMTP 主机、账号或授权码。
- [~] 完成最终 Linux 候选包部署、浏览器缓存清理和生产门禁复审。
- [!] Windows 环境明确排除,不纳入本轮上线验收。
### 当前任务状态
- [✓] T0:双机 Scert、存储通道、Nginx 均为 active;Scert 仅监听 `127.0.0.1:8443`,数据库仅监听回环地址,公网入口为 Nginx `80/443`。
- [x] T1:MySQL 5.7 隔离实例和正式发行程序 CLI 实库测试已有通过证据,等待整体验收复审;不能替代 Web 首次流程。
- [~] T2:P0-10 剩余负向窗口、锁归属不匹配、文件摘要不匹配和历史恢复证据仍在核查。
- [~] T3:三数据库完整 Web 流程尚未取得全部直接实机证据。
- [!] T4:Smail 系统账户发信凭据尚未配置,密码重置邮件和新密码登录闭环阻塞。
- [~] T5:候选包已在双机运行并通过健康检查;最终清库后的首次流程、缓存清理和统一门禁复审仍未完成。
- [!] T6:Windows 不在本轮范围。
### 本轮直接证据
- [✓] 两台服务器均通过密码 SSH 复核:Scert、存储 socket、Nginx active。
- [✓] 两台服务器均保持 Scert `127.0.0.1:8443`、MySQL 回环监听;`odacm.cn` 的 `/readyz` 返回 200 ready,HTTP 入口跳转 HTTPS。
- [✓] 生产页面 DOM 未出现 SSH 私钥、公钥或 `authorized_keys`,服务器配置只提交密码和主机指纹;`/api/v1/ssh-keys` 返回 404。
- [✓] 本地 Go 全量测试、前端 241 项测试和 Vite 生产构建通过;工作区无未提交代码修改。
- [!] 当前 `/api/v1/settings` 显示通知 SMTP 未启用且系统通知凭据未配置;该项需要从 Smail 系统通知页面取得真实授权配置后才能继续。
### 当前总体验收结论
总体仍为 **P0-03:完成,等待复审**,不能因为 T0、T1 和 SSH 安全整改通过而宣布生产验收通过。上线前必须补齐 T2、T3、T4、T5 的直接证据;其中 T4 受 Smail 系统账户授权配置阻塞。
## 2026年10月2日最新候选包实机部署记录
本节只记录非敏感结果,不记录口令、RAM 密钥、数据库密码、令牌、私钥、公钥或 `authorized_keys`。本轮按照生产安装器执行,未创建辅助验收脚本,未开放额外外部端口。
### 构建与部署
- [x] 最新候选包来源提交 `cc3ac06`,构建目录标记 `source_dirty=false`;归档 `Scert-20261002-V0.1.0-linux-amd64.tar.gz` SHA-256:`e4623636c5021a99998ce04f4680ebd3d107c02fb4a118a17875fc84c7faeefa`。
- [x] 双机均通过密码 SSH 上传和正式安装器部署,未使用密钥登录;安装后程序 SHA-256 均为 `f6fcad3a8d6d0cd47e90e0e0cc6b2ff9c9657082a63835aae0e1d72ab3500fb9`。
- [x] 两台 Scert、存储 socket、Nginx active,Nginx `-t` 通过;Scert 监听回环 8443,数据库监听回环 3306/33060,公网入口保持 Nginx 80/443。
### 入口与安全复核
- [x] 第二台 `odacm.cn` 严格 HTTPS `/readyz` 返回 200 ready,HTTP 请求跳转 HTTPS。
- [x] 第一台使用 `odacm.cn` 严格 TLS 校验时证书 SAN 不匹配,客户端拒绝连接;未关闭 TLS 校验,需更换为包含目标域名的正式证书后才能放行。
- [x] 第一台跨机探测第二台内部端口 8443、3306、33060 均超时;本机代理网卡导致的 TCP 探测结果不采纳。
- [x] `/api/v1/ssh-keys` 返回 404,生产 Web 包不存在 SSH 密钥管理入口;本轮未生成、读取、上传或部署 SSH 密钥材料。
- [x] 启动后早期出现短暂 502,等待服务就绪后恢复 200;该项仍保留为生产门禁观察项。
### 质量门禁
- [x] 后端全量 Go 测试和 `go vet` 通过。
- [x] 前端 241 项测试通过。
- [~] P0-10、三数据库清库后的 Web 流程、Smail 系统账户发信、验证码密码重置和最终清库首次验收仍未完成。
总体生产部署验收仍未放行,当前状态保持 **P0-03:完成,等待复审**。
## 2026年10月2日 SSH 登录密钥入口移除复核
本轮按安全要求收紧产品边界:SSH 登录只允许密码认证,不提供私钥、公钥、密钥生成、授权文件写入或密钥登录管理能力。HTTPS 证书部署所需的证书私钥路径仍保留,二者用途不同。
- [x] 删除未注册的 SSH 密钥 CLI 入口文件,正式 CLI 不再包含密钥管理命令。
- [x] 删除未注册的 SSH 密钥 HTTP 处理文件,生产路由继续不注册相关接口,`/api/v1/ssh-keys` 保持 404。
- [x] 删除未引用的前端密钥弹窗文件和遗留样式,生产页面不提供密钥管理入口、命令或授权文件提示。
- [x] 保留服务器主机指纹校验、SSH 密码字段和 HTTPS 证书私钥路径;没有把证书私钥误当作 SSH 登录凭据。
- [x] 后端 `go test ./...` 全部通过;前端 241 项测试全部通过;Vite 生产构建通过;`git diff --check` 通过。
- [~] 需要将最新候选包按正式安装流程部署到双机,并复核页面和 API 后再进入最终生产验收。
本轮不删除历史数据库迁移文件和禁用兼容测试;它们只用于既有数据库升级和安全回归,不会启用 SSH 密钥能力。总体生产验收仍未通过,既有证书 SAN、启动早期 502、Smail 系统账户发信和三数据库完整 Web 流程仍是门禁项。
## 2026年10月2日 SSH 登录凭据边界复审补充
本次仅记录非敏感结果。部署和验证继续使用 SSH 密码认证,不使用、不生成、不上传 SSH 私钥或公钥,不写入远程授权文件,也不创建辅助验收脚本。HTTPS 证书私钥只用于 TLS 证书部署。
- [x] SSH 客户端新增连接前密码认证硬校验,非密码认证不能进入连接流程。
- [x] 前端部署页面和 CLI 帮助文字明确区分服务器指纹、SSH 密码与 HTTPS 证书私钥。
- [x] 未新增公网端口,Scert 仍只经 Nginx 80/443 反代,服务自身保持回环监听。
- [~] 生产整体验收仍受第二台正式域名证书、短暂启动 502、三数据库 Web 首次流程、Smail 发信和密码重置闭环阻塞。
当前总体状态保持 **P0-03:完成,等待复审**。
# 2026年10月2日12:35 回环监听安全复核
本节为只读现场核查及代码整改记录,不是清库首次生产验收;没有重启线上服务、修改 Nginx、开放端口或触碰其他业务。
- [x] 第一台与第二台实际监听均为 Scert 127.0.0.1:8443、MySQL 127.0.0.1:3306 和 127.0.0.1:33060;第一台到第二台上述三个端口的四秒限时 TCP 探测均未连接成功。
- [x] 第二台 Nginx 配置检查成功,管理上游为回环 HTTPS,开启 proxy_ssl_verify、SNI 和系统 CA 校验。
- [x] 发现两台磁盘配置仍保存通配地址,但 systemd 的 SCERT_SERVER_HOST 将实际监听覆盖为回环地址。新增校验必须针对环境覆盖后的有效配置,不误伤当前安全运行方式。
- [~] 补齐程序独立启动防护:Linux 默认及空值回退改为回环地址,CLI 配置入口、初始化入口、正式应用入口在建立资源前拒绝非回环监听;新增 Linux 回归用例待执行。
- [!] 12:26 正式构建因 GOFLAGS 覆盖被拒绝;12:30 重试移除覆盖后,12:31 被 install-linux.sh 的发布路径扫描拦截。两次均判失败,不以进程启动时的 Result=success 冒充构建成功,不绕过路径检查。
- [ ] 最新代码正式候选包、双机升级及严格 HTTPS 复测待完成。