17 KiB
Scert 生产部署与运维说明
2026-09-15 云主机验收拓扑已更新:
47.108.78.12为管理机,8.137.23.95仅为推送测试机。下文局域网地址是历史测试示例,不能用于本轮部署。最新包、实机证据、禁止修改范围及生产阻塞见 发布加固与实机验收记录。
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 后端验证
cd backend
go test -count=1 ./...
go test -race -count=1 ./...
go vet ./...
go mod verify
govulncheck ./...
3.2 Web 构建
cd frontend
npm ci
npm run build
npm audit --omit=dev --audit-level=high
3.3 Linux amd64 二进制
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
发布目录必须包含:
linux-amd64/
├── install-linux.sh
├── scert
├── scert-admin
├── web/
└── systemd/
发布包应额外生成 SHA-256 清单并通过可信通道核对。安装器会再次验证二进制是 Linux amd64 ELF,并拒绝符号链接或特殊 Web 文件。
4. Linux 安装与升级
4.1 首次安装
chmod +x install-linux.sh scert scert-admin
sudo ./install-linux.sh --source-dir . --certificate-dir /ssl
安装器不会自动启动服务,也不会创建公网防火墙或云安全组规则。首次安装还没有正式配置时,应先启动服务,再通过独立 Bootstrap HTTPS 页面初始化;不需要 Linux 图形界面。
默认初始化监听 0.0.0.0:8080。管理员应预先在主机防火墙和云安全组中只对受信任的管理来源放行该端口。如果已有端口占用,先设置 SCERT_SERVER_PORT 使用空闲端口。云主机公网 IP 不在本机网卡上时,先使用 sudo systemctl edit Scert 写入以下配置(示例 IP 必须替换为实际管理机 IP):
[Service]
Environment="SCERT_BOOTSTRAP_PUBLIC_IP=203.0.113.20"
SCERT_BOOTSTRAP_PUBLIC_IP 只决定临时初始化地址和证书的 IP,不会改变监听地址、云安全组或最终管理地址。命令行运行时对应参数是 scert serve --bootstrap-public-ip <服务器公网IP>。修改 systemd 配置后执行:
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
通过可信运维通道读取并在浏览器打开完整 HTTPS 地址,不能只复制 IP 部分。链接片段包含短期一次性令牌;不要复制到日志、截图、工单或普通文档。地址文件仅允许服务用户和管理员读取,完成初始化或服务退出后会清理;令牌或会话过期时重启尚未初始化的服务再读取新地址,不要删除正式配置强制重新初始化。
临时 HTTPS 使用自签名证书,不代表公共 CA 信任。浏览器首次打开裸 IP 地址时,应先查看证书详情,通过已核对 SSH 主机身份的运维连接或云控制台读取当前启动日志:
sudo journalctl -u Scert -b -n 30 --no-pager
找到最新的 Bootstrap 临时证书 SHA-256 指纹,与浏览器证书详情的 SHA-256 指纹逐字比较(忽略显示分隔符和大小写)。必须一致才打开含一次性令牌的完整链接并填写敏感配置;不一致立即停止,不要输入 RAM 凭据或管理员密码。每次未初始化服务重启都会生成新证书,旧日志的指纹不能用于当前核对。指纹是公开证书摘要,日志不包含完整初始化链接、令牌或私钥。不得把“忽略证书警告”本身当作生产 TLS 验收;本轮仍缺少真实浏览器核对证据。
向导依次配置管理员、存储、阿里云 RAM 与域名验证、通知和正式管理入口:
- SQLite 使用本机文件;MySQL/PostgreSQL 使用预先创建的专属数据库及最小权限账号,不能用生产共享库做初始化测试。
- MySQL 必须启用 TLS 并校验服务端证书及主机名,当前使用系统信任库;PostgreSQL 使用
sslmode=verify-full,可指定服务端可读的根证书绝对路径。 - 数据库和 DNS 验证成功后保存的是短期验证状态;修改输入或状态过期需重新验证,不允许手填内部凭据引用。
- 正式入口可选择域名或 IP;域名证书需要真实 DNS/ACME 验证,IP 入口当前为自签名证书。默认 ACME staging 证书也不受公共信任,不能当作 production 证书。
- 保存成功后,临时初始化服务撤销会话并关闭,再自动加载正式配置启动服务,页面转到返回的正式 HTTPS 地址,无需手工重启切换。
若首次端口与正式端口不同,应由管理员预先准备正式端口规则,确认切换后关闭不再需要的初始化端口。服务不会代替管理员修改云安全组。固定及动态生成的 Linux systemd unit 显式设置 SCERT_BOOTSTRAP_FIREWALL_MODE=external:不运行 UFW/firewalld 查询和写入命令,完成响应及运维日志明确提示端口尚未检查。管理员应在首次访问前按受信任来源放行所需端口;反向代理模式仅允许代理访问内部端口,不能为方便初始化而向公网开放内部端口。
直接执行 scert serve 默认仍为 auto,自动防火墙操作限时三十秒,权限错误必须中止初始化,不会因失败自动切换外部模式。使用外部策略时可显式传入 --bootstrap-firewall-mode external,参数优先于同名用途的 SCERT_BOOTSTRAP_FIREWALL_MODE 环境变量。策略只能由本机启动配置选择,不能由浏览器提交。非特权 systemd 的真实初始化、重启和网络可达性仍待实机验收,不得将整个 Scert 服务改成 root 或取消加固。
反向代理方案应在初始化完成后再应用第 5 节的回环监听配置;若预先设置 SCERT_SERVER_HOST=127.0.0.1,远程浏览器将无法直接访问 IP 初始化端口。SCERT_SERVER_PORT 等环境变量会持续覆盖向导保存的配置,应与最终选项一致。
上述是新版源码的预期安装流程,不代表本轮已构建发布包或完成 Linux systemd 实机验收。
4.2 升级
安装器禁止在 Scert.service 或 Scert-admin.service 运行时覆盖程序。升级顺序:
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、数据库、主密钥、敏感配置、证书和备份。
4.3 systemd 安全边界
主服务使用独立 Scert 用户、UMask=0077、NoNewPrivileges、私有设备和临时目录、内核与控制组保护、只读系统目录和受限地址族。安装后必须验证:
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 环境覆盖:
SCERT_SERVER_HOST=127.0.0.1
SCERT_SERVER_PORT=9004
SCERT_SERVER_HTTPS_MODE=proxy
SCERT_WEB_ENABLED=true
验证:
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 必须配置:
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备份恢复说明。各平台真实演练结果须单独核对,功能存在不代表三数据库完整生产验收。
完整备份包含:
scert.jsonmaster.keysecrets.jsonscert.db、scert.sql或scert.pgdumpcertificates/manifest.json
SQLite 使用 VACUUM INTO 创建一致性快照并执行 PRAGMA quick_check。清单记录相对路径、权限、大小和 SHA-256。日志和数据库审计记录默认保留 30 天;文件日志每天最多写入 5 MiB,超过后停止文件写入但保留标准输出日志。
MySQL 账号和密码从加密数据库凭据引用读取,只在维护连接期间存在于内存,不写回普通配置或备份清单。恢复前固定当前可用账号用于快照、导入和失败回滚;数据库服务端账号本身的密码轮换仍需单独管理,恢复旧配置不能自动恢复数据库账户密码。
7.2 停服备份
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 不是数字签名,不能替代备份介质权限、异地副本和访问审计。
8. 恢复
恢复前先复制当前完整数据目录和证书目录到受保护位置,然后执行:
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。每台主机、每次配置都必须生成随机公钥名称:
/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 主机指纹变化。
常用命令:
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。
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 + SQLite 的受控试运行同样需要当前版本的目标主机验证,不得复用旧版本结论或把隔离测试等同于正式切流。