第08章 - 自动续期与维护管理
证书的自动续期是 acme.sh 最核心的价值之一。SSL/TLS 证书(Let’s Encrypt/ZeroSSL 等免费 CA)的有效期通常为 90 天,如果不能自动续期,证书过期会导致网站无法访问。本章详细介绍如何配置和维护 acme.sh 的自动续期机制。
8.1 自动续期原理
8.1.1 续期触发条件
acme.sh 不会等证书完全过期才续期,而是提前续期:
- 默认触发条件:证书剩余有效期少于 30 天时触发续期
- ARI 协议支持:如果 CA 支持 ARI(ACME Renewal Information,RFC 9773),CA 可以主动建议提前续期(例如遇到安全事件需要批量吊销重签时)
8.1.2 续期检查流程
cron 每日定时触发
↓
acme.sh --cron
↓
遍历 ~/.acme.sh/ 下所有域名目录
↓
读取每个域名的 .conf 文件
↓
检查证书有效期(本地计算,无需联网)
↓
[剩余 > 30 天] → 跳过(输出 "Skipping...")
[剩余 ≤ 30 天] → 执行续期
↓
续期成功
↓
执行 --reloadcmd(重载 Web 服务)
8.2 Cron 定时任务配置
8.2.1 自动添加的 Cron 任务
acme.sh 安装时会自动向当前用户的 crontab 添加一条定时任务:
# 查看当前用户的 cron 任务
crontab -l
输出示例:
# 时间是随机选取的,避免 CA 服务器峰值
23 0 * * * "/root/.acme.sh"/acme.sh --cron --home "/root/.acme.sh" > /dev/null
含义:每天凌晨 00:23 运行一次证书检查和续期。
8.2.2 手动管理 Cron 任务
# 重新安装 cron 任务(如果 cron 任务被误删)
acme.sh --install-cronjob
# 删除 cron 任务
acme.sh --uninstall-cronjob
# 查看 cron 任务
crontab -l
8.2.3 自定义 Cron 执行频率
默认每天执行一次。可以手动修改 crontab 改变执行频率:
crontab -e
修改为每 12 小时检查一次:
0 */12 * * * "/root/.acme.sh"/acme.sh --cron --home "/root/.acme.sh" > /dev/null
修改为每天 3:00 执行(固定时间):
0 3 * * * "/root/.acme.sh"/acme.sh --cron --home "/root/.acme.sh" >> /var/log/acme.sh.log 2>&1
8.2.4 启用 Cron 日志记录
默认情况下,cron 日志被重定向到 /dev/null(不保存)。建议在生产环境开启日志:
23 0 * * * "/root/.acme.sh"/acme.sh --cron --home "/root/.acme.sh" >> /var/log/acme.sh.cron.log 2>&1
同时配置日志轮转(/etc/logrotate.d/acme-sh):
/var/log/acme.sh.cron.log {
weekly
rotate 4
compress
missingok
notifempty
}
8.3 Systemd Timer(cron 替代方案)
在使用 systemd 的现代 Linux 系统(Ubuntu 16.04+、CentOS 7+、Debian 9+)上,systemd timer 比 cron 更加可靠和灵活。
8.3.1 创建 Service 文件
cat > /etc/systemd/system/acme.sh.service << 'EOF'
[Unit]
Description=acme.sh certificate renewal service
Documentation=https://github.com/acmesh-official/acme.sh
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
User=root
ExecStart=/root/.acme.sh/acme.sh --cron --home /root/.acme.sh
StandardOutput=journal
StandardError=journal
SyslogIdentifier=acme.sh
EOF
8.3.2 创建 Timer 文件
cat > /etc/systemd/system/acme.sh.timer << 'EOF'
[Unit]
Description=Daily acme.sh certificate renewal timer
Documentation=https://github.com/acmesh-official/acme.sh
[Timer]
# 每天执行一次,在系统启动后 10 分钟到 1 小时之间随机触发
OnCalendar=daily
RandomizedDelaySec=1h
# 如果上次执行时系统关机,下次启动后补执行
Persistent=true
[Install]
WantedBy=timers.target
EOF
8.3.3 启用 Timer
# 重载 systemd 配置
systemctl daemon-reload
# 启用并启动 timer
systemctl enable acme.sh.timer
systemctl start acme.sh.timer
# 查看 timer 状态
systemctl status acme.sh.timer
systemctl list-timers acme.sh.timer
8.3.4 手动测试 Service
# 手动触发一次(测试是否正常)
systemctl start acme.sh.service
# 查看执行日志
journalctl -u acme.sh.service -f
journalctl -u acme.sh.service --since "1 hour ago"
8.4 手动续期操作
8.4.1 手动触发续期检查
# 运行一次完整的续期检查(等同于 cron 任务执行的操作)
acme.sh --cron --home ~/.acme.sh
# 查看详细输出
acme.sh --cron --home ~/.acme.sh --debug
8.4.2 强制续期特定域名
即使证书未到期,也可以强制重新申请:
# 强制续期
acme.sh --renew -d example.com --force
# 强制续期 ECC 证书
acme.sh --renew -d example.com --force --ecc
# 强制续期并查看详细输出
acme.sh --renew -d example.com --force --debug
8.4.3 续期所有证书
acme.sh --renew-all
# 强制续期所有证书(无视有效期)
acme.sh --renew-all --force
8.4.4 检查证书状态
# 列出所有由 acme.sh 管理的证书
acme.sh --list
# 输出示例:
# Main_Domain KeyLength SAN_Domains CA Created Renew
# example.com "" *.example.com LetsEncrypt 2024-01-15T10:00:00Z 2024-04-15T10:00:00Z
# 查看证书详情(使用 openssl)
openssl x509 -in ~/.acme.sh/example.com/fullchain.cer -noout -dates
openssl x509 -in ~/.acme.sh/example.com/fullchain.cer -noout -text | grep -A2 "Subject:"
openssl x509 -in ~/.acme.sh/example.com/fullchain.cer -noout -text | grep "DNS:"
8.5 证书续期钩子
acme.sh 支持在证书申请/续期流程的不同阶段执行自定义脚本。
8.5.1 钩子类型
| 钩子 | 说明 | 触发时机 |
|---|---|---|
Le_PreHook |
申请前钩子 | 每次申请/续期开始前执行 |
Le_PostHook |
申请后钩子 | 每次申请/续期结束后执行(无论成功失败) |
Le_RenewHook |
续期后钩子 | 仅在证书成功续期后执行 |
--reloadcmd |
重载命令 | 证书成功安装后执行(相当于 RenewHook) |
8.5.2 配置钩子
# 在申请时直接指定钩子
acme.sh --issue --dns dns_cf -d example.com \
--pre-hook "/usr/bin/systemctl stop nginx" \
--post-hook "/usr/bin/systemctl start nginx" \
--renew-hook "/opt/notify_cert_renewed.sh"
# 或修改已有证书的配置
acme.sh --install-cert -d example.com \
--fullchain-file /etc/nginx/ssl/fullchain.pem \
--key-file /etc/nginx/ssl/privkey.pem \
--reloadcmd "systemctl reload nginx && /opt/sync_cert_to_cdn.sh"
8.5.3 续期通知脚本示例
发送微信/钉钉/Telegram 通知(以 Telegram 为例):
cat > /opt/notify_cert_renewed.sh << 'EOF'
#!/bin/bash
# 证书续期成功后发送 Telegram 通知
BOT_TOKEN="your_telegram_bot_token"
CHAT_ID="your_chat_id"
DOMAIN="${CERT_DOMAIN:-unknown}"
EXPIRE_DATE="${Le_NextRenewTime:-unknown}"
MESSAGE="✅ 证书续期成功!%0A域名: $DOMAIN%0A有效期至: $(date -d @$EXPIRE_DATE '+%Y-%m-%d' 2>/dev/null || echo $EXPIRE_DATE)"
curl -s -X POST "https://api.telegram.org/bot${BOT_TOKEN}/sendMessage" \
-d "chat_id=${CHAT_ID}" \
-d "text=${MESSAGE}"
EOF
chmod +x /opt/notify_cert_renewed.sh
# 安装证书时配置续期钩子
acme.sh --install-cert -d example.com \
--fullchain-file /etc/nginx/ssl/fullchain.pem \
--key-file /etc/nginx/ssl/privkey.pem \
--reloadcmd "systemctl reload nginx && /opt/notify_cert_renewed.sh"
发送钉钉通知:
cat > /opt/notify_dingtalk.sh << 'EOF'
#!/bin/bash
WEBHOOK_URL="https://oapi.dingtalk.com/robot/send?access_token=your_token"
DOMAIN="${CERT_DOMAIN:-example.com}"
curl -s -X POST "$WEBHOOK_URL" \
-H 'Content-Type: application/json' \
-d "{
\"msgtype\": \"text\",
\"text\": {
\"content\": \"SSL证书续期成功通知\\n域名:$DOMAIN\\n时间:$(date '+%Y-%m-%d %H:%M:%S')\"
}
}"
EOF
chmod +x /opt/notify_dingtalk.sh
8.6 证书管理操作
8.6.1 查看证书信息
# 查看所有证书列表
acme.sh --list
# 查看某个证书的详细信息
acme.sh --info -d example.com
8.6.2 修改证书配置
如果需要更改已申请证书的 reload 命令或安装路径:
# 重新执行 install-cert 即可更新配置
acme.sh --install-cert -d example.com \
--key-file /new/path/privkey.pem \
--fullchain-file /new/path/fullchain.pem \
--reloadcmd "new reload command"
8.6.3 更换 CA(重新申请)
如果需要将某个域名从 ZeroSSL 换到 Let’s Encrypt:
acme.sh --issue --dns dns_cf \
-d example.com -d *.example.com \
--server letsencrypt \
--force
8.6.4 吊销证书
# 吊销证书(通知 CA 吊销,之后该证书不再有效)
acme.sh --revoke -d example.com
# 吊销 ECC 证书
acme.sh --revoke -d example.com --ecc
# 吊销并指定原因(0=unspecified, 1=keyCompromise, 3=superseded, 4=cessationOfOperation)
acme.sh --revoke -d example.com --revoke-reason 4
8.6.5 删除证书管理条目
从 acme.sh 的管理中删除某个域名(不会自动吊销,仅删除本地配置):
acme.sh --remove -d example.com
# 这会删除 ~/.acme.sh/example.com/ 目录
# 但已安装到 Nginx 的证书文件不受影响
8.7 多账户和多 CA 管理
8.7.1 查看当前账户信息
acme.sh --info
8.7.2 注册不同 CA 账户
# 注册 Let's Encrypt 账户
acme.sh --register-account -m your@email.com --server letsencrypt
# 注册 ZeroSSL 账户
acme.sh --register-account -m your@email.com --server zerossl
# 注册 Buypass 账户
acme.sh --register-account -m your@email.com --server buypass
8.7.3 ZeroSSL EAB 凭据配置
ZeroSSL 推荐使用 EAB(External Account Binding)凭据以获得更好的证书管理体验:
- 登录 ZeroSSL 控制台
- 获取 EAB Key ID 和 EAB HMAC Key
- 配置到 acme.sh:
acme.sh --register-account \
--server zerossl \
--eab-kid "your_eab_key_id" \
--eab-hmac-key "your_eab_hmac_key"
8.8 监控与告警
8.8.1 检查证书有效期脚本
cat > /opt/check_cert_expiry.sh << 'EOF'
#!/bin/bash
# 检查证书有效期,如果剩余不足 7 天则发送告警
WARN_DAYS=7
CERT_DIR="/etc/nginx/ssl"
for cert_file in "$CERT_DIR"/**/fullchain.pem; do
if [ -f "$cert_file" ]; then
domain=$(dirname "$cert_file" | xargs basename)
expiry=$(openssl x509 -enddate -noout -in "$cert_file" | cut -d= -f2)
expiry_epoch=$(date -d "$expiry" +%s 2>/dev/null || date -j -f "%b %d %T %Y %Z" "$expiry" +%s)
now_epoch=$(date +%s)
days_left=$(( (expiry_epoch - now_epoch) / 86400 ))
if [ "$days_left" -lt "$WARN_DAYS" ]; then
echo "⚠️ 告警:$domain 证书将在 $days_left 天后过期!"
# 在这里添加发送通知的逻辑
else
echo "✅ $domain:剩余 $days_left 天"
fi
fi
done
EOF
chmod +x /opt/check_cert_expiry.sh
8.8.2 使用 acme.sh 内置检查
# 一次性查看所有证书到期时间
acme.sh --list
# 结合 grep 过滤快到期的证书
acme.sh --list | awk 'NR>1 {print $1, $6}' | while read domain renew; do
echo "Domain: $domain, Next renew: $renew"
done
8.9 备份与恢复
8.9.1 备份 acme.sh 数据
# 备份整个 .acme.sh 目录(包含所有证书、密钥和配置)
tar -czf /backup/acme.sh-backup-$(date +%Y%m%d).tar.gz ~/.acme.sh/
# 仅备份账户配置和证书配置(不含 DNS API 密钥的完整备份不太安全)
tar -czf /backup/acme-certs-$(date +%Y%m%d).tar.gz \
--exclude='~/.acme.sh/dnsapi' \
~/.acme.sh/
安全提示:备份文件包含 DNS API 密钥和证书私钥,务必加密存储或存放到安全位置。
8.9.2 迁移到新服务器
# 在旧服务器上打包
tar -czf acme.sh-full.tar.gz ~/.acme.sh/
# 传输到新服务器
scp acme.sh-full.tar.gz user@new-server:/tmp/
# 在新服务器上解压
cd ~
tar -xzf /tmp/acme.sh-full.tar.gz
# 重新安装(更新脚本路径和 cron 任务)
~/.acme.sh/acme.sh --install
8.10 小结
本章介绍了 acme.sh 的自动续期和维护管理:
- Cron 任务:acme.sh 安装时自动配置,每天检查,30 天内到期时自动续期
- Systemd Timer:现代 Linux 系统推荐使用,比 cron 更可靠
- 手动续期:
--renew -d domain --force强制重新申请 - 续期钩子:支持在续期成功后发送通知(微信、钉钉、Telegram 等)
- 证书管理:
--list、--info、--revoke、--remove等完整管理命令 - 备份恢复:备份
~/.acme.sh/目录即可完整保留所有数据
下一章介绍 acme.sh 的高级功能与技巧。