Let's Encrypt 证书自动化:certbot deploy-hook 同步宝塔
摘要
家里 5 个公网域名(blog、img、file、pwd、nav),每个都要 HTTPS,每个证书 90 天过期,手动续期早晚会忘。这篇记录 Let's Encrypt 的全自动化:先一分钟讲透 ACME 协议(证书是怎么签出来的),再讲 certbot 的签发与自动续期,最后讲 deploy-hook——续期后自动把证书同步到宝塔面板的 cert 目录并 reload nginx。目标:证书这件事,从此不需要人想起来。
目录
- 一、ACME 协议一分钟:证书是怎么签出来的
- 二、签发:certbot 一把梭
- 三、自动续期:certbot 的定时任务
- 四、deploy-hook:续期后自动同步宝塔
- 五、全家证书清单
- 六、踩坑记录
- 七、总结
一、ACME 协议一分钟:证书是怎么签出来的
Let's Encrypt 的签发走 ACME 协议,流程三步:
- 证明你是域名的控制者:certbot 在你的服务器上放一个随机文件(HTTP-01 挑战),Let's Encrypt 从公网访问
http://你的域名/.well-known/acme-challenge/<随机串>,能取到就证明你控制这个域名。或者走 DNS-01:在 DNS 里加一条 TXT 记录。 - 签发:验证通过,CA 给你签 90 天的证书。
- 续期:90 天快到了,重新走一遍。certbot 的定时任务每天跑两次,证书剩 30 天就自动续。
HTTP-01 要求 80 端口公网可达(CA 要从公网取挑战文件),DNS-01 要求 DNS 服务商有 API。这套环境用 HTTP-01:5 个域名都指到 VPS,80 端口正常服务,certbot 的 nginx 插件自动处理挑战文件的放置和清理。
理解 ACME 的意义:证书续期失败,90% 是"挑战失败"——80 端口被挡了、nginx 配置把 .well-known 拦了、域名解析变了。排查续期问题,先看挑战那一步。
二、签发:certbot 一把梭
# nginx 插件签发(自动改 nginx 配置、自动 reload)
certbot --nginx -d blog.028000.xyz
# 5 个域名一次签完也行
certbot --nginx -d blog.028000.xyz -d img.028000.xyz -d file.028000.xyz \
-d pwd.028000.xyz -d nav.028000.xyz
certbot 的 nginx 插件会自动:放挑战文件、验证、签证书、改 nginx 配置加上 ssl_certificate、reload nginx。全程无手工。签完确认:certbot certificates 看有效期,浏览器看小锁。
三、自动续期:certbot 的定时任务
certbot 安装时自带 systemd timer(或 cron),每天跑两次:
# 看续期任务
systemctl list-timers | grep certbot
# 手动 dry-run 测试续期流程(不真续)
certbot renew --dry-run
certbot renew 每天跑,证书剩余 30 天才真续。--dry-run 是上线前的必测:走一遍挑战流程,确认 80 端口、nginx 配置、域名解析都没问题。dry-run 过了,真续期才放心。
四、deploy-hook:续期后自动同步宝塔
这套环境有个特殊需求:VPS 上跑着宝塔面板,面板的 cert 目录里也有一份证书(面板自己的 HTTPS 用)。certbot 续期只更新 /etc/letsencrypt/,宝塔那份不会自动跟新,旧证书过期了面板就报不安全。
解法:certbot 的 --deploy-hook,续期成功后自动执行:
# /etc/letsencrypt/renewal-hooks/deploy/sync-bt.sh(示意)
#!/bin/bash
# $RENEWED_DOMAINS 是 certbot 传进来的续期域名
# 把新证书拷贝到宝塔 cert 目录,并 reload nginx
cp /etc/letsencrypt/live/$RENEWED_DOMAINS/fullchain.pem /www/server/panel/ssl/certificate.pem
cp /etc/letsencrypt/live/$RENEWED_DOMAINS/privkey.pem /www/server/panel/ssl/privateKey.pem
nginx -s reload
deploy-hook 只在"真续期成功"时触发,dry-run 不触发。逻辑:续期 → 拷贝到宝塔 → reload nginx,全自动。以后证书过期这种事,彻底不需要人想起来。
五、全家证书清单
| 域名 | 用途 | 有效期至 | 续期 |
|---|---|---|---|
| blog.028000.xyz | Halo 博客 | 2026-12-30 | certbot 自动 |
| img.028000.xyz | 图床 | 2026-12-30 | certbot 自动 |
| file.028000.xyz | 文件站 | 自动 | certbot 自动 |
| pwd.028000.xyz | 密码库 | 2026-12-30 | certbot 自动 |
| nav.028000.xyz | 导航页 | 2026-12-30 | certbot 自动 |
| 028000.xyz | WordPress 主站 | 2026-11-08 | 待处理(审计发现 11 月 8 日到期) |
注意最后一行:WordPress 主站的证书 11 月 8 日到期,是审计发现的待办。Let's Encrypt 续期是自动的,但"自动"的前提是续期任务正常、挑战能通过。到期前手动 certbot renew --dry-run 验证一遍,别等过期了才发现挑战失败。
六、踩坑记录
坑 1:.well-known 被 nginx 配置拦了
有次 nginx 加了全局的 location 限制,把 /.well-known/ 也挡了,续期挑战 404。教训:nginx 配置里给 /.well-known/acme-challenge/ 留白,任何全局限制都要排除它。
# 续期挑战路径,永远放行
location ^~ /.well-known/acme-challenge/ {
allow all;
}
坑 2:宝塔的证书和 certbot 的证书不一致
续期后宝塔面板报证书过期,查下来是宝塔用的还是旧证书。根因:两份证书,certbot 只更新自己那份。解法就是 deploy-hook 同步。教训:一台机器上如果有两处用证书,续期后都要同步,列个清单,别漏。
坑 3:dry-run 没测,真续期才发现 80 端口被挡
防火墙调过一次,80 端口忘了放行,dry-run 也没跑。续期当天失败,证书差点过期。教训:改防火墙后跑一遍 certbot renew --dry-run,把续期验证纳入防火墙变更的检查清单。
DNS-01 挑战:HTTP-01 不可用时
HTTP-01 要求 80 端口公网可达,如果站点在内网(比如纯内网的 Stirling PDF 也想上 HTTPS),就用 DNS-01:在 DNS 里加一条 TXT 记录证明域名控制权。
# DNSPod 的 API 做 DNS-01(需 certbot-dns-dnsPod 插件)
certbot certonly --dns-dnspod \
--dns-dnspod-credentials /root/.secrets/dnspod.ini \
-d inner.028000.xyz
DNS-01 的 API 密钥是敏感信息,放 /root/.secrets/(600 权限),不进版本库。内网服务的证书也可以这么签,签完分发到内网机器。HTTP-01 和 DNS-01 按场景选:公网服务用 HTTP-01(简单),内网服务用 DNS-01(不需要 80 端口)。
证书监控:别等过期才发现
自动续期不是"配完就忘",要监控:
| 监控项 | 做法 |
|---|---|
| 续期任务存活 | systemctl list-timers 看 certbot timer 正常 |
| 证书有效期 | Uptime Kuma 加 HTTPS 证书过期监控,剩 15 天告警 |
| dry-run 定期跑 | 改防火墙/nginx 后跑一遍,确认挑战能过 |
Uptime Kuma 的证书监控是最后一道防线:就算续期挂了,过期前 15 天也能收到告警。证书过期是"本可避免的故障",监控把它变成"不可能发生的故障"。
证书的生命周期:从签发到吊销
证书不是"签完就完",它有完整的生命周期:签发 → 部署 → 续期 → 吊销(出事时)→ 归档。自动化覆盖了中间三步,但两头要心里有数。
吊销是最少用、但最关键的一步。如果私钥泄露(服务器被入侵、备份外泄),要立刻吊销证书,让浏览器不再信任它。Let's Encrypt 的吊销走 ACME,一条命令的事,但前提是你还控制着私钥或域名。吊销之后,重新签发、重新部署,全链条再走一遍。希望永远用不上,但流程要记在小本本上。私钥泄露的应急,第一步永远是吊销证书,第二步才是查入侵,两件事别搞反了顺序——先止血,再查病因。
归档是容易被忽略的一步。每次续期,旧证书其实可以留一份(带日期),存在 NAS 上。作用是"回滚":新证书配错了(比如 deploy-hook 同步时拷错了文件),5 分钟内能拿旧证书顶回去。归档不占地方,关键时刻省大事。和备份一个道理:用不上的时候觉得多余,用上的时候救命。
透明度日志是个冷知识:所有公开信任的证书签发,都会被记进 Certificate Transparency 日志,任何人都能查。这意味着"悄悄给别人的域名签证书"是不可能的,签发即公开。对自建服务来说,CT 日志是个免费审计:如果有人冒你的域名签了证书,CT 日志里能看到。crt.sh 这个网站可以查任意域名的签发记录,定期搜一下自己的域名,是个好习惯。
证书自动化做到最后,是"三不":不用想起来(自动续期)、不用守着(deploy-hook 同步)、不用怕出事(吊销流程在小本本上)。Let's Encrypt 把"签发"变成了免费和自动化,我们要做的,是把"生命周期管理"也变成自动化和流程化。证书这件事,目标永远是"从此不需要人想起来",但"想不起来"的前提,是"出事时有预案"。
通配符证书:一个证书管所有子域名
5 个子域名(blog、img、file、pwd、nav),现在是一个域名一张证书(或一张多域名证书)。另一种方案是通配符证书:*.028000.xyz 一张,管所有子域名。
| 方案 | 优点 | 缺点 |
|---|---|---|
| 单域名/多域名证书 | HTTP-01 挑战,简单 | 域名多了管理麻烦 |
| 通配符证书 | 一张管所有,新加子域名不用重签 | 必须 DNS-01 挑战(HTTP-01 签不了通配符) |
通配符必须走 DNS-01,因为 CA 要确认你控制整个域,而 HTTP-01 只能证明单个主机。DNS-01 需要 DNS 服务商的 API(DNSPod 有),certbot 配 certbot-dns-dnspod 插件,密钥放 /root/.secrets/。
选哪个?子域名少(5 个以内)、不常加,单域名证书简单直接;子域名多、经常加(比如每个应用一个子域名),通配符省心。这套环境目前 5 个,单域名够用。但如果以后"每个新应用一个子域名",通配符是迟早的事。方案可以演进,不用一次到位。
通配符还有个安全细节:一张证书管所有子域名,私钥泄露的影响面是"所有子域名"。单域名证书泄露,只影响那一个。所以通配符的私钥保护级别要更高,deploy-hook 同步时多检查一遍权限。方便和安全的权衡,无处不在。
证书链:fullchain 和 cert 的区别
配 nginx 时,ssl_certificate 指向的是 fullchain.pem,不是 cert.pem。区别:cert.pem 只有你的证书,fullchain.pem 是"你的证书 + 中间证书"。浏览器验证时,要从你的证书一路验证到根证书,中间证书就是这条链上的一环。
如果配了 cert.pem(缺中间证书),部分浏览器会报"证书不可信",部分能自己补全(AIA 追踪),表现不一致,排查很烦。配 fullchain,一步到位。Let's Encrypt 的中间证书偶尔会换(比如从 X3 换到 R3),certbot 续期时自动带新的,不用操心。记住:nginx 的 ssl_certificate 永远指 fullchain,这是铁律。
证书链验证失败,还有个常见原因:服务器时间不对。证书有"生效期",服务器时间飘到 2020 年,证书"还没生效",握手失败。NTP 对时是基础设施,timedatectl 看一眼,差几秒没事,差几天就有事。时间不对,证书、TOTP、Kerberos,全崩。NTP 是隐形地基。
证书自动化的最后一步,是"忘记它"。配好续期、配好 hook、配好监控,然后忘掉证书这件事。好的基础设施,都是"被遗忘"的。哪天你想起证书,不是因为它过期了,而是因为看到这篇文章。那就对了。
另外,私钥的权限永远是 600,目录 700。证书是公开的(CT 日志里全有),私钥是命。公开发布证书没关系,私钥多看一眼权限。这条适用于所有 TLS 场景,不只 Let's Encrypt。WordPress 主站证书 11 月 8 日到期,提前 dry-run 验证,别等过期。到期前两周,cron 会发提醒,提醒来了就去看,别拖。续期成功后,顺手 curl 一下站点,确认新证书生效了,别只信 cron 的 exit 0。退出码为 0 不代表成功,这是全套 homelab 的通用铁律,证书这里也一样。验证三步:看证书日期(openssl s_client)、看站点 200、看浏览器小锁头。三步过,才算续期闭环。闭环了,才能忘。忘,是自动化的最高境界。 证书这事,还有个"监控"的维度:Uptime Kuma 可以监控证书有效期,提前 14 天告警。cron 的提醒邮件可能进垃圾箱,Uptime Kuma 的推送更显眼。双保险:cron 发邮件,Kuma 推手机。证书过期是 P0 事故(全站 HTTPS 挂),P0 的监控,值得两层。配一次,管一年。到期前 14 天收到推送,从容续期,这就是自动化的体面。体面,是给未来的自己最好的礼物。未来的你,会感谢现在配监控的你。证书的事,就到这。到这,不是结束,是"不用再想起"。不用再想起,就是自动化修成了正果。正果修成,证书这事,翻篇。翻篇后,记得 Uptime Kuma 里加上证书有效期监控。监控加上,双保险才算齐。
七、总结
证书自动化三层:certbot 签发(nginx 插件一把梭)、自动续期(systemd timer + dry-run 验证)、deploy-hook 同步宝塔(续期后自动拷贝 + reload)。理解 ACME 挑战原理,排查续期失败先看挑战那一步。两个清单:全家证书清单(到期时间)、"哪些地方用了证书"清单(续期后逐个同步)。目标:证书过期这种事,永远不需要人想起来。待办:WordPress 主站证书 11 月 8 日到期,提前 dry-run 验证。