今天打开自己博客,浏览器弹了个大红页,大意是:
攻击者可能试图窃取您发送到 blog.zzr233.online 的信息。
好家伙,我攻击我自己。
一查,SSL 证书过期了,过期时间停在昨天下午。行,排查。
先看表面:证书确实没续上
curl 看了下证书,到期时间停在 10 月 14 日。按理说 acme.sh 每天都会自动续签,翻日志发现更有意思的:
- 新证书其实早就签好了(9 月 15 日续的,有效期到 12 月 14 日),但它压根没进 nginx
- nginx 里一直挂着 7 月的旧证书,旧的那张一到期,全站就爆了
- 每 6 小时一次的定时任务报一次
Install cert retry failed,报了一百多次,没人理它
再往下挖:三个坑叠在一起
坑一:证书"部署链"在迁移时断了
之前把 nginx 从 Docker 迁到宿主机,证书文件搬过来了,但 acme.sh 里「续签后把新证书装到哪」的配置没跟着改——还指着 Docker 时代已经删掉的目录,和一句 docker exec 的 reload 命令。
于是:续签正常执行,证书正常签发,然后部署环节静默失败。新证书躺在 acme.sh 目录里吃灰,nginx 继续用旧的。
坑二:另外三个域名其实也一直在失败
后台、主页那几个域名,acme 验证用的 webroot 还是旧路径,nginx 早改到新目录了——验证请求全部 404,从 9 月开始每次续签都失败。只是它们的旧证书还没到期,一直没炸出来。
坑三:一个 nginx 冷知识
修的时候发现主页配置里 return 301 写在了 server 层级。nginx 的规则:server 级的 return 先于 location 匹配执行——加在它下面的验证 location 形同虚设,验证请求也被 301 走了。
这个坑的直接表现是 Let's Encrypt 报 reader size limit exceeded,看着完全不像重定向的问题,找了半天。
修复
三个坑清掉,顺手把部署链整个重做了:
- 写了个部署脚本:复制证书 →
nginx -t检查 → reload,所有域名统一走它 - 四个域名全部重配
install-cert,指向脚本 - webroot 统一,nginx 跳转结构修正
现在的链路:cron 续签 → 自动部署 → 自动 reload。终于全自动了,不用再人工盯着。
留几个排障命令,以防复发
~/.acme.sh/acme.sh --list # 各域名状态 + 下次续签时间
grep 'Install cert retry\|Error renewing' ~/.acme.sh/acme.sh.log # 部署链断没断
openssl x509 -noout -dates -in /etc/nginx/ssl/xxx.pem # nginx 实际在用哪张教训
- 迁移架构,自动化链路也要一起迁,不只是搬文件
- 「续签成功」≠「部署成功」,日志里的
Install cert retry failed就是信号 - 证书这玩意不炸则以,一炸全站;到期前一个月没动静就该上去看看
好了,刷新一下,小锁图标回来了。