liufeisheng.cn 通配符 HTTPS 证书自动续期部署完整记录

写于 2026-10-08

部署完成日期:2026-10-08 最终方案:acme.sh + DNSPod API(dns_dp)自动 DNS-01 验证 + Let's Encrypt 通配符证书,Nginx 终止 TLS,cron 全自动续期 状态:www / crm / blog / dh / nas / jxcg 等全部子域名 HTTPS 已恢复并全球生效;根域名解析为遗留待办(见文末)。


一、最终架构:到底是谁在干什么

这套 HTTPS 同时涉及三家平台,职责必须先分清楚,否则容易误判故障点。

平台 / 组件 角色 是否参与证书管理
阿里云 ECS(47.113.x.x,Ubuntu,公网入口) 运行 Nginx 1.24.0,对外提供 443,TLS 在此终止;同时运行 acme.sh 和 cron,负责申请、续期、安装证书 ✅ 证书就在这台机器上管理
腾讯云 DNSPod liufeisheng.cn 的 DNS 托管商;通过 API Token 让 acme.sh 自动增删 _acme-challenge 的 TXT 记录完成验证 ✅ 提供 DNS-01 验证接口
Let's Encrypt 免费证书颁发机构(CA),签发 *.liufeisheng.cn + liufeisheng.cn 通配符证书,有效期 90 天 发证方
群晖 DS920+(FeiNas,内网) 跑 Django 个人站、CRM、Blog 等后端容器,通过 frp 穿透回源到 ECS ❌ 不参与证书,证书不在群晖上
frp 群晖上的 frpc 连到 ECS 上的 frps,把公网请求按域名转发回内网各容器;TLS 已在 ECS 的 Nginx 终止,frp 隧道内全程 HTTP ❌ 不管证书

请求链路:

浏览器 --HTTPS(443)--> 阿里云ECS Nginx (TLS终止, 校验通配符证书)
                         --> frps 按域名分发
                         --> frpc (群晖) --> 内网各 Docker 容器 (HTTP)

关键结论:证书只在阿里云 ECS 上管理,DNSPod 只负责自动验证,群晖完全不碰证书。


二、问题是怎么来的(背景与根因)

2.1 现象

浏览器访问 https://www.liufeisheng.cn 报 ERR_CERT_DATE_INVALID,所有子站点(blog/crm/dh/nas/jxcg)小锁全部失效;HTTP 80 仍能 301 跳到 HTTPS。

2.2 诊断过程

  1. 用 openssl 查 443 实际证书:
echo | openssl s_client -connect www.liufeisheng.cn:443 -servername www.liufeisheng.cn 2>/dev/null \
  | openssl x509 -noout -subject -issuer -dates -ext subjectAltName

发现: - 443 端口实际由 nginx/1.24.0 (Ubuntu) 响应,不是 Caddy(纠正了最初"用 Caddy 实现 HTTPS"的猜测)。 - 证书主体 CN=*.liufeisheng.cn,SAN 含 *.liufeisheng.cn, liufeisheng.cn,所有子域名共用同一张通配符证书。 - 有效期 2025-12-22 至 2026-03-22,到 2026-10-08 已过期约 6 个半月。

  1. 根因:

通配符证书 *.liufeisheng.cn 只能用 DNS-01 方式验证签发(HTTP-01 无法证明对整个泛域名的控制权)。这张证书最初是手动用 certbot 申请的,但: - certbot renew 定时任务里没有配置 DNS 插件凭据,或 - 架构从 Caddy 切回 Nginx 后自动续期链路断了,

到期(3 月 22 日)后一直没续成功。


三、踩过的坑(重点,避免再犯)

坑 1:误以为 443 是 Caddy,实际是 Nginx

最初参考旧笔记以为是 Caddy 自动 HTTPS。实测 443 响应头是 Server: nginx/1.24.0,说明后来架构已切回 Nginx,证书由 Nginx 手动管理。 教训:以端口实际探测为准,不要凭旧笔记下结论。排查 HTTPS 先看 Server 头和 openssl 证书。

坑 2:通配符证书不能用 HTTP-01 自动续

普通单域名证书 certbot 可自动 HTTP-01 续期;但 *.liufeisheng.cn 必须 DNS-01。certbot 若没配 DNS 插件凭据,续期一定失败。 教训:泛域名证书从第一天起就要把 DNS API 自动验证配好,别指望默认的自动续期。

坑 3:certbot 手动 DNS 流程连续失败三次(两条 TXT + 缓存 + 删除时机)

申请的是两个域名(根域名 + 通配符),Let's Encrypt 会分别验证,certbot 会出现两次 TXT 提示:

  • 第 1 次提示:建值 A 的 TXT;
  • 第 2 次提示:再建值 B 的 TXT,此时 A、B 两条必须同时存在。

