fix(证书管家): writeapi.usj.cc 证书纳管(相对软链接);pushall 改为先 rebase 再推

用户:「动手 推送吧」—— 动手处理两个有死线的遗留。

一、writeapi.usj.cc 的证书(12 月会断)→ 已解决

现状复核推翻了上一轮的判断:
  · 上一轮记的「它的 ssl_certificate 引用不在 /www/sites/ 树里」是**错的** ——
    /www/conf.d/writeapi.usj.cc.conf 里明确写着
    ssl_certificate /www/sites/writeapi.usj.cc/ssl/fullchain.pem;
  · 真正原因是**它在 1Panel 面板上根本不是一个网站**(面板共 11 个站点,没有它),
    所以 `findWebsite('writeapi.usj.cc')` 必然抛「找不到网站」→
    加进 one_panel_sites 只会让**每次续期都报错**(而且只警告不阻断,长期没人发现)
  · 实测确认:redeploy 时其余 5 个站点正常,只有它失败

解法:**不写一行代码** —— 它与 artalk.usj.cc 本来就同用一张 *.usj.cc 证书,
让它用**相对软链接跟随 artalk** 即可,随续期自动更新:
  ln -sfn ../../artalk.usj.cc/ssl/fullchain.pem /1panel/1panel/www/sites/writeapi.usj.cc/ssl/fullchain.pem

★★ 踩到的坑:软链接**必须相对路径**
  宿主 /1panel/1panel/www/sites/ ↔ 容器内 /www/sites/(1Panel 的 openresty 跑在容器
  `1Panel-openresty-Kmh2`)。第一版写成宿主绝对路径 → **容器侧断链** →
  nginx -s reload **静默失败**(保留旧配置、服务不中断、握手照旧拿旧证书)→
  表面全绿,真实后果是「等某天 nginx 重启,writeapi 的 HTTPS 直接挂」。
  唯一线索是 worker 进程的启动时间没变 —— **所以验收必须看 worker 是否真的重启**。

验收(全部实测):
  替换前 CN=usj.cc    / LiteSSL RSA CA / 2026-12-07(mtime 10-04 21:50)
  替换后 CN=*.usj.cc  / LiteSSL ECC CA / 2027-01-04
  容器内 head -1 → -----BEGIN CERTIFICATE----- / -----BEGIN PRIVATE KEY-----
  预检 syntax is ok / test is successful
  reload → signal process started;**worker 22:51:39 重启**(证明软链接真被读了)
  握手 SNI=writeapi.usj.cc → *.usj.cc / Jan 4 2027;实访 /health → HTTP 200

新增 deploy/cn-certkeeper/src/dump-cert.mjs:把 KV 里(AES-GCM 加密)的证书导成明文 PEM,
补上「KV ↔ 手工 vhost 磁盘文件」之间原先不存在的桥。只读,用完即删(含明文私钥)。

操作层的两个坑(写进文档):
  · 1Panel 文件 API **拒绝写 .mjs**(返回 500「目标路径不存在」,实为可执行扩展名过滤);
    往新建的 root:root 700 目录写也会失败 → 临时脚本用
    `docker exec -i … sh -c 'cat > 路径' < 本地文件` 送
  · cnrun.py(1Panel 计划任务通道)偶发「退出码 0 但零输出」;
    加 `timeout 30 docker exec …` 包一层即稳定,判断状态优先看容器内直接证据

二、配置回退 + 文档更正

  · certkeeper-config.mjs:把 writeapi 从 one_panel_sites 移除,换成一段注释说明
    「它不是 1Panel 站点,别加进来」+ 软链接做法 + 必须相对的警告;
    线上 config.kv 同步回退(sha256 与改动前完全一致 ccac2530…)
  · 更正「3 个手工 vhost」的说法:vaultwarden **是**正常纳管的站点(面板 #13,
    主域名 vw.usj.cc,别名才是 vaultwarden)→ 手工 vhost 实际只剩 writeapi + dnsapi
  · dnsapi.usj.cc 重新定性为**不做**:它是 cn-dns-helper 的反代入口
    (proxy_pass 127.0.0.1:8018,仅 HTTP),去留应与 cn-dns-helper 一起决定,
    不该单独给一个待退役的服务加 HTTPS

三、pushall 加固:先 fetch + rebase 再推(本次真实撞到的问题)

