From e5014b3a98368dbd306f2e3f3599f2fcee1b3978 Mon Sep 17 00:00:00 2001 From: xuans Date: Tue, 15 Sep 2026 20:46:18 +0800 Subject: [PATCH] =?UTF-8?q?=E8=AE=B0=E5=BD=95=E5=8F=91=E5=B8=83=E5=8A=A0?= =?UTF-8?q?=E5=9B=BA=E4=B8=8EUbuntu=E5=AE=9E=E6=9C=BA=E9=AA=8C=E6=94=B6?= =?UTF-8?q?=E7=BB=93=E6=9E=9C=E5=8F=8A=E7=94=9F=E4=BA=A7=E9=98=BB=E5=A1=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/Scert开发任务清单.md | 2 + doc/发布加固与实机验收记录.md | 162 +++++++++++++++++++++++ doc/生产部署与运维说明.md | 2 + 3 files changed, 166 insertions(+) create mode 100644 doc/发布加固与实机验收记录.md diff --git a/doc/Scert开发任务清单.md b/doc/Scert开发任务清单.md index 59d57a6..1be8ccb 100644 --- a/doc/Scert开发任务清单.md +++ b/doc/Scert开发任务清单.md @@ -1,5 +1,7 @@ # Scert 开发任务清单 +> 2026-09-15 最新进展:见 [发布加固与实机验收记录](发布加固与实机验收记录.md)。初始化事务、远程部署锁、回滚与 SSH 安全依赖已加固;Linux 候选已构建并通过隔离实机推送验收。管理端仍未完成初始化,正式 ACME、业务 reload、TLS/SNI 和浏览器全流程继续标记 `[!]`,不是生产放行。 + > 文档用途:作为 Scert 项目后续开发、进度跟踪、阶段复审和验收的执行清单。 > > 项目名称:Scert diff --git a/doc/发布加固与实机验收记录.md b/doc/发布加固与实机验收记录.md new file mode 100644 index 0000000..ca0b35b --- /dev/null +++ b/doc/发布加固与实机验收记录.md @@ -0,0 +1,162 @@ +# 发布加固与实机验收记录 + +日期:2026 年 9 月 15 日,时间均为北京时间。 + +## 一、结论与授权边界 + +**当前是 Linux amd64 发布候选验收,不是正式生产放行。** 本记录是本轮最新状态入口;其他历史验收记录中的日期、主机、缓存和未完成项,不应直接视为当前状态。 + +| 主机 | 用途 | 本轮边界 | +| --- | --- | --- | +| `47.108.78.12` | Scert 管理机 | 独立候选包和验收程序,不覆盖在用配置、数据库或主程序 | +| `8.137.23.95` | 证书推送目标 | 仅使用新建隔离证书目录,不部署 Scert 管理服务 | + +- `pcux.cn` 及其子域的 DNS、证书、私钥、权限、属主和站点配置禁止修改。 +- 不清空业务数据库、不修改 RAM 策略、不执行全局域名枚举。 +- 不重载或重启共享 Nginx,不将模拟命令成功当作真实业务重载验收。 +- 本地既有补丁、二进制和构建目录保留,不混入源码提交。 + +## 二、需求与任务清单 + +- [✓] 保留多用户功能,阿里云权限错误失败关闭,撤权后重新在线验证。 +- [✓] 初始化必须在签发、校验证书和私钥、配置提交成功后标记就绪。 +- [✓] CLI 的已有凭据引用和 `--dns-verified` 不得绕过在线权限检查。 +- [✓] 初始化失败使用独立上下文回滚证书,保护用户原有凭据。 +- [✓] ACME 测试根与正式系统信任池隔离,无独立 issuer 字段也必须验证链。 +- [✓] 远程部署按文件路径加协作锁,锁冲突拒绝,不自动抢锁。 +- [✓] 远程上传独占创建、先收紧权限再写私钥,所有上传完成才替换。 +- [✓] 重命名响应丢失后重连恢复;回滚失败保留本事务锁并报告人工处置。 +- [✓] 发布脚本受版本控制,使用独立目录,不覆盖旧前端产物或旧归档。 +- [✓] SSH 安全依赖升级、全量回归和漏洞复扫。 +- [✓] 最新 Linux 候选包构建、传输校验和隔离实机复验。 +- [✓] 同一候选源码的部署包单元与协议测试在 Ubuntu 管理机通过。 +- [!] 正式 ACME、初始化完成、TLS/SNI、实际业务重载和浏览器完整流程。 + +## 三、代码复审与安全加固 + +### 初始化与证书信任 + +`setup.Apply` 仅接受配置输入,不再直接标记 ready。HTTP 与 CLI 各自完成证书事务后才能提交就绪状态。CLI 初始化使用进程锁,取得锁后再次检查配置是否已存在。 + +新增 CLI 参数 `--acme-environment staging|production`,默认 `staging`。测试证书不被普通浏览器信任,不能用于生产。四个测试根来自 Let's Encrypt 官方仓库,仅用于测试签发结果校验;来源见 `backend/internal/certificate/trust/来源说明.md`。没有向操作系统、浏览器或 ACME HTTP 客户端添加测试根,也没有关闭正式证书链校验。 + +`cert.pem`、`fullchain.pem` 的叶子及私钥必须一致,不能只验证 fullchain 而忽略错误的单独证书文件。 + +### 远程部署事务 + +为每个规范化目标路径创建 `.scert-lock` 目录,按路径排序获取。远程原子建目录可以让遵守该协议的不同任务和进程互斥;不能防止管理员或旧版本直接改文件。 + +无法确认临时上传文件所有权时,不自动删除该路径。备份目标已存在时拒绝覆盖。失败后用独立的 30 秒上下文重连、重新校验 SSH 指纹并回滚。文件回滚和服务运行态恢复是两件事:reload 已尝试后即使文件回滚成功,仍提示运行态需要核查。 + +### 安全依赖 + +本轮 `govulncheck` 检出 `golang.org/x/crypto v0.55.0` 的 `GO-2026-6354`、`GO-2026-6355`,调用路径涉及 `ssh.NewClientConn`。两项均为 SSH 通道死锁拒绝服务问题,按扫描建议升级至 `v0.56.0`。升级后必须重新全量测试、竞态测试并复扫,不能沿用升级前结果。 + +升级后 `govulncheck ./...` 与详细复扫均退出零:可达漏洞为零,已导入包的其他漏洞为零。模块层仍有 `GO-2026-5932`,属于未被本项目导入的弃用 `golang.org/x/crypto/openpgp` 包,无修复版本。本轮没有为消除该提示而删除仍需使用的 SSH 模块,不把“无可达漏洞”扩大为依赖树完全没有提示。 + +## 四、构建与验收证据 + +本轮后端实际构建工具链为 Go `1.26.6`;仓库外默认 `go version` 可能显示不同版本,以模块内工具链及发布清单为准。 + +构建说明见 `build/README.md`。发布清单包含源码提交、源码脏状态、实际工具链、目标平台和 SHA256。仅本轮 Linux 服务包,不包含新的 Windows 或桌面端验收。 + +升级安全依赖后以下验证均通过: + +```text +go test -p 1 -count=1 -timeout=3m ./... +go test -race -p 1 -count=1 -timeout=3m ./... +go vet -p=1 ./... +go mod verify +govulncheck ./... +govulncheck -show verbose ./... +npm ci +npm audit --omit=dev --audit-level=high +``` + +全量后端验证在 Windows 开发机运行。前端全新依赖安装及生产构建通过,依赖审计零漏洞。发布脚本语法检查通过;已有发布目录的拒绝覆盖测试通过,原 `frontend/dist/index.html` 哈希保持不变。 + +20:42 收取 Ubuntu 管理机部署包测试结果:`PASS`,退出码 `0`。测试程序来自下述同一候选源码,执行前取消 `SCERT_ACCEPTANCE_CONFIRM`、`SCERT_ACCEPTANCE_CONFIG` 和 `SCERT_ACCEPTANCE_TARGET`,运行参数为 `-test.v -test.timeout 3m`。本次覆盖部署包的路径校验、证书一致性、上传权限、SSH 指纹与算法选择、锁冲突、超时取消、重连回滚、重命名响应丢失恢复等测试;隔离实机测试因未启用环境变量按预期跳过,其单独实测结果见后文。**这是 Ubuntu 上的部署包测试,不是整个后端的 Linux 全量测试。** + +最终候选来源为独立 Git 克隆,提交 `b40fff703115df33f065ddf1e0ec970b04818760`。发布清单 `source_dirty=false`,程序内嵌 `vcs.revision` 与提交一致、`vcs.modified=false`,`GOOS=linux`、`GOARCH=amd64`、`CGO_ENABLED=0`,SSH 依赖为 `v0.56.0`。 + +本地包: + +```text +build/release-verified-20260915-2040/Scert-20260915-V0.1.0-linux-amd64.tar.gz +``` + +| 产物 | SHA256 | +| --- | --- | +| Linux 归档 | `6e53af49f6fa8db62f8cdbe3ca99d064030bbaa54da0110e84f0f1b45be3dad0` | +| 核心程序 `scert` | `9cf21bde232c0d05d61b2044af5be9c9e739e3aa880a38492315a37d1733a786` | +| 隔离部署测试程序 | `0024836bf883e00ec484bdae66b0bf16f757e64204d6a7f2f158cf55b114df81` | +| 发布清单 `manifest.json` | `e6f8702c94f8e9d139ad72418b0edd7cb9f5ba0fa24a3554895b1dbac8496514` | + +上述归档、测试程序和清单已传输至管理机,全部回读 SHA256 一致。远程最终候选入口: + +```text +/scert/Scert-20260915-V0.1.0-linux-amd64-verified-2040.tar.gz +/scert/release-verified-20260915-2040/ +/scert/scert-release-verified-20260915-2040-manifest.json +/scert/scert-deployment-verified-20260915-2040.test +``` + +20:37 在管理机检查归档成员无越界路径或特殊文件后解压到新目录,`sha256sum -c SHA256SUMS` 全部通过,安装脚本 `bash -n` 通过,`scert`、`scert-admin` 和安装器可执行权限均为 `0755`。实际运行候选 `scert version` 返回 `Scert 0.1.0`,DNS 帮助中文正常。未执行安装器,未替换正在运行的 `/scert/scert`。 + +最新 `TestRemoteDeploymentIsolatedAcceptance` 从管理机发起,向 `8.137.23.95` 新目录 `/etc/ssl/scert-acceptance-20260915-123741.890429938` 推送,**PASS,9.66 秒**: + +| 场景 | 实机结果 | +| --- | --- | +| 首次推送 | 成功 | +| 第二次替换、保留旧备份 | 成功 | +| 第二个 SSH 连接遇到已持有的文件锁 | 拒绝部署,他人锁和当前证书不变 | +| validation 返回非零 | 失败并恢复原文件 | +| reload 返回非零 | 失败并恢复原文件,明确提示运行态未确认 | +| validation 超时 | 实际触发 deadline exceeded,重连回滚成功 | + +全部场景复核 cert/fullchain 为 `0644`、key 及 key 备份为 `0600`,无遗留 `.scert-tmp-*` 或 `.scert-lock`。隔离目录保留 6 个证书及备份文件。 + +20:38 复核目标机 Nginx active,`nginx -t` 通过;受保护的 `/ssl/pcux.cn/fullchain.pem` 和 `/ssl/pcux.cn/privkey.pem` 哈希、权限、UID/GID 与 20:17 基线完全一致。 + +本次使用自签测试证书和 `/usr/bin/true`、`false`、`sleep`,没有真实 DNS/ACME 签发、没有配置业务 Nginx 站点、没有 reload 或 restart 共享服务。**此结果证明 SSH/SFTP 事务与回滚,不证明正式证书、真实业务 reload 或 HTTPS/SNI 已验收。** + +构建过程中发现嵌套 Git worktree 的 Go VCS 识别会读取外层工作区状态,外置 linked worktree 的产物则没有内嵌提交。最终改用独立克隆构建,未伪造 VCS 字段,也没有清理原工作区。此前所有候选包保留但不作为最终候选入口。 + +## 五、远程部署遗留锁与恢复流程 + +1. 停止针对这些目标文件的新任务,核实所有部署进程和管理员操作都已停止。 +2. 记录目标路径、错误消息、事务 `BackupID`、临时路径和备份路径。不要复制私钥内容到工单或日志。 +3. 确认原证书、完整链和私钥属于同一版本,检查内容、域名、有效期、属主及权限;私钥和私钥备份至少保持 `0600`,并确认业务服务的实际读取用户。 +4. 回滚失败或状态不明确时,先从本事务备份恢复并重新校验。不要对整个证书根目录执行递归删除或递归改属主。 +5. 文件恢复不等于运行态恢复。使用目标业务自身的配置验证命令;共享进程的 reload 必须获得对应维护授权。 +6. 只有确认没有在途事务、文件已经一致且运行态处理完成后,管理员才能逐个移除已核实归属的空锁目录。禁止自动清理所有 `.scert-lock`,禁止根据锁目录年龄直接抢锁。 +7. 无法确认创建成功响应的临时文件可能保留,即使本次业务文件已回滚也需要人工核查;不能按名称前缀批量删除。 + +## 六、正式升级与回滚门槛 + +1. 核对发布提交、`source_dirty=false` 和可信通道取得的归档 SHA256;解压到全新目录,再执行 `sha256sum -c SHA256SUMS`。 +2. 确认域名在用户允许范围内,并分别验证当前 RAM 的读取、新增、删除能力。读取成功不代表可写,历史成功不是永久授权。 +3. 确认管理机能以正常 TLS 校验连接正式 ACME 目录;不得借助关闭验证、随意更换 DNS 或修改未授权网络设施绕过。 +4. 维护窗口内停止 Scert,按 `生产部署与运维说明.md` 创建包含配置、数据库、凭据和证书的完整备份,保留旧程序、Web 资源及服务配置。 +5. 使用同一发布包更新核心程序、Web 资源与服务模板,先检查配置和本地健康,再检查 `/readyz`;不能为获取 200 手工伪造 ready 字段。 +6. 初始化、实际 TLS、登录和授权链路通过后才接入真实业务推送。更新共享服务前单独获得维护授权。 +7. 启动或验收失败时停止新版本,恢复经核验的旧程序、Web 资源及对应配置;如涉及数据变化,只使用已验证的完整恢复流程,不能只换二进制赌数据兼容。 + +## 七、当前外部阻塞与待验收项 + +20:19 附近管理机 Production 与 Staging ACME 均能解析,但 TCP 连接 5 秒超时,尚未进入 TLS。实际 Scert 进程的大小写 HTTP/HTTPS/ALL/NO_PROXY 环境变量均未设置。阿里云 API 连接正常不代表 ACME 路径正常;本轮证据不足以定位具体云防火墙或上游网络根因。 + +20:24 只读复核:管理服务 active,配置仍 `setup_ready=false`,监听 `127.0.0.1:9004`,时钟同步正常,根分区约 13% 使用。既有后台域名 `scert-accept-20260914.epwn.uno` 不在本轮允许范围,未对其签发。 + +20:38 再次复核管理服务:`/healthz=200`,`/readyz=503 setup_incomplete`,运行中的主程序仍为 `/scert/scert`,新候选尚未替换在用服务。这是明确的生产准入阻塞,不能用独立候选验收通过覆盖。 + +20:44 收尾只读复核:管理服务仍 active,`/healthz=200`、`/readyz=503 setup_incomplete`。管理机的最终归档、清单和部署测试程序 SHA256 再次与本地一致;本地发布清单的全部 14 项产物哈希也复核通过。正式 ACME 目录连接仍在 5 秒后超时,HTTP 为 `000`、TCP 和 TLS 建连计时均为零,不能宣布出站网络问题已经解决。 + +正式生产验收还需: + +- [!] 确定并配置允许范围内的管理后台域名,完成正式 ACME 签发和初始化。 +- [!] `/readyz` 真正就绪,公网 HTTPS、SNI、证书链和管理后台浏览器流程通过。 +- [!] 真实业务目标的证书替换、服务读取权限、配置校验和受控 reload。 +- [!] 在允许测试写入但 RAM 实际拒绝的候选上验证拒绝路径;不通过写入 `pcux.cn` 代替此项。 +- [!] 当前版本的受控升级、备份恢复和故障回退演练。 +- [!] 如发布 Windows、桌面端或 MySQL 版本,另做相应环境验收。 diff --git a/doc/生产部署与运维说明.md b/doc/生产部署与运维说明.md index 2c42005..2e7fcd7 100644 --- a/doc/生产部署与运维说明.md +++ b/doc/生产部署与运维说明.md @@ -1,5 +1,7 @@ # Scert 生产部署与运维说明 +> 2026-09-15 云主机验收拓扑已更新:`47.108.78.12` 为管理机,`8.137.23.95` 仅为推送测试机。下文局域网地址是历史测试示例,不能用于本轮部署。最新包、实机证据、禁止修改范围及生产阻塞见 [发布加固与实机验收记录](发布加固与实机验收记录.md)。 + ## 1. 文档范围 本文档用于 Scert `0.1.0` 的 Linux 生产部署、升级、备份、恢复、监控和故障处理。当前测试拓扑为: