Let’s Encrypt 证书自动化续期:Certbot、ACME 协议与常见坑

Let's Encrypt 让 HTTPS 普及成为现实,但真正让运维团队头疼的往往不是首次签发,而是证书到期后的续期管理。一旦续期失败,浏览器会毫不留情地打出安全警告,直接影响业务访问。本文从 ACME 协议原理出发,结合 Certbot 的实战配置,梳理自动化续期中的常见问题和解决方案。

ACME 协议:自动化续期的基石

ACME(Automatic Certificate Management Environment)是 Let's Encrypt 使用的证书自动化协议,目前主流实现为 RFC 8555。它的核心思路是用一套标准化流程完成「证明域名控制权 → 提交 CSR → 下载证书」的全过程。

一次典型的 ACME 交互包含几个关键步骤:

  1. 账户注册:客户端生成密钥对,向 ACME 服务器注册账户。
  2. 申请订单:声明要签发的域名列表,服务器返回一组授权挑战(Challenge)。
  3. 完成挑战:证明你确实控制这些域名。常见挑战类型有:
    • http-01:在 http://域名/.well-known/acme-challenge/ 下放置指定内容,服务器通过 HTTP 访问验证。
    • dns-01:在域名下添加指定 TXT 记录,适合泛域名证书。
    • tls-alpn-01:通过 TLS 握手时的特殊扩展验证,使用相对较少。
  4. 提交 CSR 并下载证书:挑战通过后,提交证书签名请求,获取证书链。

理解这套流程很重要,因为绝大多数续期故障都可以定位到某个具体环节:挑战失败、DNS 未生效、HTTP 端口被拦截、CSR 域名不匹配等。

Certbot:最常用的 ACME 客户端

Certbot 由 EFF 维护,是 Let's Encrypt 官方推荐的客户端之一。它的设计目标是「让 HTTPS 配置尽可能简单」,同时支持自动续期。

安装与首次签发

以常见的 Nginx + Ubuntu 环境为例:

sudo apt update
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d example.com -d www.example.com

Certbot 会自动修改 Nginx 配置、申请证书并配置 443 端口。如果使用 --webroot 模式,则不会改动 Web 服务器配置,只负责签发:

sudo certbot certonly --webroot -w /var/www/html -d example.com

自动续期机制

Certbot 安装时会自动创建一个 systemd timer 或 cron 任务,通常每天运行两次:

systemctl list-timers | grep certbot
# 或查看 cron
cat /etc/cron.d/certbot

续期逻辑是:检查证书剩余有效期,若少于 30 天则尝试续期。这个「30 天阈值」是 Let's Encrypt 官方建议,既能避免频繁请求,又留出足够的失败重试窗口。

手动测试续期(不会真正签发):

sudo certbot renew --dry-run

这条命令应当成为上线前的必备检查项。

常见坑与解决方案

坑一:HTTP 挑战被重定向或拦截

http-01 验证要求 Let's Encrypt 能从公网访问 /.well-known/acme-challenge/ 路径。常见问题包括:

  • 网站强制 HTTPS 跳转,导致验证请求被 301 到 HTTPS 后失败;
  • CDN 或 WAF 拦截了未知路径的请求;
  • Nginx 配置中 location /.well-known/acme-challenge/ 被其他规则覆盖。

解决:在 Web 服务器中显式放行该路径,且不要做跳转:

location ^~ /.well-known/acme-challenge/ {
    root /var/www/html;
    default_type "text/plain";
}

若使用 CDN,需要确认回源规则不会改写或拦截该路径。

坑二:DNS 挑战的传播延迟

使用 dns-01 申请泛域名证书时,Certbot 需要等待 TXT 记录生效。如果 DNS 服务商 API 响应慢或 TTL 设置过长,验证会超时。

解决:使用支持 API 自动化的 DNS 插件(如 certbot-dns-cloudflare),并适当增加 --dns-propagation-seconds 等待时间。TTL 建议设置为 60 秒左右,验证完成后再调回。

坑三:续期成功但服务未重载

Certbot 续期后只是更新了证书文件,Nginx、Apache 等并不会自动加载新证书。如果只配置了 renew 而没有部署钩子,服务仍在用旧证书,直到手动重启。

解决:使用 --deploy-hook 在续期成功后重载服务:

sudo certbot renew --deploy-hook "systemctl reload nginx"

也可以在 /etc/letsencrypt/renewal/ 下的配置文件中加入 renew_hook

坑四:速率限制触发

Let's Encrypt 对同一域名有每周 50 张证书的签发限制,重复失败重试很容易触顶。--dry-run 使用的是 staging 环境,不受生产速率限制,调试阶段应优先使用。

坑五:权限与文件路径问题

Certbot 默认将证书存放在 /etc/letsencrypt/live/域名/,且私钥仅 root 可读。如果 Web 服务以非 root 用户运行,直接引用该路径会报权限错误。

解决:通过 --deploy-hook 将证书复制到服务可读的位置,并设置正确属主,而不是直接放宽 /etc/letsencrypt 的权限。

监控与兜底策略

再完善的自动化也可能因为网络、DNS、配置变更而失败。建议至少做两件事:

  1. 证书到期监控:用脚本或监控平台检查证书剩余天数,低于 14 天告警。
  2. 续期日志巡检:定期查看 /var/log/letsencrypt/letsencrypt.log,关注 renewal failure 关键字。

一个简单的到期检查脚本:

echo | openssl s_client -servername example.com -connect example.com:443 2>/dev/null \
  | openssl x509 -noout -enddate

小结

Let's Encrypt 的自动化续期并不复杂,但「配置一次就忘掉」的前提是把 ACME 挑战路径、DNS 传播、部署钩子和监控告警都处理妥当。理解 ACME 协议的分步流程,能让你在续期失败时快速定位是验证环节、网络环节还是部署环节出了问题。把 certbot renew --dry-run 纳入日常检查,配合到期监控,基本可以告别证书过期引发的线上事故。

未经允许不得转载:任鹏个人博客 » Let’s Encrypt 证书自动化续期:Certbot、ACME 协议与常见坑

赞 (0) 打赏

评论 0

取消
  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