实际三次失败原因各不相同: 1. 把前一条删了/替换成新的 → 报 Incorrect TXT ... (and 1 more)(两条里错一条); 2. 全删了 → 报 No TXT record found; 3. 只剩第一条旧值 → 报 Incorrect TXT qXtP...。

另外 DNS 缓存不一致加剧了问题:权威 DNS(119.29.x.x)已查不到,但 8.8.x.x、阿里 DNS 仍缓存旧值。

教训:手动 DNS-01 极易出错,同一主机记录下多条 TXT 是正常的,不能删旧条;改记录后要等全球缓存生效。这也是最终放弃手动、改用 API 自动验证的直接原因。

坑 4:acme.sh 官方脚本在国内 ECS 上装不上(GitHub 被重置)

执行官方安装命令:

curl https://get.acme.sh | sh -s email=[REDACTED_EMAIL]

报错:

curl: (35) Recv failure: Connection reset by peer
-bash: /root/.acme.sh/acme.sh: No such file or directory

原因:get.acme.sh 返回的只是引导脚本,真正的代码要从 GitHub 下载,国内服务器访问 GitHub 被连接重置。 解决:改用 Gitee 镜像克隆安装(见下文第 2 步),版本与功能和 GitHub 完全一致。

坑 5:两套工具不能同时管一张证书 / 443

  • Nginx 和 Caddy 不能同时占用、管理 443;
  • certbot 和 acme.sh 不能同时对同一张证书做续期,会互相覆盖打架。 解决:改用 acme.sh 后,删除 certbot 的续期配置 /etc/letsencrypt/renewal/liufeisheng.cn.conf,让 acme.sh 独自管理。

坑 6:根域名 @ 误指内网地址(遗留待办)

排查中发现 liufeisheng.cn(不带 www)的 A 记录指向 192.168.0.100(内网地址),公网根本打不开。证书虽已覆盖根域名,但解析不对仍无法访问。此条截至文档撰写时仍未修复,处理方式见文末"遗留待办"。


四、完整部署步骤(最终成功方案,全程在阿里云 ECS 上 root 操作)

群晖全程不用动。整个过程约 10 分钟。

第 1 步:在腾讯云 DNSPod 创建 API Token

注意是 DNSPod 的 API Token,不是腾讯云 CAM 的 SecretKey。

  1. 浏览器打开:https://console.dnspod.cn/account/token/token (DNSPod 控制台 → 右上角头像 → 密钥管理 → API 密钥)
  2. 点「创建密钥」,备注填 acme,确定后得到: - ID:一串纯数字(如 123456) - Token:一长串字符,只显示这一次,立刻保存
  3. 确认域名在此账号下:https://console.dnspod.cn/dns/list 能看到 liufeisheng.cn。

第 2 步:在阿里云 ECS 安装 acme.sh(用 Gitee 镜像,绕开 GitHub 重置)

apt update
apt install -y git socat cron

git clone https://gitee.com/neilpang/acme.sh.git /root/acme-src
cd /root/acme-src
./acme.sh --install -m [REDACTED_EMAIL]

source ~/.bashrc

# 设置默认 CA 为 Let's Encrypt
~/.acme.sh/acme.sh --set-default-ca --server letsencrypt

说明: - 安装成功后命令位于 ~/.acme.sh/acme.sh,安装过程会自动写入 crontab。 - 源码目录 /root/acme-src 之后可删,不影响运行。 - 若 Gitee 也 clone 失败,先 apt install -y ca-certificates 再试。

第 3 步:用 DNSPod API 自动签发通配符证书

把下面两个值换成第 1 步拿到的 ID 和 Token(单引号保留):

export DP_Id='647638'
export DP_Key='cbdeb0fb8c2ff34f461ba92126faed72'

~/.acme.sh/acme.sh --issue --dns dns_dp \
  -d liufeisheng.cn -d '*.liufeisheng.cn'

全自动过程(约 1~3 分钟,无需手动加任何 TXT): - acme.sh 调 DNSPod 接口自动添加 _acme-challenge 的 TXT 记录; - Let's Encrypt 验证通过后自动删除 TXT; - 成功结尾看到 Cert success. - 证书原件保存在 ~/.acme.sh/liufeisheng.cn_ecc/(acme.sh 内部工作目录,平时不用动)。

第 4 步:把证书安装到 Nginx 正在使用的路径

先核对 Nginx 当前引用的证书路径:

certbot certificates 2>/dev/null | grep -A3 Certificate
# 或
grep -r ssl_certificate /etc/nginx/

确认路径为:

/etc/letsencrypt/live/liufeisheng.cn/fullchain.pem
/etc/letsencrypt/live/liufeisheng.cn/privkey.pem