推 CNB 时被 rejected —— 因为线上写作后台(editor-api)发文章会**直接推 main**,
本机两笔提交与之分叉。这不是异常,是**日常**。原别名直接 push,必然反复撞。

  · 新增 scripts/pushall.sh:fetch → 已在远端之后则 rebase → 推所有远端
    - 冲突时**停在 rebase 中途**交人工(不强推、不丢东西)
    - 工作区不干净时 git 自己会拒绝 rebase(不会吞改动)
    - 任一远端失败**不改判另一个**(主仓失败辅仓照样推),退出码以第一次失败为准
    - ⚠️ 修了自己写的一处疏漏:`if ! cmd; then ec=$?` 的 $? 是**取反后**的结果(0),
      不是 git 的退出码 —— 必须显式 ec=1(靠 set -o pipefail 保证管道退出码不被 sed 掩盖)
  · setup-cnb-remotes.sh 的别名改指向脚本;README 脚本表补这一行

四、文档

  · docs/证书管家.md:速览表「已知遗留」→ 已解决;§9.14 遗留段重写(含纠正自己写错的判据
    「路径像不像不构成判据,要直接查面板站点清单」);**新增 §9.15**
    「手工 vhost 的证书:用相对软链接跟随已纳管站点」(机制 + 路径坑 + 三条验收 + dump-cert 用法)
  · docs/架构精简候选.md:候选 5 标记完成,补上「为什么 A 走不通、B 未采用」;
    执行顺序里划掉它
  · 架构总览.md:§5.7「已知遗留」重写(2 个手工 vhost + vaultwarden 更正);
    §6 待办 #13 改为已解决、#14 标注完成
This commit is contained in:
zqlit committed 2026-10-06 22:54:58 +08:00
1 parent 7abef4ac13
commit 3eeafa07b2
8 files changed
+306 -30

No files matched your search

