输入搜索…

· server · 7 min 阅读

使用 acme.sh 通过 CNAME 申请域名证书

当 DNS 不在自己手里时,如何通过 CNAME 接力验证免费申请 Let's Encrypt 证书?手把手教你用 acme.sh 解决无 DNS 权限场景下的证书签发难题。

使用 acme.sh 通过 CNAME 申请域名证书

@marekpiwnicki

前言

在申请 Let’s Encrypt 证书时,最常用的方式是 DNS-01 校验:在域名解析中添加一条 _acme-challenge 的 TXT 记录,由 ACME 服务器通过 DNS 查询完成域名所有权校验。

但在实际场景中,我们经常会遇到以下问题:

  • 域名托管在第三方平台(如 Cloudflare、企业内部 DNS),无法直接修改解析记录
  • 想要集中管理多个域名的证书,但每个域名对应不同的 DNS 服务商;
  • 想为子域名申请证书,但主域的 DNS 不在自己手里

此时可以使用 acme.sh 的 CNAME 接力验证 功能:把 _acme-challenge 记录通过 CNAME 指向另一个你有完整控制权的域名,由该域名的 DNS API 完成 ACME 校验,从而绕过主域 DNS 的限制。


原理说明

acme.sh 通过 --challenge-alias 参数实现 CNAME 接力验证,整体流程如下:

     example.com 的 _acme-challenge  →  CNAME  →  _acme-challenge.x1.example2.com

                              Let's Encrypt 沿 CNAME 查询到 example2.com

                              acme.sh 通过腾讯云 API 在 x1.example2.com 下
                              添加 _acme-challenge TXT 记录

                              Let's Encrypt 校验通过 → 证书签发成功

简单来说:

  • example.com:你想要申请证书的域名(没有 DNS 控制权);
  • example2.com:你拥有完整控制权的域名(用于添加 TXT 记录);
  • x1.example2.comexample2.com 下任意一个子域名,作为校验的”中转站”。

前提条件

  • 一台 Linux 服务器(Debian/Ubuntu/CentOS 均可);
  • 一个自己有 DNS API 权限的域名(本文以腾讯云为例);
  • 在「主域」(example.com) 的 DNS 中添加一条 CNAME 记录的能力(虽然拿不到完整权限,但至少能加 CNAME,这是绝大多数 DNS 服务商都允许的)。

操作步骤

1. 在主域 DNS 中添加 CNAME 记录

登录 example.com 所在的 DNS 服务商,添加以下记录:

主机记录记录类型记录值
_acme-challenge.example.comCNAME_acme-challenge.x1.example2.com

说明
如果同时为 www.example.com 申请证书,也需要为它添加一条 CNAME 记录,指向相同的 _acme-challenge.x1.example2.com

2. 安装 acme.sh

     # 国内服务器访问 GitHub 受限,建议先配置代理
export https_proxy=http://your-proxy:port

curl https://get.acme.sh | sh -s email=my@example.com

# 使 acme.sh 命令立即生效
source ~/.bashrc

关于 --email
该邮箱用于接收证书到期提醒、Let’s Encrypt 账号恢复等通知,建议使用真实邮箱。

3. 切换默认 CA 为 Let’s Encrypt

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

为什么要切换?
acme.sh 默认 CA 是 ZeroSSL,部分场景下 Let’s Encrypt 兼容性更好,且国内使用更广泛。

4. 配置腾讯云 DNS API

前往 腾讯云 API 密钥管理 创建 SecretIdSecretKey(至少需要 DNSPod 的解析权限),然后执行:

     export Tencent_SecretId="AKIDxxxxxxxxxxxxxxxxxxxxxxxx"
export Tencent_SecretKey="xxxxxxxxxxxxxxxxxxxxxxxx"

# 写入 ~/.bashrc,避免重启服务器后失效
echo 'export Tencent_SecretId="AKIDxxxxxxxxxxxxxxxxxxxxxxxx"' >> ~/.bashrc
echo 'export Tencent_SecretKey="xxxxxxxxxxxxxxxxxxxxxxxx"' >> ~/.bashrc

⚠️ 注意:必须使用腾讯云 API 密钥,不能使用 DNSPod Token
腾讯云 API 控制台创建的密钥是 SecretId + SecretKey 组合;如果误用了 DNSPod 的 Token,会报 invalid dns api 错误。

5. 签发证书

     acme.sh --issue \
  --dns dns_tencent \
  -d example.com \
  -d "*.example.com" \
  --challenge-alias x1.example2.com

参数说明:

  • --dns dns_tencent:使用腾讯云 DNS API 添加校验记录;
  • -d:要申请证书的域名(支持多个,*.example.com 表示通配符);
  • --challenge-alias:指定用于接力校验的子域名(必须在腾讯云 DNS 中存在)。

6. 安装证书到指定路径

     acme.sh --install-cert -d example.com \
  --cert-file      /etc/nginx/ssl/cert.pem \
  --key-file       /etc/nginx/ssl/key.pem \
  --fullchain-file /etc/nginx/ssl/fullchain.pem \
  --reloadcmd      "docker exec nginx nginx -s reload"

关于 --reloadcmd

  • 推荐使用 docker exec nginx nginx -s reload 热加载,服务不会中断
  • 不要使用 docker restart nginx,这会导致服务短暂不可用;
  • 如果不是容器化部署,可以改成 systemctl reload nginx

自动续期与验证

acme.sh 安装时会自动创建 cron 续期任务(每天凌晨随机时间检查一次,到期前 60 天自动续期),通常无需额外配置。可以通过以下命令验证:

     # 查看当前所有证书及其到期时间
acme.sh --list

# 查看指定证书的详细信息
acme.sh --info -d example.com

# 手动强制续期测试(不改变实际有效期)
acme.sh --renew -d example.com --force

# 查看 cron 续期任务
crontab -l | grep acme

证书签发后,建议:

  1. 浏览器访问 https://example.com,确认证书已生效;
  2. 使用 SSL Labs 等在线工具检测证书链配置是否完整。

常见问题

报错 Invalid challenge alias

可能原因:

  • _acme-challenge.example.com 的 CNAME 记录尚未生效(DNS 缓存一般 5~30 分钟);
  • CNAME 记录配置错误,请检查主机记录是否漏了 _
  • _acme-challenge.x1.example2.com 在腾讯云 DNS 中不存在,需要先在腾讯云控制台添加这条主机记录(任意值都可以)。

报错 dns api error: com.tencentcloudapi

请检查:

  • Tencent_SecretIdTencent_SecretKey 是否正确;
  • API 密钥是否具备 DNSPod 解析权限;
  • 服务器能否访问 tencentcloudapi.com(国内服务器一般无问题,海外服务器可能需要代理)。

证书签发成功但浏览器提示「不安全」

通常是证书链不完整导致,请确认:

  • Nginx 配置中使用了 fullchain.pem(包含中间证书),而不是 cert.pem(只有叶子证书);
  • Nginx 重载配置正确,没有缓存旧的证书文件。

是否支持申请通配符证书?

支持。直接在签发命令中添加 -d "*.example.com" 即可,CNAME 记录共用同一条即可。

返回文章列表