安装证书(整段粘贴,--ecc 必须带,与第 3 步签发的 ECC 证书对应):

~/.acme.sh/acme.sh --install-cert -d liufeisheng.cn --ecc \
  --key-file       /etc/letsencrypt/live/liufeisheng.cn/privkey.pem \
  --fullchain-file /etc/letsencrypt/live/liufeisheng.cn/fullchain.pem \
  --reloadcmd      "nginx -t && systemctl reload nginx"

效果: - 新证书直接覆盖旧的过期文件,Nginx 配置一行都不用改; - 自动执行 nginx -t 语法检查并 reload; - 目标路径和 reload 命令会被 acme.sh 记住,以后自动续期照此覆盖 + 重载。

(若目录不存在:mkdir -p /etc/letsencrypt/live/liufeisheng.cn 后再执行。)

第 5 步:停掉 certbot 对该证书的续期,防止两套工具打架

rm -f /etc/letsencrypt/renewal/liufeisheng.cn.conf

旧证书文件保留无妨(已被新内容覆盖),certbot 从此不再管它。冗余证书可顺手删:

certbot delete --cert-name nas.liufeisheng.cn --non-interactive

第 6 步:确认自动续期已生效

# 1) 确认 cron 里有 acme.sh
crontab -l | grep acme.sh

应看到类似:

xx xx * * * /root/.acme.sh/acme.sh --cron --home /root/.acme.sh ...

做一次强制续期演练,验证整条链路:

~/.acme.sh/acme.sh --cron --force

结尾看到 Renew success 且 nginx reload 成功,即说明自动续期万无一失。acme.sh 每天检查一次,到期前 30 天自动续,续完自动覆盖证书并 reload Nginx,永久无需人工干预。

若 crontab 里没有该任务,手动补:

~/.acme.sh/acme.sh --install-cronjob

第 7 步:验证成果

echo | openssl s_client -connect www.liufeisheng.cn:443 -servername www.liufeisheng.cn 2>/dev/null \
  | openssl x509 -noout -issuer -dates

预期: - issuer 含 Let's Encrypt; - notBefore 为 2026-10-07,notAfter 为 2027-01-05(约 90 天后)。

浏览器打开 https://www.liufeisheng.cn 按 Ctrl + F5 强刷,小锁恢复;所有共用通配符证书的子站同时恢复。若个别浏览器仍提示不安全,是旧连接缓存,等几分钟或用无痕窗口验证。


五、部署完成后的实测结果(2026-10-08 外部核验)

域名 证书颁发者 有效期 状态
www.liufeisheng.cn Let's Encrypt (YE1) 2026-10-07 ~ 2027-01-05 ✅
crm.liufeisheng.cn Let's Encrypt (YE1) 同上 ✅
blog.liufeisheng.cn Let's Encrypt (YE1) 同上 ✅
dh.liufeisheng.cn Let's Encrypt (YE1) 同上 ✅
nas.liufeisheng.cn Let's Encrypt (YE1) 同上 ✅
jxcg.liufeisheng.cn Let's Encrypt (YE1) 同上 ✅
liufeisheng.cn(根域名) — — ⚠️ 解析未修

其他验证: - http://www.liufeisheng.cn → 301 Moved Permanently 跳转到 HTTPS; - https://www.liufeisheng.cn → 200 OK,Server: nginx/1.24.0 (Ubuntu)。


六、遗留待办

修复根域名解析(不带 www)

liufeisheng.cn 的 A 记录当前错误指向内网 192.168.0.100,公网打不开。

  1. 打开 https://console.dnspod.cn/dns/list ,点 liufeisheng.cn;
  2. 找到主机记录为 @ 的 A 记录,记录值改为:47.113.x.x;
  3. 保存后等几分钟,https://liufeisheng.cn 即可正常访问(证书已覆盖根域名)。

七、常用运维命令速查

# 查看已安装证书及 acme.sh 记录
~/.acme.sh/acme.sh --list

# 手动强制续期(演练)
~/.acme.sh/acme.sh --cron --force

# 查看自动续期 cron
crontab -l | grep acme.sh

# 在线查看某域名实际生效的证书
echo | openssl s_client -connect www.liufeisheng.cn:443 -servername www.liufeisheng.cn 2>/dev/null \
  | openssl x509 -noout -issuer -dates -ext subjectAltName

# 检查并重载 Nginx
nginx -t && systemctl reload nginx

八、一句话总结

证书在阿里云 ECS 上用 acme.sh 管理,靠腾讯云 DNSPod 的 API Token 自动完成 DNS-01 验证,向 Let's Encrypt 申请泛域名证书,Nginx 终止 TLS,cron 到期前 30 天自动续期并 reload;群晖只通过 frp 提供后端服务,完全不碰证书。配好之后,90 天一次的续期永久自动化,无需再人工干预。