强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Certbot 证书自动化教程 / 第 8 章:自动续期

第 8 章:自动续期

8.1 为什么要自动续期

Let’s Encrypt 证书有效期仅 90 天,过期后浏览器会显示安全警告,严重影响用户体验和网站信誉。手动续期容易遗忘,因此自动化续期是生产环境的必备要求。

证书生命周期

签发 ────────── 60天 ────── 续期窗口 ── 30天 ── 到期
│                            │                  │
│                            │ Certbot 自动     │
│                            │ 尝试续期          │
│                            │                  │
│<── 正常使用期 ─────────────>│<─ 续期尝试期 ────>│

续期时机

策略说明建议
默认(30 天前)到期前 30 天自动续期✅ 推荐
自定义天数-- renew-before-days 14按需
立即续期certbot renew --force-renewal仅紧急情况

注意: 不要过早续期,会消耗 Let’s Encrypt 的速率限制。

8.2 Certbot 续期命令

手动续期

# 续期所有即将到期的证书
sudo certbot renew

# 续期特定证书
sudo certbot renew --cert-name example.com

# 强制续期(即使未到期)
sudo certbot renew --force-renewal

# 模拟续期(dry-run)
sudo certbot renew --dry-run

续期行为

# 查看所有证书的到期时间
sudo certbot certificates

输出示例:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Found the following certs:
  Certificate Name: example.com
    Serial Number: 1234567890abcdef1234567890abcdef
    Key Type: ECDSA
    Domains: example.com www.example.com
    Expiry Date: 2025-08-10 12:00:00+00:00 (VALID: 60 days)
    Certificate Path: /etc/letsencrypt/live/example.com/fullchain.pem
    Private Key Path: /etc/letsencrypt/live/example.com/privkey.pem
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

续期配置文件

每个证书的续期配置存储在 /etc/letsencrypt/renewal/ 目录下:

cat /etc/letsencrypt/renewal/example.com.conf
# /etc/letsencrypt/renewal/example.com.conf
[renewalparams]
authenticator = nginx
account = <account-id>
server = https://acme-v02.api.letsencrypt.org/directory
key_type = ecdsa

[[lineage]]
cert = /etc/letsencrypt/live/example.com/cert.pem
privkey = /etc/letsencrypt/live/example.com/privkey.pem
chain = /etc/letsencrypt/live/example.com/chain.pem
fullchain = /etc/letsencrypt/live/example.com/fullchain.pem

8.3 systemd Timer(推荐)

systemd timer 是现代 Linux 系统推荐的定时任务管理方式,比 cron 更灵活、可靠。

检查默认 systemd timer

# Snap 安装的 Certbot 会自动创建 systemd timer
sudo systemctl list-timers | grep certbot

# 查看 timer 详情
sudo systemctl cat certbot.timer
sudo systemctl cat certbot.service

Certbot 默认 systemd timer

# /lib/systemd/system/certbot.timer
[Unit]
Description=Certbot timer

[Timer]
# 每天随机两次检查
OnCalendar=*-*-* 00,12:00:00
RandomizedDelaySec=43200
Persistent=true

[Install]
WantedBy=timers.target
# /lib/systemd/system/certbot.service
[Unit]
Description=Certbot
Documentation=file:///usr/share/doc/certbot/manual/html/index.html
Documentation=https://certbot.eff.org/docs

[Service]
Type=oneshot
ExecStart=/usr/bin/certbot -q renew
# 注意:-q 参数表示安静模式,仅在有错误或成功续期时输出

启用 systemd timer

# 启用 timer
sudo systemctl enable certbot.timer

# 启动 timer
sudo systemctl start certbot.timer

# 查看 timer 状态
sudo systemctl status certbot.timer

# 查看下次运行时间
sudo systemctl list-timers certbot.timer

自定义 systemd timer

# 创建自定义 timer
sudo tee /etc/systemd/system/certbot-custom.timer > /dev/null << 'EOF'
[Unit]
Description=Certbot renewal custom timer

[Timer]
# 每天凌晨 2:30 和下午 2:30 各检查一次
OnCalendar=*-*-* 02,14:30:00
RandomizedDelaySec=3600
Persistent=true

[Install]
WantedBy=timers.target
EOF

# 创建对应的 service
sudo tee /etc/systemd/system/certbot-custom.service > /dev/null << 'EOF'
[Unit]
Description=Certbot renewal custom service

[Service]
Type=oneshot
ExecStart=/usr/bin/certbot -q renew
PrivateTmp=true
EOF

# 启用自定义 timer
sudo systemctl daemon-reload
sudo systemctl enable certbot-custom.timer
sudo systemctl start certbot-custom.timer

自定义 systemd timer 参数说明

参数说明示例
OnCalendar定时触发规则*-*-* 02:00:00(每天凌晨 2 点)
RandomizedDelaySec随机延迟(秒)43200(最多延迟 12 小时)
Persistent错过的任务是否补执行true
OnBootSec开机后多久触发5min
OnUnitActiveSec上次触发后多久再次触发12h

8.4 Cron 定时任务

使用 cron 进行续期

# 编辑 root 的 crontab
sudo crontab -e

添加以下内容:

# 每天凌晨 2 点和下午 2 点各执行一次续期
0 2,14 * * * certbot -q renew

Cron 表达式说明

分 时 日 月 周 命令
│  │  │  │  │  │
│  │  │  │  │  └── 命令
│  │  │  │  └──── 星期几 (0-7, 0和7都是周日)
│  │  │  └────── 月份 (1-12)
│  │  └──────── 日 (1-31)
│  └────────── 小时 (0-23)
└──────────── 分钟 (0-59)

推荐的 Cron 配置