+12 -4
View File
@@ -473,8 +473,16 @@ git clone blog.bundle blog # 或 git fetch blog.bundle 'refs/*:refs/*'
**已知遗留**:
- `writeapi.usj.cc` / `dnsapi.usj.cc` / `vaultwarden` 三个**手工 nginx vhost 不归 1Panel 站点管理** ——
证书是**文件拷贝**,与证书库记录解耦,`/websites/{id}/https` 碰不到它们 → 需单独纳管
- **手工 nginx vhost**(不归 1Panel 站点管理,`/websites/{id}/https` 碰不到)目前**只剩 2 个**:
- `writeapi.usj.cc`(写作后台 API 入口)—— ✅ **已解决**(2026-10-06):证书改用
**相对软链接跟随 `artalk.usj.cc`**(两者本就同用 `*.usj.cc` 那张证书),随续期自动更新。
⚠️ 软链接**必须相对路径**(宿主 `/1panel/1panel/www/…` vs 容器 `/www/…`,绝对路径会在容器内断链,
且断链后 nginx reload 会**静默失败**);验收要看 **worker 进程是否真的重启**,光看握手会被骗。
机制与坑见 [`docs/证书管家.md`](docs/证书管家.md) §9.15
- `dnsapi.usj.cc` —— 其实是 `cn-dns-helper` 的反代入口(`proxy_pass 127.0.0.1:8018`,**仅 HTTP**)。
它的去留应与 `cn-dns-helper` 一起决定,**不单独**给它加 HTTPS(见 [`docs/架构精简候选.md`](docs/架构精简候选.md) 候选 2)
- 注:`vaultwarden` **不是**手工 vhost —— 它是面板 #13 站点(主域名 `vw.usj.cc`),正常纳管。
(本文档早先把它误算作手工 vhost,2026-10-06 更正)
- 动代码前必看 [`docs/证书管家.md`](docs/证书管家.md) 的 **§8.4 / §9.9 / §9.11 / §9.12** 四节
「易误判、勿回退」的坑(ACME 客户端 bug、CSR 三层 SEQ、`ssl/update` 不物化站点文件、
1Panel HTTPS 字段名是大写 `SSL` 等)
@@ -497,8 +505,8 @@ git clone blog.bundle blog # 或 git fetch blog.bundle 'refs/*:refs/*'
| 10 | **整仓离线备份** | ✅ **已上线**(2026-10-06),现为**备选**异地备份(在线那一路是自建 Gitea 辅仓),见 §5.6。计划任务 `Blog-BundleBackup` 每天 03:30 跑,端到端已验证;失败会发告警邮件(已实测)。**2026-10-06 补**:打包前加 `git fetch --all` —— 原来备的是「本机已知 ref」的快照,后台新发的文章可能一份都不在里面,且备份照样报成功(§5.6)。待补三件安全项:把 `BACKUP_PASSPHRASE` 抄进密码管理器;给 OpenList **改掉 admin 密码 + 开两步验证**(现 `otp: false`,且旧密码已出现在对话里);确认 5244 端口**未暴露到公网** |
| 11 | **F50 存储目录的使用约定** | `/本地/` 下的 `刷机` / `系统` / `资料` / `软件` / `驱动` / `备份` 都是用户自己在管的类别。**本项目的 bundle 只写 `备份/blog-bundle/` 子目录**,不与用户手工备份(`github-zqlit-*`、`local-repos-*`)混放 |
| 12 | **editor-api 容器的三层留存** | ✅ **已落地**(2026-10-06,国内机 `119.29.215.187`):`PUSH_REMOTES=origin,gitea`(CNB 主仓 + 自建 Gitea 备份仓),文件层走 §5.6 的加密 bundle。实测:容器内推 `origin` ✅ / 推 `gitea` ✅(临时分支推完即删),`/health` 返回 `remotes:["origin","gitea"]`,三处 `main` 对齐 `448b898e`。接入时 `/srv/blog` 落后 10 个提交,一次 fast-forward 追平 |
| 13 | ★ **证书管家的两个遗留** | 见 §5.7。**① `writeapi.usj.cc` 的证书没纳管**(真问题,会到期):它的 nginx 配置在 `/www/conf.d/writeapi.usj.cc.conf` —— **不在 `/www/sites/` 管理树里**,所以 1Panel 的 `ssl/upload`+`sslID` 物化时碰不到它。现状实测:`CN=usj.cc` / **RSA** / 到期 **2026-12-07**,而本项目续期**不会更新它** → 12 月会断。**② `dnsapi.usj.cc` 没上 HTTPS**(`ssl/` 目录空、源站握手失败)。<br>(附带澄清:`200181.xyz` 公网走 Cloudflare,看到的 LE 证书是 **CF 自家的边缘证书**,与本项目无关;源站 `ssh.200181.xyz` 已是本项目签的 `*.200181.xyz` / 2027-01-04 ✅) |
| 14 | **架构复杂度盘点** | 「感觉架构还是复杂了」的正面回应 → 已盘点:**跨 6 个环境,其中 4 个要自己维护**,这才是复杂度的来源;国内机 9 个容器里属于本项目的只有 3 个,能动的只有 2 个。**5 个精简候选 + 建议执行顺序**见 [`docs/架构精简候选.md`](docs/架构精简候选.md):certimate 容器可停、`cn-dns-helper` 大概率是遗留、Gitea 迁 or 不留、两套写作前端收敛、writeapi 证书纳管 |
| 13 | ★ **证书管家遗留** | ✅ **① 已解决**(2026-10-06):`writeapi.usj.cc` 是**手工 vhost**(面板 11 个网站里没有它,所以按站点名绑定永远找不到目标)→ 改用**相对软链接跟随 `artalk.usj.cc`**,随续期自动更新。证书从 `CN=usj.cc`/RSA/**2026-12-07** 换成 `CN=*.usj.cc`/ECC/**2027-01-04**,容器内可读、预检通过、**worker 已重启**、`/health` 200;机制与「必须用相对路径」的坑见 [`docs/证书管家.md`](docs/证书管家.md) §9.15。<br>**② `dnsapi.usj.cc` 重新定性为「不做」**:它是 `cn-dns-helper` 的反代入口(`proxy_pass 127.0.0.1:8018`,仅 HTTP),去留应与 `cn-dns-helper` 一起决定。<br>(附带澄清:`200181.xyz` 公网走 Cloudflare,看到的 LE 证书是 **CF 自家的边缘证书**,与本项目无关;源站 `ssh.200181.xyz` 已是本项目签的 `*.200181.xyz` / 2027-01-04 ✅) |
| 14 | **架构复杂度盘点** | 「感觉架构还是复杂了」的正面回应 → 已盘点:**跨 6 个环境,其中 4 个要自己维护**,这才是复杂度的来源;国内机 9 个容器里属于本项目的只有 3 个,能动的只有 2 个。**5 个精简候选 + 建议执行顺序**见 [`docs/架构精简候选.md`](docs/架构精简候选.md):certimate 容器可停、`cn-dns-helper` 大概率是遗留、Gitea 迁 or 不留、两套写作前端收敛、~~writeapi 证书纳管~~(✅ 2026-10-06 已完成,见 #13) |
---