docs(ssl): 方案文档补充第八章 —— 签发/续期落地实现与上线记录

记录本轮实测结论:免费版 [limits] 会拒绝部署(code 100328)、
多吉云 bind 参数名 id、1Panel v2 与 SSL 大写字段、LiteSSL /v2 目录、
CSR extensionRequest 三层 SEQ 等易误判的坑;含上线 version_id 与
403-vs-404 路由存在性验证方法。
This commit is contained in:
zqlit committed 2026-10-06 17:07:29 +08:00
1 parent 8f2eee1a38
commit a3bc10c68c
1 file changed
+73
+73
View File
@@ -384,3 +384,76 @@ KV 键位规划:
> 而且额外多了「凭据加密存储」和「多吉云自动部署」。
> 唯一少的是 certimate 的「可视化工作流编排」(拖拽节点那种)——
> 本项目只有 3 个固定工作流,用配置表单比拖拽更直接,不构成损失。
---
## 八、签发/续期落地实现(2026-10-06 完成并上线)
### 8.1 模块划分(全部纯 WebCrypto,零 npm 依赖)
| 文件 | 职责 | 参照 certimate |
|---|---|---|
| `src/lib/acme.ts` | ACME v2 客户端:ES256 JWS、RFC7638 thumbprint、EAB、badNonce 重试、DNS-01、手写 DER 的 CSR | `internal/certacme` |
| `src/lib/dnsprovider.ts` | DNS-01 适配:DNSPod(TC3-HMAC-SHA256)、Cloudflare | `pkg/sdk3rd/*` |
| `src/lib/deployer.ts` | 部署适配:多吉云 CDN、1Panel 站点(幂等换证书) | `deployers/*` |
| `src/lib/certissue.ts` | 编排:探针判天数 → 账户注册/复用 → 签发 → 落库 → 逐目标部署 | `workflow` |
### 8.2 与 certimate 的关键差异:探针优先
certimate 每个 workflow 挂 cron,**每天无条件重跑整条流水线**。
本项目改为:**先探针查证书剩余天数,低于 `RENEW_BEFORE_DAYS`(30 天)才真正签发**。
好处:省 CA 限速额度(Let's Encrypt / LiteSSL 都有周限额)、DNS 改动更少、日志只有真实动作才有记录。
### 8.3 ★ 免费版跑不了签发(重要结论)
| 项 | 免费版 | 付费版($5/月) |
|---|---|---|
| Worker CPU | **硬顶 10ms** | 默认 30s,可调至 5min |
| `[limits] cpu_ms` 配置 | ★ **直接拒绝部署**(code 100328) | 支持 |
| ACME 签发 | ❌ 跑不起来 | ✅ |
实测报错:
```
X [ERROR] A request to the Cloudflare API (.../workers/scripts/artalk-cf/versions) failed.
CPU limits are not supported for the Free plan. [code: 100328]
```
注意是**整个部署失败**,不是「忽略该字段」——所以 Free plan 下 `[limits]` 必须保持注释。
(探针 / 环境自检 / 手动查询这些轻量功能在免费版完全可用。)
HTTP 请求 duration 本身**无硬限制**,签发的耗时主要花在 `sleep` 等 DNS 传播 + 轮询上,
**不计 CPU**——所以升级 Paid 后,这套架构是成立的。
### 8.4 实测踩坑(易误判,勿回退)
- **多吉云 `bind` 参数是 `{id, domain}`**,不是 `cert_id`。用假 id 999999 做对照实验确认:
`cert_id` 回「域名不存在」(参数被无视)、`id` 回「指定证书不存在」(参数生效)。
- 多吉云上传私钥字段名是 **`private`**(`pri`/`key`/`privateKey` 均报「私钥格式错误」);
列域名用 `/cdn/domain/list.json`(`/cdn/domain.json` 回 `400 domain 格式错误`)。
- **LiteSSL ACME 目录必须带 `/v2`**:`https://acme.trustasia.com/acme/v2/directory`
(少 `/v2` 直接 404);`meta.externalAccountRequired: true` 强制 EAB。
- **1Panel 必须用 `/api/v2/`**。v1 的表现极具欺骗性:**HTTP 200 但正文是 HTML 停用页**
(`Access Temporarily Unavailable`)——看似限流,实为 v1 已停用。
- **1Panel HTTPS 配置字段是 `SSL`(大写)**,写错不报错,只是 `cur?.ssl?.id === sslId` 永假
→ **每次续期都重绑、短暂 reload nginx**。
- **CSR 的 `extensionRequest` 只能套三层 SEQ**:`a0 <len> / 30 / 06 09…090e / 31 …`
多套一层会让 OpenSSL 拿 SAN 的 OID 当 attribute type 查表,报
`wrong tag ... Field=object, Type=X509_ATTRIBUTE`(这个 bug 会让所有签发失败)。
### 8.5 上线记录
- Worker version_id `1899e229-ca59-486a-a758-a6e2cbec0919`,路由 `api.200181.xyz`
- cron 三条:`17 3 * * *`(评论 GC)、`0 * * * *`(RSS)、`10 4 * * *`(**证书续期**)
- KV 已写入 8 条:7 条凭据(AES-GCM)+ 1 条 `certkeeper:config`(3 组域名、阈值 30 天、收件人)
- 新路由已注册上线的**铁证**:`GET /api/v2/ssl/renew-check` → **403**(鉴权拦截),
而瞎编路径 `/api/v2/ssl/__nope` → **404**。403 vs 404 是最干净的「路由存在性」证明。
### 8.6 待办
- **1Panel API 白名单**:代码侧已按官方 SDK 写好,但线上调用被「Access Temporarily Unavailable」拦。
1Panel 开了「安全登录」(入口 `/project`),需在 **面板 → 设置 → API 接口**确认已启用,
并把 Cloudflare Worker 出口 IP 加入白名单,否则 1panel 部署目标会失败。
(环境自检 `/ssl/selfcheck` 会如实报出这一点。)
- **Cloudflare DNS 凭据缺口**:现 `cloudflare` token 是 Workers/KV/D1 专用,
`GET /zones` 回 200 + 空数组、`dns_records` 直连 403 → **`200181.xyz` 无法走 DNS-01**。
需到 Cloudflare 重新生成含 `Zone → DNS → Edit` 且覆盖 `200181.xyz` 的 token。
- **真实端到端签发验证**(需 Paid plan + 用户批准):会真写 DNS TXT、消耗一次 CA 配额、重绑站点。