# 每天凌晨 3:00 执行续期,3:05 重启 Web 服务器
0 3 * * * certbot -q renew --deploy-hook "systemctl reload nginx"
5 3 * * * systemctl reload nginx

# 每天随机时间执行(避免与其他人同时请求)
# 使用 RANDOM_DELAY
RANDOM_DELAY=30
0 2 * * * certbot -q renew

Cron vs systemd timer

特性Cronsystemd Timer
安装复杂度简单中等
随机延迟需要脚本支持原生 RandomizedDelaySec
错过补执行Persistent=true
日志集成需要手动配置自动集成 journald
依赖管理支持 After/Requires
适用场景简单需求推荐

8.5 续期钩子

续期钩子(Hooks)允许在续期过程中执行自定义操作,如重启 Web 服务器、发送通知等。

命令行钩子

# 续期前执行(pre-hook)
sudo certbot renew --pre-hook "systemctl stop nginx"

# 续期后执行(post-hook)
sudo certbot renew --post-hook "systemctl start nginx"

# 续期成功后执行(deploy-hook)
sudo certbot renew --deploy-hook "systemctl reload nginx"

# 组合使用
sudo certbot renew \
  --pre-hook "systemctl stop nginx" \
  --post-hook "systemctl start nginx" \
  --deploy-hook "systemctl reload nginx"

钩子类型对比

钩子类型执行时机执行条件典型用途
--pre-hook续期尝试前每次都执行停止 Web 服务器(Standalone)
--post-hook续期尝试后每次都执行启动 Web 服务器
--deploy-hook证书更新成功后仅续期成功时重载 Web 服务器、发送通知

在续期配置文件中设置钩子

# /etc/letsencrypt/renewal/example.com.conf
[renewalparams]
authenticator = webroot
webroot_path = /var/www/certbot

[renewalhooks]
pre_hook = /usr/local/bin/certbot-pre-renew.sh
post_hook = /usr/local/bin/certbot-post-renew.sh
deploy = /usr/local/bin/certbot-deploy.sh

使用 hook 脚本目录

# 创建钩子脚本目录
sudo mkdir -p /etc/letsencrypt/renewal-hooks/{pre,post,deploy}

# 在对应目录下放置脚本(自动执行)
sudo tee /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh > /dev/null << 'EOF'
#!/bin/bash
systemctl reload nginx
echo "[$(date)] Nginx reloaded after certificate renewal" >> /var/log/certbot-hooks.log
EOF

sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh

8.6 续期测试

Dry Run 测试

# 测试所有证书的续期流程(不实际续期)
sudo certbot renew --dry-run

# 输出示例:
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Processing /etc/letsencrypt/renewal/example.com.conf
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Simulating renewal of an existing certificate for example.com
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# The dry run was successful.

测试特定证书

sudo certbot renew --dry-run --cert-name example.com

测试钩子脚本

# 测试续期并执行钩子
sudo certbot renew --dry-run \
  --deploy-hook "echo 'Deploy hook executed'"

# 手动测试钩子脚本
sudo /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh

定期测试建议

# 每月执行一次 dry-run 测试(可以放在 cron 中)
0 4 1 * * certbot renew --dry-run 2>&1 | tee -a /var/log/certbot-test.log

8.7 续期日志

日志位置

日志文件内容
/var/log/letsencrypt/letsencrypt.logCertbot 主日志
/var/log/letsencrypt/letsencrypt.log.1上一次的日志(轮转)
journald (systemd)systemd timer/service 日志

查看日志

# 查看 Certbot 日志
sudo cat /var/log/letsencrypt/letsencrypt.log

# 查看最近的日志
sudo tail -n 50 /var/log/letsencrypt/letsencrypt.log

# 查看 systemd timer 日志
sudo journalctl -u certbot.service

# 查看最近的 timer 运行记录
sudo journalctl -u certbot.timer --since "1 week ago"

# 实时查看
sudo journalctl -fu certbot.timer

日志轮转

Certbot 自带日志轮转配置,可在 /etc/letsencrypt/cli.ini 中调整:

# 保留最近 10 个日志文件
max-log-backups = 10

8.8 续期失败排查

常见失败原因

错误原因解决方案
端口 80 被占用其他服务占用了 80 端口配置 pre/post hook 停止/启动服务
防火墙阻止80 端口不可达检查防火墙规则
域名解析错误DNS 记录变更检查域名解析
速率限制短时间内申请太多证书等待限制重置或使用 staging
配置文件损坏续期配置被修改检查 /etc/letsencrypt/renewal/
Web 服务器未运行Nginx/Apache 未启动确保服务正常运行
权限不足脚本权限问题检查脚本权限和 SELinux

排查步骤

# 1. 查看证书状态
sudo certbot certificates

# 2. 执行 dry-run 测试
sudo certbot renew --dry-run

# 3. 查看详细日志
sudo certbot renew --dry-run --debug-challenges

# 4. 检查续期配置
cat /etc/letsencrypt/renewal/example.com.conf

# 5. 检查 Web 服务器状态
sudo systemctl status nginx
sudo nginx -t

手动修复续期配置

# 如果续期配置损坏,可以删除并重新创建
sudo rm /etc/letsencrypt/renewal/example.com.conf

# 重新申请证书(会自动创建续期配置)
sudo certbot --nginx -d example.com

8.9 续期最佳实践

  1. 每天执行两次续期检查: 早晚各一次,使用随机延迟
  2. 使用 systemd timer: 比 cron 更可靠,支持日志和依赖管理
  3. 定期 dry-run 测试: 每月至少一次,确保续期流程正常
  4. 配置 deploy-hook: 续期成功后自动重载 Web 服务器
  5. 监控证书过期: 使用外部监控工具检查证书有效期
  6. 保留日志: 方便排错和审计

扩展阅读