证书半夜过期,我用 acme.sh 的 DNS-01 把续期交给了 cron

举报
phantomxjc 发表于 2026/10/17 09:02:47 2026/10/17
【摘要】 去年有天晚上十点多,家里群里有人问"你的网盘怎么打不开了"。我打开浏览器一看,地址栏挂着红色警告——证书过期了。签的是 Let's Encrypt 三个月的免费证书,我一直以为它会自己续,结果那次续期因为验证方式的问题没跑成功,我自己也没收到任何提示。 那之后我把整个流程换成了 acme.sh + DNS01,到现在快一年没管过。写下过程,免得下次又从零开始查。 为什么非得是 DNS01 我最早用

去年有天晚上十点多,家里群里有人问"你的网盘怎么打不开了"。我打开浏览器一看,地址栏挂着红色警告——证书过期了。签的是 Let's Encrypt 三个月的免费证书,我一直以为它会自己续,结果那次续期因为验证方式的问题没跑成功,我自己也没收到任何提示。

那之后我把整个流程换成了 acme.sh + DNS-01,到现在快一年没管过。写下过程,免得下次又从零开始查。

为什么非得是 DNS-01

我最早用的是 HTTP-01:在网站根目录放一个 .well-known/acme-challenge/ 下的文件,让 Let's Encrypt 来验。这在单个域名、80 端口对外开放的时候挺好用。但我的场景有两个问题。

一是服务都在内网,靠 nginx 反代,80 端口只留了跳转,验证路径经常被重定向规则拦掉,续期就失败了。二是我给 *.home.example.com 底下挂了七八个子服务,一个个签太麻烦,想用泛域名证书——泛域名只能用 DNS-01 验证,HTTP-01 根本不支持。

DNS-01 的原理是:acme.sh 调 DNS 服务商的 API 加一条 _acme-challenge 的 TXT 记录,Let's Encrypt 查到这条记录就算你拥有这个域名。不需要任何端口对外开放,内网机器也能签。

安装和第一次签发

curl https://get.acme.sh | sh -s email=me@example.com
source ~/.bashrc

装完注意一件事:acme.sh 从某个版本开始默认 CA 换成了 ZeroSSL,如果你就想用 Let's Encrypt,手动切一下:

acme.sh --set-default-ca --server letsencrypt

我用的是 DNSPod(腾讯云的),在控制台申请一个 API 密钥,拿到 ID 和 Token,写进 ~/.acme.sh/account.conf,或者直接在当前 shell 里 export:

export DP_Id="123456"
export DP_Key="xxxxxxxxxxxxxxxxxxxxxxxx"

然后签发。我选了 ECC 证书,256 位,体积小一点:

acme.sh --issue --dns dns_dp -d example.com -d '*.example.com' --keylength ec-256

这里有个我踩过的坑:*.example.com 不包含 example.com 本身。 我第一次只写了泛域名,签完部署上去,访问根域名还是报证书不匹配。必须两个 -d 都写上,acme.sh 会生成一张同时覆盖根域和所有子域的 SAN 证书。

另一个坑是 DNS 生效时间。acme.sh 加完 TXT 记录默认等 120 秒再让 CA 来查,大部分情况够用。我遇到过一次解析延迟比较久,查 TXT 老是查不到,加个 --dnssleep 300 就过去了。别急着怀疑密钥写错了,先用 dig _acme-challenge.example.com TXT 看看记录出来没有。

装证书,别直接指过去

签完的证书躺在 ~/.acme.sh/example.com_ecc/ 下面,目录结构是 acme.sh 自己定的,而且续期后里面的文件会被覆盖。官方文档明确说别直接把这个目录配进 nginx。用 --install-cert 拷到固定位置:

acme.sh --install-cert -d example.com --ecc \
  --key-file       /etc/nginx/ssl/example.com.key \
  --fullchain-file /etc/nginx/ssl/example.com.crt \
  --reloadcmd      "systemctl reload nginx"

--reloadcmd 是关键。证书文件换了但 nginx 不 reload,进程里还是老证书,浏览器照样报错。我一般用 systemctl reload nginx,比 nginx -s reload 稳一点。

还有个 cron 环境下的老问题:cron 执行时 PATH 特别干净,如果你在 reloadcmd 里写了不带全路径的命令,很可能执行失败但日志里看不出所以然。保险起见写成 "/usr/sbin/nginx -s reload" 这种绝对路径。

续期到底谁来触发

acme.sh 安装时会自动往 crontab 里塞一条:

0 0 * * * "/root/.acme.sh"/acme.sh --cron --home "/root/.acme.sh" > /dev/null

它每天跑,但只在证书剩余有效期不足 30 天时才真正续期,其余时间直接跳过。所以别看到今天没续就以为坏了。想验证整条链路通不通,手动加 --force 跑一次:

acme.sh --cron --force

跑完用这个看新证书的日期:

openssl x509 -in /etc/nginx/ssl/example.com.crt -noout -dates

或者直接连一下:

echo | openssl s_client -connect home.example.com:443 2>/dev/null | openssl x509 -noout -dates

建议在 --reloadcmd 里把续期结果也推一条到你的通知渠道(我用的是企业微信机器人,curl 一下就完事),这样万一真的续失败了,至少能第一时间知道,而不是等别人来问你怎么打不开了。

剩下的小事

--issue 那次用的 DP_Id/DP_Key 会被 acme.sh 存进 account.conf,之后 cron 续期不需要再 export,这也是为什么那两个密钥只需要在第一次签发时准备。

acme.sh 默认开着自动升级。如果哪天突然报错说某个参数不认识,先 acme.sh --upgrade 一下,很可能就是版本太老。

现在我的 crontab 里就那一条,一年到头不用管。证书的活儿应该就是这样——签完一次就忘掉它。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。