Scert/doc/生产部署与运维说明.md

444 lines
36 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.

# Scert 生产部署与运维说明
## 部署中断处理与阶段诊断(2026-09-30)
服务重启会将未完成任务和执行尝试标记为失败并记录恢复审计,但这不表示远端文件、部署锁或服务运行态已经恢复。不得仅凭任务失败提示直接重复申请证书或删除远端锁。
处理前应确认没有活动部署和仍在执行的远程命令,核对任务关联服务器、三个目标文件、对应事务旧文件、权限及实际站点证书。只有确认现场归属和恢复方案后才能由管理员处置;保留证据,不抢占无法确认归属的锁。修正配置后优先部署已有证书,避免重复签发。
新候选的部署响应结果增加可选 failure_code,message 为固定中文诊断;任务部署结果查询保留同一安全说明,不要求数据库结构升级。旧记录和未知错误仍返回通用提示。代码、路径和远端输出不从错误文本直接透传;详细原因查受保护日志。新增诊断尚需正式部署复测,本节不代表自动恢复功能已完成。
| 错误码 | 处理含义 |
| --- | --- |
| lock_unavailable | 并发部署、遗留锁或锁创建失败;先核查,不直接删除锁 |
| validation_failed | 配置校验失败,文件已回滚,未执行重载 |
| rollback_failed | 文件回滚未完成,保留锁并人工核查 |
| runtime_unconfirmed | 重载失败后文件回滚,但运行态未确认 |
| lock_release_failed | 锁释放失败,核查现场后再重试 |
| cleanup_failed | 已推送并重载,但旧文件清理失败,不能宣称站点已生效 |
连接、目录、上传和文件替换等阶段也有独立固定诊断。站点 HTTPS 校验继续使用 verification_status,与单台服务器文件推送结果分开判断。
> 验证收尾同步(2026-09-28):最终候选已完成 Windows 独立程序历史备份/升级/成员恢复和 Windows/Linux 构建,产物指纹与最终回归见支持矩阵第二十二节。Linux 为交叉构建,未进行原生运行;本批没有清理生产数据库、执行 SSH 部署或真实服务重启,仍禁止将候选构建通过解释为生产准入。
> 当前运行时候选(2026-09-28):SQLite 驱动固定为 v1.59.0,配套 libc v1.75.7,实测引擎 3.53.4;模式仍为 14。旧 v1.40.1 的专项通过不意味着其引擎满足当前 WAL 数据完整性要求,不使用中间版本替代最终候选。升级需先验证正式备份、历史模式升级及恢复;不直接部署本批工作区或降低 TLS。当前验证与未关闭的 MySQL 索引异常见支持矩阵第二十二节,本节不是生产放行许可。
> 成员成功变更与审计已使用同一事务;MySQL 成员表和审计表必须为 InnoDB,遇到历史非事务表拒绝变更而非自动改引擎。失败请求审计、完整 Web/API 和业务授权仍未接入,不能以仓储回归替代权限隔离验收。
> 最新候选模式为 14:新增项目成员多角色关系与实时判权基础,不为历史账户自动授予默认项目权限,也不将用户分组转为项目权限。必须使用支持模式 14 的程序打开升级后的库;下方模式 13 及更早提示仅为历史批次。不能删除迁移记录强行降级。
> 成员关系引用的项目和账户不能直接删除或替换,必须先显式撤销关系。MySQL 成员外键匹配历史用户主键字符集,版本登记失败可重试,残缺同名表必须拒绝;SQLite 触发器冲突时回滚,不覆盖旧触发器。成员管理接口、界面和业务授权尚未全部接入;此候选不是生产放行版本。本批隔离验证、备份恢复和剩余缺口见支持矩阵第二十一节,未执行生产清库或部署。
> 最新候选模式为 13:新增任务/审计范围快照与历史回填。必须使用支持模式 13 的程序打开已升级数据库,不能删除迁移记录降级。SQLite/PostgreSQL 在事务中回填;MySQL 可能保留部分 DDL,修复失败原因后使用同版迁移重试。回填会执行原有更新触发器,禁止绕过触发器或删除历史记录强行升级。未知归属和操作者保持未知,不自动补成默认项目或管理员。
> 本批只做隔离验证,不执行生产升级或清库。程序备份恢复、双版本 MySQL 及未覆盖项见支持矩阵第二十节;PostgreSQL 当前实库、Linux 原生、干净完整发行包、服务重启及生产门禁仍未完成。下方模式 10~12 提示为历史记录,不是当前程序版本,也不是生产放行许可。
> 当前模式提示(2026 年 9 月 28 日):模式 11 已增加域名/服务器归属与同项目绑定;新增模式 12 用于修复 SQLite 替换冲突对引用保护的绕过。下方模式 10 提示是上一批历史记录。必须用支持模式 12 的程序升级已有模式 11 数据库;只改旧迁移文件不能修复已登记的库,也不能用只支持模式 10/11 的旧程序直接打开升级后的数据库。禁止删除迁移记录强行降级,回退必须使用兼容程序和已验证备份。
> 升级发现悬空绑定或资源引用缺失项目时应拒绝继续,保留原库及模式,先核查真实业务再修复;不能自动删除旧任务、资源或绑定来通过迁移。SQLite 触发器冲突、版本登记失败也必须整体回滚后重试。当前证据见支持矩阵第十九节;本次未执行生产部署,成员授权、完整发行包、实机服务与全部平台门禁仍待验收。
> 2026 年 9 月 28 日模式版本提示:新增第 10 号项目根实体迁移,只建立项目表和默认项目,不代表旧资源已经完成项目归属或权限隔离。SQLite、MySQL 5.7/8.0 的局部验证及独立 Windows 程序备份恢复证据见支持矩阵第十八节;PostgreSQL 项目实库、完整发行包、服务重启和生产门禁仍未完成。不要直接用只支持模式 9 的旧程序打开已升级数据库,也不要删除迁移记录强行降级;升级回退仍需兼容版本及经过验证的备份恢复流程。该说明不是生产放行许可。
> 2026-09-27 验收入口:部署当前候选前,先按《整改任务规格与验收约定》及《Scert整改任务执行清单》核对版本、环境、授权与 12 层生产门禁。下文历史拓扑不是当前服务器状态,不因旧批次通过而跳过本次实机检查。源码、候选包与证据边界见《仓库目录与产物边界说明》。
> 2026-09-27 正式范围:仅交付 Web 和 Windows/Linux 服务端。Wails 历史源码不参与发布,桌面窗口、托盘、更新、桌面配置及 UI 测试不作为生产门禁。CLI 仅作为服务端运维工具;本文件的 Linux 部署步骤不代表 Windows Service 已验收。
> 2026-09-15 云主机验收拓扑已更新:`47.108.78.12` 为管理机,`8.137.23.95` 仅为推送测试机。下文局域网地址是历史测试示例,不能用于本轮部署。最新包、实机证据、禁止修改范围及生产阻塞见 [发布加固与实机验收记录](发布加固与实机验收记录.md)。
## 1. 文档范围
本文档用于 Scert `0.1.0` 的 Linux 生产部署、升级、备份、恢复、监控和故障处理。当前测试拓扑为:
| 主机 | 用途 | 允许部署内容 |
| --- | --- | --- |
| `192.168.2.3` | Scert 服务主机 | Scert、systemd unit、Nginx 反向代理 |
| `192.168.2.4` | SSH 和证书推送目标 | SSH、公钥、证书目标目录、Nginx;禁止部署 Scert |
开发验收端口:
- Scert:`127.0.0.1:9004`
- Nginx:`0.0.0.0:9003`
正式生产通常由 Nginx 监听 `443`。`9003` 只用于当前隔离网络开发测试,`9004` 必须保持回环监听,不得对公网开放。
## 2. 生产准入条件
部署前必须全部满足:
- [ ] 使用 Go `1.26.6` 或更高兼容安全版本完成发布构建。
- [ ] `go test -count=1 ./...`、`go test -race -count=1 ./...`、`go vet ./...`、`go mod verify` 和 `govulncheck ./...` 通过。
- [ ] Web 前端生产构建通过,生产依赖不存在高危或严重漏洞。
- [ ] Linux amd64 发布包中的 `scert`、`scert-admin`、`web/`、systemd unit 和安装器来自同一提交。
- [ ] 目标主机时间同步正常,系统 CA、OpenSSH、Nginx 和安全更新处于受支持状态。
- [ ] Scert 仅绑定 `127.0.0.1`,Nginx 到 Scert 的 HTTPS 上游证书校验已开启。
- [ ] 已完成一次停服完整备份、篡改拒绝、恢复和失败回滚演练。
- [ ] 已确认 SSH 主机指纹并验证证书推送、私钥权限、Nginx 重载和失败回滚。
- [ ] 管理员账号使用独立强密码,DNS、SMTP、HTTP 通知和 SSH 私钥没有写入代码、日志、文档或 Git。
任何一项未满足时,只能作为开发或受控试运行环境,不能宣称正式生产准入。
## 3. 发布构建
### 3.1 后端验证
```bash
cd backend
go test -count=1 ./...
go test -race -count=1 ./...
go vet ./...
go mod verify
govulncheck ./...
```
### 3.2 Web 构建
```bash
cd frontend
npm ci
npm run build
npm audit --omit=dev --audit-level=high
```
### 3.3 Linux amd64 二进制
```bash
cd backend
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
go build -trimpath -ldflags "-s -w" -o ../release/linux-amd64/scert ./cmd/scert
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
go build -trimpath -ldflags "-s -w" -o ../release/linux-amd64/scert-admin ./cmd/scert-admin
```
发布目录必须包含:
```text
linux-amd64/
├── install-linux.sh
├── scert
├── scert-admin
├── web/
└── systemd/
```
发布包应额外生成 SHA-256 清单并通过可信通道核对。安装器会再次验证二进制是 Linux amd64 ELF,并拒绝符号链接或特殊 Web 文件。
## 4. Linux 安装与升级
### 4.1 首次安装
```bash
chmod +x install-linux.sh scert scert-admin
sudo ./install-linux.sh --source-dir . --certificate-dir /ssl/scert
```
安装器不会自动启动服务,也不会创建公网防火墙或云安全组规则。证书目录使用 Scert 专属子目录,不接管共享 `/ssl`。首次安装通过 Bootstrap HTTPS 页面初始化;本轮公网只使用 Nginx 的 `80/443`,内部服务从首次启动起保持回环监听。
不得沿用默认公网监听或放行 `8080`。首次启动前使用 `sudo systemctl edit Scert` 写入以下配置;内部端口须确认空闲,并与向导最终配置及 Nginx 上游一致:
```ini
[Service]
Environment="SCERT_SERVER_HOST=127.0.0.1"
Environment="SCERT_SERVER_PORT=9004"
Environment="SCERT_BOOTSTRAP_FIREWALL_MODE=external"
```
`SCERT_BOOTSTRAP_PUBLIC_IP` 只决定临时初始化地址和证书的 IP,不会改变监听地址、云安全组或最终管理地址。命令行运行时对应参数是 `scert serve --bootstrap-public-ip <服务器公网IP>`。修改 systemd 配置后执行:
```bash
sudo systemctl daemon-reload
sudo systemctl enable Scert
sudo systemctl start Scert
sudo systemctl status Scert --no-pager -l
sudo cat /run/scert/bootstrap-url
```
受保护地址文件中的内部地址不是公网入口。应通过已经严格验证上游证书及名称的 Nginx 入口访问,浏览器端仍须验证正常域名及可信链;首次反代及切换正式证书尚需实机验证。链接片段包含短期一次性令牌,不要复制到日志、截图、工单或普通文档。地址文件仅允许服务用户和管理员读取,完成初始化或服务退出后会清理;不要删除正式配置强制重新初始化。
初始化上游使用临时自签名证书,不代表公共 CA 信任。建立限定用途的上游信任前,先通过已核对 SSH 主机身份的运维连接或云控制台读取当前启动日志:
```bash
sudo journalctl -u Scert -b -n 30 --no-pager
```
找到最新的 `Bootstrap 临时证书 SHA-256 指纹`,与回环上游实际证书的 SHA-256 指纹逐字比较;不一致立即停止,不得输入 RAM 凭据或管理员密码。核对后仍须保持 Nginx 上游的证书及名称验证,不能通过关闭 TLS 校验接通代理。每次未初始化服务重启都会生成新证书,旧日志的指纹不能用于当前核对。指纹是公开证书摘要,日志不包含完整初始化链接、令牌或私钥。上游核对不代替浏览器到 Nginx 的域名和公共信任验证。
向导依次配置管理员、存储、阿里云 RAM 与域名验证、通知和正式管理入口:
- SQLite 使用本机文件;MySQL/PostgreSQL 使用预先创建的专属数据库及最小权限账号,不能用生产共享库做初始化测试。
- MySQL 必须启用 TLS 并校验服务端证书及主机名,当前使用系统信任库;PostgreSQL 使用 `sslmode=verify-full`,可指定服务端可读的根证书绝对路径。
- 数据库和 DNS 验证成功后保存的是短期验证状态;修改输入或状态过期需重新验证,不允许手填内部凭据引用。
- 正式入口可选择域名或 IP;域名证书需要真实 DNS/ACME 验证,IP 入口当前为自签名证书。默认 ACME staging 证书也不受公共信任,不能当作 production 证书。
- 保存成功后,临时初始化服务撤销会话并关闭,再自动加载正式配置启动服务,页面转到返回的正式 HTTPS 地址,无需手工重启切换。
首次与正式端口都只能监听回环;若端口不同,应同步准备 Nginx 上游切换,不新增公网放行规则。固定及动态生成的 Linux systemd unit 显式设置 `SCERT_BOOTSTRAP_FIREWALL_MODE=external`:不运行 UFW/firewalld 查询和写入命令,完成响应及运维日志明确提示端口尚未检查。服务不会代替管理员修改云安全组,不能为方便初始化而向公网开放内部端口。
直接执行 `scert serve` 默认仍为 `auto`,自动防火墙操作限时三十秒,权限错误必须中止初始化,不会因失败自动切换外部模式。使用外部策略时可显式传入 `--bootstrap-firewall-mode external`,参数优先于同名用途的 `SCERT_BOOTSTRAP_FIREWALL_MODE` 环境变量。策略只能由本机启动配置选择,不能由浏览器提交。2026-09-17 已通过非特权 systemd 的隔离 Bootstrap 启动与重启协议检查,正式初始化完成及公网可达性仍待验收,不得将整个 Scert 服务改成 root 或取消加固。
主服务地址族白名单必须包含 `AF_NETLINK`,用于 Go 读取本机网卡并生成临时证书;缺失时会出现 `netlinkrib: address family not supported by protocol`。保留空 capability 集,不需要授予 `CAP_NET_ADMIN`。此要求只适用于主服务,不得照搬到仅允许本地 IPC 的超级管理员恢复服务。
回环监听必须在初始化前应用,远程浏览器只使用 Nginx 入口,不直连内部 IP 端口。`SCERT_SERVER_PORT` 等环境变量会持续覆盖向导保存的配置,应与最终选项一致。代理可信链尚未接通时必须先修复,不得退回公网直连。
上述是新版源码的预期安装流程,不代表本轮已构建发布包或完成 Linux systemd 实机验收。
### 4.2 升级
安装器禁止在 `Scert.service` 或 `Scert-admin.service` 运行时覆盖程序。升级顺序:
停服核验使用 `systemctl show` 同时读取加载状态、活动状态、子状态、主进程、控制进程和排队任务。仅允许已加载的 `inactive/dead`、无进程的 `failed/failed`,以及首次安装的 `not-found/inactive/dead`;主进程与控制进程必须都为零,任务必须为空。查询失败、字段缺失、启动中、停止中、重载中或 masked 状态均在写入前拒绝。失败状态获准替换文件不代表原故障已解决,启动前仍需检查退出原因。
这是安装前的状态检查,不是与 systemd 的原子互斥。维护窗口内不得有其他管理员、定时任务或自动化工具并发启动服务;安装器不能防止检查完成后外部又启动服务。
```bash
sudo systemctl stop Scert-admin
sudo systemctl stop Scert
sudo -u Scert /scert/scert --config /scert/conf/scert.json backup
sudo ./install-linux.sh --source-dir /tmp/scert-linux-amd64
sudo systemctl start Scert
sudo systemctl status Scert --no-pager -l
```
没有显式传入 `--certificate-dir` 时,升级会读取 `/scert/conf/installation.json` 并保留既有证书根目录。升级不得覆盖 `/scert/conf/scert.json`、数据库、主密钥、敏感配置、证书和备份。
安装器在任何发布文件写入前检查既有目标类型;文件目标不能是目录,Web 目录不能包含符号链接或特殊文件。升级前发布文件会保留在 root 所有、权限 `0700` 的 `/scert/.install-XXXXXXXX` 中,`targets.txt` 按行记录目标,`before-0` 等编号与行号减一对应。不要删除该目录,确认新版本业务验收完成后再按保留策略人工处理。
Web 使用整目录替换,旧资源不会继续留在当前 Web 目录中;原目录和升级副本均保留。安装器捕获非零退出、INT 和 TERM 时会尝试恢复安装记录、两个程序、Web、两个 unit、证书目录 drop-in 和两份服务说明,并重新加载 systemd。失败现场保存在同一目录的 `failed-*` 中,不启动或启用服务。若提示“安装回退不完整,禁止启动服务”,必须先核查保留目录与 systemd 状态,不能继续执行启动命令。
此回退仅涵盖安装中的发布文件变更,不回退服务用户创建、既有业务目录权限规范化,不替代停服数据库备份;断电、SIGKILL、磁盘故障及安装成功后启动新程序造成的数据迁移,也不保证自动恢复。需要回到旧版本时,应保留现场,使用已校验的旧版完整发布包重新安装;若新程序已经改写数据库,应先按对应版本的备份恢复流程处理,不得仅覆盖可执行文件。
仓库提供 `deploy/tests/install-linux-test.py`,仅用于 Linux root 下的隔离文件安装测试。它以私有挂载命名空间映射固定安装路径,使用新建测试目录和 systemctl 故障注入桩,保留全部产物;要求指定既有普通证书挂载点及挂载点之外的产物目录。该测试不能代替真实 systemd 升级、启动、就绪检查和完整业务回退验收。
### 4.3 systemd 安全边界
主服务使用独立 `Scert` 用户、`UMask=0077`、`NoNewPrivileges`、私有设备和临时目录、内核与控制组保护、只读系统目录和受限地址族。安装后必须验证:
```bash
sudo systemd-analyze verify /etc/systemd/system/Scert.service
sudo systemd-analyze security Scert.service
sudo systemctl show Scert -p User -p Group -p NoNewPrivileges -p ProtectSystem
```
不得为方便调试改成 `root` 运行,也不得扩大 `ReadWritePaths` 到整个文件系统。
## 5. 网络与反向代理
### 5.1 Scert 监听
生产配置或 systemd 环境覆盖:
```text
SCERT_SERVER_HOST=127.0.0.1
SCERT_SERVER_PORT=9004
SCERT_SERVER_HTTPS_MODE=proxy
SCERT_WEB_ENABLED=true
```
验证:
```bash
sudo ss -lntp | grep 9004
curl -k --fail --silent --show-error https://127.0.0.1:9004/healthz
curl -k --fail --silent --show-error https://127.0.0.1:9004/readyz
```
`ss` 结果必须是 `127.0.0.1:9004` 或 `[::1]:9004`,不能是 `0.0.0.0:9004`。
`/healthz` 只用于存活检查;返回 HTTP `200` 仅表示 Scert 进程和 HTTP 服务可响应。`/readyz` 才是生产就绪检查,安装、升级、恢复、配置变更、Nginx 切流和监控告警均必须以它为准。
`/readyz` 只有在以下条件全部满足时返回 HTTP `200`:
- 初始化已完成。
- DNS 凭据引用存在并且可解密。
- 已配置的 HTTP 通知和 SMTP 通知凭据可解密。
- 内部 TLS 证书和私钥存在、是普通文件且不是符号链接。
- 数据库连接检查成功。
失败时返回 HTTP `503`,原因码不包含完整敏感信息引用:
| 原因码 | 处理要求 |
| --- | --- |
| `setup_incomplete` | 完成初始化流程。 |
| `dns_credential_missing` | 通过管理页面配置 DNS Provider 并完成在线 API 测试。 |
| `secret_store_unavailable` | 检查 `master.key`、`secrets.json` 和配置目录权限。 |
| `credential_unavailable` | 重新录入真实 DNS、HTTP 通知或 SMTP 通知凭据;禁止写入假 Token 绕过。 |
| `tls_material_unavailable` | 检查内部 TLS 证书和私钥路径、文件类型与权限。 |
| `database_unavailable` | 检查 SQLite 文件、MySQL/PostgreSQL 连通性、加密凭据、TLS、账户权限和连接限制。 |
`/healthz` 返回正常而 `/readyz` 返回 `503` 时,服务只能用于故障排查,不能接收生产流量。
### 5.2 Nginx
生产 Nginx 对外通常只开放 `443/tcp`;如需 HTTP 跳转,再开放 `80/tcp`。Nginx 到 Scert 必须配置:
```nginx
proxy_ssl_server_name on;
proxy_ssl_name scert.example.com;
proxy_ssl_verify on;
proxy_ssl_trusted_certificate /etc/ssl/certs/ca-certificates.crt;
proxy_ssl_verify_depth 3;
```
生产配置禁止 `proxy_ssl_verify off`。私有 CA 应使用 root 管理的独立 CA 文件。当前 `9003` 测试入口不得开放到公网。
## 6. 防火墙
生产建议:
| 端口 | 来源 | 用途 | 要求 |
| --- | --- | --- | --- |
| `22/tcp` | 管理网段 | SSH 运维和证书推送 | 限制来源地址,优先仅允许密钥认证 |
| `443/tcp` | 业务访问网段 | Nginx HTTPS | 正式生产入口 |
| `80/tcp` | 可选 | HTTP 到 HTTPS 跳转 | 不使用跳转时关闭 |
| `9004/tcp` | 无外部来源 | Scert 内部端口 | 只绑定回环,不创建放行规则 |
| `9003/tcp` | 仅开发测试网段 | 当前 Nginx 测试入口 | 禁止公网开放 |
云安全组和主机防火墙必须同时核对,不能只配置其中一层。
## 7. 备份
### 7.1 备份内容
当前 Scert 内置备份/恢复支持 SQLite、MySQL 和 PostgreSQL。PostgreSQL 使用官方工具,自动恢复仅支持原目标数据库且对象清单、所有者一致的场景,不支持空库灾难恢复或跨结构版本迁移。工具版本、严格 TLS、权限及集群级资源的保护要求见 [PostgreSQL备份恢复说明](PostgreSQL备份恢复说明.md)。各平台真实演练结果须单独核对,功能存在不代表三数据库完整生产验收。
完整备份包含:
- `scert.json`
- `master.key`
- `secrets.json`
- `scert.db`、`scert.sql` 或 `scert.pgdump`
- `certificates/`
- `manifest.json`
SQLite 使用 `VACUUM INTO` 创建一致性快照并执行 `PRAGMA quick_check`。清单记录相对路径、权限、大小和 SHA-256。日志和数据库审计记录默认保留 30 天;文件日志每天最多写入 5 MiB,超过后停止文件写入但保留标准输出日志。
SQLite 文件库的每条物理连接(包括扩容和重建)均设置 WAL 与默认 5000 毫秒忙等待;不会把最大连接数强行改成 1 来绕过并发问题。带截止时间的数据库调用在剩余时间不足 5 秒时缩短本次原生等待预算,执行结束后恢复默认值;覆盖普通/预处理执行、查询、事务启动和结果读取。配置恢复失败会淘汰连接,不自动重放业务 SQL。其余 DSN 参数保留,忙等待和日志模式由产品控制。
重要限制:没有截止时间的手动取消仍依赖原生驱动中断,本批持锁测试在取消后约 5 秒返回;较长截止时间调用中途手动取消也不能承诺立即退出当前忙等待。事务提交/回滚沿用驱动接口,本批不承诺任意 SQL 脚本整体原子性或所有取消路径立即终止。Windows SQLite 3.49.1 已验证双连接池、三子进程竞争和活动 WAL 快照,但不代表网络文件系统、Linux 服务重启、断电恢复、完整业务数据恢复或多实例生产部署已经验收。证据及未完成项见《构建版本与数据库支持矩阵》第十五节。
SQLite 恢复前必须停止 Scert 服务和所有外部数据库访问。恢复命令取得维护锁后,由 SQLite 自身合并旧 WAL 并切换到 DELETE 日志模式,关闭连接后确认日志已清理,再生成恢复前副本并替换主文件;不能手动删除 WAL/SHM 来强行通过。存在外部事务、孤立日志、非普通日志路径或无法完成模式切换时会拒绝恢复。服务下次正常打开数据库仍按产品配置启用 WAL。服务维护锁不能阻止不遵守该锁的外部程序在检查结束后重新访问数据库,因此维护期间必须持续停止这些程序。
源码 `6db5d7b` 的 Windows 隔离测试已验证异常退出后的恢复与失败回滚、当前业务表和关联、配置归属、用户密码、加密凭据、真实证书私钥及重复恢复;证据见支持矩阵第十六节。这不是历史业务升级、独立发行程序、真实服务重启、物理断电或外部 DNS/SSH 调用的验收证明。无法正常读取的损坏库应先人工隔离并保留整套主文件和日志,不绕过日志安全检查。
MySQL 账号和密码从加密数据库凭据引用读取,只在维护连接期间存在于内存,不写回普通配置或备份清单。恢复前固定当前可用账号用于快照、导入和失败回滚;数据库服务端账号本身的密码轮换仍需单独管理,恢复旧配置不能自动恢复数据库账户密码。
MySQL 完整备份先检查有效库级或全局 `SELECT`,不能用零散表级权限或过滤后的空清单证明对象完整。视图还要求有效 `SHOW VIEW`;失效视图或定义不可见时拒绝备份并保留原文件。视图按本库依赖排序,保留定义者、创建字符集和检查选项,不创建占位基础表,也不删除与视图同名的已有基础表。基础数据和例程先于视图,随后创建触发器、恢复外键检查,最后恢复事件;仍需管理员保证维护窗口内没有外部 DDL 或授权变化。
本批嵌套 `CHECK OPTION` 夹具的原始官方导出文件在官方客户端出现 1368;Scert 仅对严格识别、连续且闭合的官方最终视图块按依赖重排,块内 SQL 和输入文件保持不变。不跨业务语句、`USE` 或未知设置排序;执行对应块前核对数据库、SQL 模式与客户端编码。此能力不是任意 SQL 兼容承诺,也不是恢复失败后已执行 DDL 的原子回滚保证。账户、跨库依赖及定义者授权仍需管理员准备。验证范围、证据和限制见《构建版本与数据库支持矩阵》第十四节。
MySQL 自有备份已增加触发器导出。维护账户除读取表结构/数据外,还需对每张可见基础表具有可证明的有效 `TRIGGER` 权限;没有权限时,数据库可能隐藏触发器,Scert 会明确拒绝而不是报告不完整备份成功。即使业务当前没有触发器,也不能据此省略该可见权限。产品不会自动扩权;精确库级或表级授权由管理员审核配置,复杂且存在遮蔽的通配授权可能被保守拒绝。
MySQL 自有备份同时导出存储过程和函数。5.7 维护账户需对 `mysql.proc` 具有完整 `SELECT` 权限;8.0 需具有有效 `SHOW_ROUTINE`,或没有被当前库部分撤销限制的全局 `SELECT`。仅有库级 `EXECUTE`、`CREATE ROUTINE` 或元数据中看不到例程,不能证明完整定义可见;即使源库当前没有例程,检查仍会执行。8.0 支持通过已启用角色获得权限,不会自动启用角色或扩权。请优先由管理员评估专用维护账户及最小可见权限,不给业务账户照搬测试授权。
恢复时先写入所有表数据,再创建例程和触发器,恢复外键检查后最后创建事件,保留原定义者、SQL 模式和创建字符集;同名例程和事件会先删除再创建。目标账户需具备相应对象管理权限、数据库 `ALTER` 权限,并满足实际服务器对定义者和二进制日志的权限要求。不能照搬隔离测试账户的全局管理权限,也不能通过开启全局信任来绕过验证。
MySQL 自有备份已增加事件导出,保存计划、时区、启停状态和创建会话属性。维护账户必须具有可证明的有效全局或库级 `EVENT` 权限,即使当前看不到事件也不能省略;表级授权不能证明事件完整可见。已经到期且重新创建会被自动删除或改变启用状态的事件,以及无法验证时区的事件,会被明确拒绝,不覆盖已有备份。
Scert 恢复需要有效全局 `PROCESS` 权限以检查事件工作线程。请在维护窗口由管理员停止事件调度,等待工作线程自然退出,并防止其他会话重新开启调度或修改对象;仅将调度器设为 `OFF` 不代表已有工作线程已经退出。产品不会自行修改全局调度器、终止线程或扩大权限。调度器为 `ON` 且目标库已有启用事件,或导入内容可能启用事件时,Scert 会在执行首条恢复语句前拒绝;全部事件禁用且没有运行线程的情况不受此项限制。工作线程识别会保守拒绝其他处于 `Connect` 状态的连接。
事件恢复后发现自动删除、自动禁用等影响对象完整性的警告时,Scert 会停止后续语句并报告失败,但不承诺回滚已经执行的 DDL。使用官方 MySQL 客户端导入时不会执行 Scert 的预检,管理员必须自行落实上述维护窗口、检查警告并核验对象、计划、时区及启停状态,完成核验后再恢复调度。
自有导出对视图及完整表清单的支持尚未补齐;例程和事件的 UTF-8、Latin-1 指定场景不代表任意字符集、SQL 模式或跨服务器定义者迁移通过。成功恢复后还原会话属性和源库数据库排序规则;中途失败的 DDL 原子回滚及排序规则回退仍需验证。不会清除备份之外的额外目标例程或事件,也不恢复数据库账号授权。例程历史证据见《构建版本与数据库支持矩阵》第十一、十二节;事件实现及正式 Go 测试已纳入代码,但不能据此宣布完整 MySQL 灾难恢复或生产部署验收通过。
### 7.2 停服备份
```bash
sudo systemctl stop Scert
sudo -u Scert /scert/scert --config /scert/conf/scert.json backup
sudo systemctl start Scert
```
程序会获取 `/scert/data/scert.lock` 跨进程维护锁。即使存在维护锁,生产流程仍必须停服,因为证书目录还可能被外部部署工具修改。
备份目录必须保持 `0700`,并复制到加密、离线或不可变存储。`manifest.json` 不是数字签名,不能替代备份介质权限、异地副本和访问审计。
### 7.3 SQLite 历史模式升级与恢复
升级前必须停止 Scert 服务,保留一份经过清单和 SHA-256 校验的有效备份,并确认目标数据库、配置目录和证书目录均属于本次维护范围。不要直接用旧发行包覆盖生产数据库;先在隔离副本上使用目标版本执行升级,核对迁移历史、业务表、关联、索引、视图、触发器、凭据和证书配对,再安排生产窗口。
SQLite 历史模式在迁移 4 需要为已有任务数据增加时间列时,程序会在固定连接和单一事务中重建任务表,保留已验证的业务列及依赖对象,提交前执行外键检查。临时关闭的外键检查只作用于该迁移连接和该事务,事务结束后恢复原设置,不是全局永久关闭。临时表冲突、外键冲突或版本登记失败时必须整体回滚;排除具体阻塞后才允许重试,不得手工删除表、日志或修改 `sqlite_master`。
当前回归覆盖仓库迁移文件构造的模式 1~9、代表性业务数据、生成列、索引、视图、触发器和外键关联;不承诺任意第三方改造模式、旧发行包实际数据、断电损坏或磁盘故障均可自动升级。交叉编译和 Windows 隔离测试不能替代 Linux 原生运行、真实服务重启或生产数据库验收。
## 8. 恢复
恢复前先复制当前完整数据目录和证书目录到受保护位置,然后执行:
```bash
sudo systemctl stop Scert
sudo -u Scert /scert/scert --config /scert/conf/scert.json \
restore --input /scert/backups/scert-YYYYMMDD-HHMMSS
sudo systemctl start Scert
```
恢复会拒绝:
- 缺少或被篡改的清单。
- 符号链接、特殊文件、异常权限、大小或 SHA-256。
- 跨配置目录、数据目录、证书目录、备份目录或数据库目标恢复。
- 无法解密的敏感配置引用。
- 主服务或另一个维护任务持有进程锁。
恢复前副本使用 `*.before-restore-YYYYMMDD-HHMMSS` 命名。失败时程序会尝试整体回滚;完成后必须验证健康接口、登录、域名列表、证书读取、SSH 推送和 Nginx HTTPS。
## 9. SSH 与证书推送
`192.168.2.4` 仅作为推送目标,不安装 Scert。每台主机、每次配置都必须生成随机公钥名称:
```text
/ssl/ssh/用户名+scert-随机值
```
验收要求:
- 首次连接人工核对 SSH 主机指纹。
- 主机指纹变化时拒绝连接,禁止自动接受新指纹。
- `cert.pem`、`fullchain.pem` 权限为 `0644`,`privkey.pem` 为 `0600`。
- 使用临时文件和原子重命名替换证书。
- Nginx 配置测试成功后才执行 reload。
- 推送、校验或 reload 失败时恢复旧证书。
- 临时私钥、密码、Token 和完整连接串不得写入日志。
## 10. 监控与日常检查
建议至少监控:
- `/healthz` 状态和响应时间。
- `/readyz` 生产就绪状态;连续返回 `503` 时停止切流并立即告警。
- systemd 服务状态、重启次数和退出码。
- 证书剩余有效期;默认续期阈值为 6 天,3 天进入紧急状态。
- 证书申请、部署、校验和通知任务失败数量。
- 文件系统剩余空间、SQLite/MySQL/PostgreSQL 可用性和备份最后成功时间。
- Nginx 5xx、上游 TLS 校验失败和 SSH 主机指纹变化。
常用命令:
```bash
sudo systemctl status Scert --no-pager -l
sudo journalctl -u Scert --since "1 hour ago" --no-pager
sudo ss -lntp | grep -E ':(443|9003|9004)\b'
sudo -u Scert /scert/scert --config /scert/conf/scert.json status
```
日志输出不得包含 DNS Token、SMTP 密码、HTTP 通知密钥、SSH 私钥、超级管理员密码或会话 Cookie。
### 10.1 共享主机测试资源隔离
启动实机演练前检查 `MemAvailable`、Swap、`/proc/pressure/memory`、磁盘空间和内核 OOM 日志,确认既有业务服务正常。多个项目应协调测试窗口,避免并发构建或启动数据库集群。
测试使用独立目录、全新数据库和回环端口,不清空既有业务库。通过 systemd 临时单元将测试程序及其子进程放入同一 cgroup,设置内存、CPU、进程数和总运行时间限制,并保留 `KillMode=control-group`。资源限制须按场景容量制定;小数据演练的限制不能直接作为生产配置。
超时或 SSH 失联后,先检查主机和共享服务,再核对本轮 PID、集群目录及端口,只停止确认属于本轮的测试资源。保留产物,不通过重启共享数据库、扩大权限或单纯延长超时掩盖故障。区分全局 OOM 与 `CONSTRAINT_MEMCG`,不要把其他项目的受限测试失败记为 Scert 的结果。
## 11. 当前外部验收边界
以下项目必须有真实外部环境和合法凭据才能完成,不能由本地自动化测试替代:
- 真实 DNS Provider API 的最小权限凭据与在线写入/清理验证。
- 合法域名的 Let’s Encrypt Production 申请、续期和速率限制处理。
- MySQL 5.7 与 8.0 的真实备份、恢复和失败回滚。
- PostgreSQL 目标环境的真实连接、迁移、权限、业务与恢复演练;空库灾难恢复及跨版本迁移仍不属于内置自动恢复支持范围。
- 新 IP 初始化在最小权限 systemd 下的防火墙行为、临时证书身份核对、浏览器页面及正式入口切换。
- Windows Service 安装、升级、开机启动、停止和 ACL。
- 公网生产 Nginx、真实证书链和最终业务访问。
这些外部项未完成时不能声明完整生产准入。2026-09-17 已从干净提交生成 Linux 候选包,当前实机进展见 [Linux生产流程测试记录](Linux生产流程测试记录.md)。Linux + SQLite 的受控试运行同样需要当前版本的目标主机验证,不得复用旧版本结论或把隔离测试等同于正式切流。