Hotdry.
ai-security

拆解 Let's Encrypt 10 年零人工续期背后的自动化证书流水线与失效回滚策略

从 90 天到 45 天生命周期,Let's Encrypt 把‘续期’逼成‘高频运维’。本文给出可落地的原子快照、灰度 reload、OCSP 超时回滚三段式方案,让证书在 10 年里真正零人工。

2028 年起,Let's Encrypt 将把默认有效期从 90 天压缩到 45 天,授权复用期缩短至 7 小时。这意味着一张证书的黄金续期窗口只剩 30 小时左右 —— 一旦错过,服务就会在 1.8 天后报警。10 年零人工续期不是口号,而是一场对 “失败 - 感知 - 回滚” 速度极限的持续压测。

一、把续期做成流水线,而不是定时任务

传统做法是在 crontab 里写一句 certbot renew,再配一个 --post-hook "nginx -s reload"。这套脚本在 90 天时代可以 “跑就行”,但在 45 天时代会暴露三个短板:

  1. 失败感知慢:certbot 默认随机 12h 偏移,失败后再等 1 天重试,窗口直接耗尽。
  2. 无回滚点:新证书写入 /etc/letsencrypt/live 后旧文件立即消失,失败即裸奔。
  3. 无法灰度:reload 是全 or nothing,一旦新证书握手异常,全站 TLS 直接降级。

流水线思路是把 “签发→备份→灰度→全量” 做成不可拆分的四阶段,任何一步异常立即回到上一快照。

二、原子快照:把证书变成 “可回滚的部署单元”

Let's Encrypt 的本地文件结构是 live/域名 -> ../../archive/域名/序号_{cert,chain,fullchain,privkey}.pem。序号每续期一次 +1,天然适合用硬链接做零拷贝快照。

# 在成功签发后触发
SNAP=$(date +%s)
cp -al /etc/letsencrypt /var/letsencrypt-snap/${SNAP}
# 保留最近 7 份,约 1 年历史
ls -t /var/letsencrypt-snap/ | tail -n +8 | xargs -r rm -rf

快照目录只读,且与 live 目录在同一挂载点,回滚耗时 <1s。注意把 /archive 一起打包,否则只有符号链接会指向空气。

三、触发策略:ARI 信号 + 30% 生命周期双保险

ACME Renewal Info(ARI)是 Let's Encrypt 2023 年推出的官方 “续期信号灯”。客户端访问 https://acme-v02.api.letsencrypt.org/get/draft-ietf-acme-ari-01/... 即可拿到建议续期时间,比固定倒计时更贴合 CA 侧负载。

  • 若 ARI 返回 suggestedRetryDate,在该时间点 ±6h 内发起续期。
  • 若客户端未实现 ARI,则退回到证书剩余 30% 生命周期(45 天制即 13 天)。

双保险保证:即使 ARI 接口 500,系统仍能在证书失效前两周自动续期。

四、灰度 reload:先边缘、再核心、最后全网

  1. 从 Service Mesh 或负载均衡器摘出一台边缘节点,执行 certbot certonly --csr xxx --server xxx 获得新证书。
  2. 在该节点执行 nginx -s reload,用 Prometheus + Blackbox Exporter 检查 ssl_handshake_ok 指标 5min 无异常。
  3. 若无异常,把快照版本号写入 Consul/KV,剩余节点逐台拉取新证书并 reload;若异常,执行回滚脚本。

回滚脚本只做三件事:

systemctl stop nginx
cp -al /var/letsencrypt-snap/${LAST_GOOD_SNAP}/* /etc/letsencrypt
systemctl start nginx

全程 <3s,且无需重新申请旧证书 —— 旧证书仍在快照里有效。

五、失效判定与阈值

场景 判定条件 回滚阈值
OCSP 超时 续期时 curl -m 30 返回 28 连续 2 次
订单失败 ACME 返回 429/500 连续 2 次
握手异常 Blackbox 探测失败率 >1% 单次
ARI 延迟 retry-after >6h 单次即告警,不阻断

所有阈值均通过 OpenTelemetry 统一上报,对接 Alertmanager:

- alert: CertRollback
  expr: certbot_last_success{} < unixtime() - 3600*2
  for: 0m
  annotations:
    summary: "证书续期连续失败 2 次,已触发快照回滚"

六、2028 年时间表如何适配

阶段 生效日期 证书有效期 授权复用期 建议续期触发
当前 2025 90 天 30 天 剩余 30% ≈ 27 天
中间 2027-02 64 天 10 天 剩余 30% ≈ 19 天
终态 2028-02 45 天 7 小时 ARI 信号 + 13 天保底

授权复用期缩到 7h 后,域名验证必须每次重新做 TXT 或 HTTP 挑战,无法再利用 30 天缓存。此时 DNS-01 挑战的 API 速率成为新瓶颈,需提前在 DNS 服务商侧申请白名单或做 Token 池化。

七、一行命令自检

把下面脚本丢到巡检系统,每天跑一次即可:

openssl x509 -checkend $((45*24*3600/2)) -noout -in /etc/letsencrypt/live/$DOMAIN/fullchain.pem || echo "证书不足 50% 生命周期,请检查续期流水线"

失败即说明续期逻辑已卡死,快照回滚也救不了,需要人工介入。

八、小结

10 年零人工续期的本质是把 “证书” 当成普通交付物:可版本化、可灰度、可回滚。Let's Encrypt 把生命周期压到 45 天,反而让这场演练更有价值 —— 只要快照回滚链足够快,任何 CA 侧故障都只是触发一次自动「撤销 - 重签」的小波动,而不会再演变成凌晨三点的全站救火。

参考

  1. 天威诚信《Let's Encrypt 免费证书用户请注意:你的证书可能已经无法签发 / 更新》
  2. Sherlock《群晖 NAS Let's Encrypt 泛域名证书自动更新》
  3. Let’s Encrypt 官方时间表《ACME Profiles and 45-Day Certificates》
查看归档