4、ACME协议与Let's Encrypt:从原理到自动续期
说到证书轮换,ACME 协议和 Let's Encrypt 是绕不开的话题。我个人觉得,这俩东西的出现,彻底改变了证书管理的游戏规则。以前申请个证书,填表、发邮件、等审核,折腾好几天。现在呢?几分钟搞定,还能自动续期。
说白了,ACME 就是一套让客户端和 CA(证书颁发机构)自动对话的协议。Let's Encrypt 是第一个大规模落地这套协议的 CA。你想想看,没有 ACME,我们哪来的免费证书自动续期?
核心认知:ACME 协议的核心价值在于「自动化」。它把证书申请、验证、签发、续期这整套流程,变成了机器与机器之间的标准对话。你只需要配好客户端,剩下的交给定时任务。
4.1 ACME 协议原理:到底在聊什么?
ACME 全称是 Automatic Certificate Management Environment。嗯,名字挺长,但逻辑其实很简单。它解决的核心问题就一个:如何证明你拥有某个域名。
我记得第一次看 ACME 的 RFC 时,觉得它设计得很巧妙。它把验证过程分成了几个清晰的步骤:
- 注册账号:客户端生成一对公私钥,在 CA 那边注册一个账户。
- 发起订单:客户端告诉 CA,我要为某个域名申请证书。
- 完成挑战:CA 给你出题,证明你确实控制这个域名。
- 签发证书:验证通过,CA 签发证书给你。
- 续期:证书快过期时,重复 2-4 步。
这里面最关键的是第三步——挑战。Let's Encrypt 支持两种主流挑战方式:
| 挑战类型 | 验证方式 | 适用场景 | 我的建议 |
|---|---|---|---|
| HTTP-01 | 在 Web 服务器根目录放一个特定文件 | 有公网 HTTP 服务的场景 | 最常用,配置简单 |
| DNS-01 | 在 DNS 解析记录里加一个 TXT 记录 | 通配符证书、无公网 IP 的场景 | 支持通配符,但需要 DNS API |
我的经验:如果你只需要单域名证书,优先用 HTTP-01。它不需要操作 DNS,速度快。但如果你要申请 *.example.com 这种通配符证书,那就只能用 DNS-01。我曾经在一个内网服务上折腾了半天 HTTP-01,最后发现根本行不通——因为 CA 访问不到我的内网服务器。
为什么会这样?因为 CA 验证时,是从公网发起请求的。你的服务器如果在内网,CA 根本找不到你。这时候 DNS-01 就派上用场了——它只验证 DNS 记录,不关心你的服务器在哪。
4.2 Certbot 客户端:最常用的 ACME 客户端
Certbot 是 Let's Encrypt 官方推荐的客户端。我用过不少 ACME 客户端,比如 acme.sh、lego,但 Certbot 的生态最完善,文档也最全。
安装 Certbot 很简单。以 Ubuntu 为例:
# 安装 Certbot 和 Nginx 插件
sudo apt update
sudo apt install certbot python3-certbot-nginx
# 或者用 Snap(官方推荐方式)
sudo snap install certbot --classic
sudo snap set certbot trust-plugin-with-root=ok
sudo snap install certbot-dns-cloudflare # 如果需要 DNS 插件
我个人习惯用 Snap 版本,因为它会自动更新,省得我手动维护。嗯,这里要注意:如果你用 apt 安装,版本可能比较旧,建议用 Snap 或直接从 GitHub 下载。
4.2.1 申请证书:一行命令搞定
申请证书真的就是一行命令的事。假设你用的是 Nginx:
# 自动检测 Nginx 配置并申请证书
sudo certbot --nginx -d example.com -d www.example.com
这条命令会做几件事:
- 自动修改 Nginx 配置,添加 SSL 相关指令
- 完成 HTTP-01 挑战
- 下载证书并保存到
/etc/letsencrypt/live/example.com/ - 重载 Nginx 使配置生效
如果你不想让 Certbot 自动修改 Nginx 配置,可以用 certonly 模式:
# 只申请证书,不修改 Web 服务器配置
sudo certbot certonly --nginx -d example.com
证书申请成功后,你会看到类似这样的输出:
IMPORTANT NOTES:
- Congratulations! Your certificate and chain have been saved at:
/etc/letsencrypt/live/example.com/fullchain.pem
Your key file has been saved at:
/etc/letsencrypt/live/example.com/privkey.pem
- Your certificate will expire on 2025-06-15.
避坑指南:我曾经遇到过一个问题——申请证书时提示「域名验证失败」。排查了半天,发现是 Nginx 的防火墙没放行 80 端口。CA 来验证时,根本访问不到我放的那个文件。记住:HTTP-01 挑战需要 80 端口可访问,DNS-01 挑战需要 DNS API 权限。这两个前提条件一定要先确认好。
4.2.2 证书文件结构
申请成功后,Certbot 会在 /etc/letsencrypt/live/你的域名/ 下生成四个文件:
| 文件 | 内容 | 用途 |
|---|---|---|
cert.pem |
服务器证书(不含中间证书) | 一般不用,除非你手动拼接 |
chain.pem |
中间证书链 | 与 cert.pem 拼接使用 |
fullchain.pem |
cert.pem + chain.pem | Nginx/Apache 配置中用的证书文件 |
privkey.pem |
私钥 | 必须保密,权限设为 600 |
你配置 Web 服务器时,通常只需要两个文件:fullchain.pem 和 privkey.pem。Nginx 配置示例:
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# 其他配置...
}
4.3 自动续期配置:一劳永逸
Let's Encrypt 的证书有效期是 90 天。嗯,你没看错,只有 90 天。为什么这么短?说白了就是为了推动自动化。你想想看,如果证书有效期是一年,很多人可能就忘了续期。90 天的话,你必须用自动化工具来管理。
Certbot 自带续期功能。你只需要设置一个定时任务:
# 测试续期是否正常工作
sudo certbot renew --dry-run
# 如果测试通过,设置 crontab 定时任务
sudo crontab -e
# 添加以下行(每天凌晨 3 点检查续期)
0 3 * * * /usr/bin/certbot renew --quiet --post-hook "systemctl reload nginx"
这里有几个关键点:
--quiet:静默模式,只有出错时才输出日志--post-hook:续期成功后执行的命令,通常是重载 Web 服务器- Certbot 会自动判断哪些证书需要续期(距离过期不足 30 天)
我的习惯:我不会只依赖 crontab。我会再加一个 systemd timer,这样日志管理更方便。而且我会在续期脚本里加一个健康检查——续期后自动访问一下 HTTPS 页面,确认证书生效。如果访问失败,发告警通知。这样即使续期出问题,我也能第一时间知道。
3.3.1 续期原理:为什么能自动续期?
自动续期的核心在于 ACME 协议的「无状态」设计。CA 不保存你的证书状态,每次续期都是重新走一遍申请流程。但 Certbot 会保存你的账户密钥和之前的挑战记录,所以续期时不需要你重新配置。
具体流程是这样的:
- Certbot 检查本地证书,发现距离过期不足 30 天
- 用保存的账户密钥向 CA 发起续期请求
- CA 重新验证域名所有权(再次完成挑战)
- 签发新证书,覆盖旧证书文件
- 执行 post-hook,重载 Web 服务器
整个过程完全自动化,你不需要任何人工干预。
4.4 核心流程可视化
下面这张图展示了 ACME 协议从申请到续期的完整流程。我把它画出来,方便你理解整个生命周期。
4.5 常见问题与避坑
最后,分享几个我实际踩过的坑:
- 续期失败不告警:默认的 crontab 配置是静默模式,续期失败不会发邮件。我建议加上告警机制,比如续期失败后发 Slack 通知或邮件。
- 证书文件权限:
privkey.pem必须只有 root 可读。我曾经遇到过权限设置不对,导致 Nginx 启动失败。 - 多域名证书:如果你有多个域名,建议每个域名单独申请证书,而不是用 SAN 证书。这样某个域名出问题时,不影响其他域名。
- DNS 插件兼容性:如果你用 DNS-01 挑战,一定要确认你的 DNS 服务商有对应的 Certbot 插件。Cloudflare、AWS Route53、阿里云都有插件,但一些小的 DNS 服务商可能没有。
重要提醒:千万不要手动修改 /etc/letsencrypt/live/ 下的证书文件。Certbot 续期时会自动覆盖这些文件。如果你手动改了,续期后会被覆盖掉。如果你需要自定义证书路径,用 --deploy-hook 把证书复制到目标位置。