SSD Nodes Learn
指南 Matt Connor作者: Matt Connor · 更新于 2026-07-19

Certbot 通配符证书:用 DNS-01 验证申请

使用 DNS 验证方式通过 Certbot 申请通配符证书。讲清 TXT 记录如何证明域名控制权、要安装哪个插件,以及续期为何能保持自动。

为什么通配符证书必须用 DNS-01

通配符证书覆盖一个域名的每个一级子域:*.example.com 匹配 app.example.comblog.example.com,以及任何只比主域深一层的名称。Let's Encrypt 只通过 DNS-01 验证签发通配符证书,因此 Certbot 必须通过在 _acme-challenge.example.com 处发布一条 TXT 记录,来证明它控制该域名的 DNS。HTTP-01 验证无法胜任,因为提供一个令牌文件只能证明对某一个主机名的控制,也就是验证服务器取回该文件的那个主机名。而通配符是对该域名下每一个可能名称的声明,唯一能代表整个命名空间发声的公开记录,就是 DNS 本身。

这一条要求决定了本页其余的一切。要通过 DNS-01,您必须能够在域名的区域中创建 TXT 记录,或是手动创建,或是通过您的 DNS 服务商的 API(应用程序编程接口)。手动方式能成功一次,然后会在续期时失败,原因下文会具体说明。而通过 Certbot 的 DNS 插件走 API 这条路,可以无人值守地续期,这也是您最终应当采用的配置。

这是我们 Certbot 系列指南中的通配符篇。普通的单主机名证书、Web 服务器配置以及 80 端口规则,请见 在 Ubuntu 24.04 上用 nginx 配置 Certbot在 Ubuntu 24.04 上用 Apache 配置 Certbot

_acme-challenge TXT 记录的工作原理

当 Certbot 申请 *.example.com 时,Let's Encrypt 会返回一个随机令牌。Certbot 将该令牌与您的 ACME(自动证书管理环境)账户密钥组合,用 SHA-256 对结果做哈希,得到一个短文本值。该值必须以 TXT 记录的形式出现在 _acme-challenge.example.com 处。随后 Let's Encrypt 会从它自己的基础设施查询您域名的权威名称服务器。如果它读到的记录与它期望的值一致,您就证明了您控制该区域,而对该区域的控制会被视为对其下每一个名称的控制。

有两个细节会导致大多数失败:

  • 在同一张证书上同时申请 example.com*.example.com,意味着两个各自独立的验证,而两条 TXT 记录都位于同一个名称 _acme-challenge.example.com 之下。两条必须同时存在。追加第二条记录是对的;用第二条替换第一条则会让第一个验证失败。
  • 验证读取的是您的权威服务器,但服务商控制面板可能需要一分钟甚至更久,才能把新记录推送到这些服务器上。在放行验证之前,先从外部检查一下:
dig +short TXT _acme-challenge.example.com @1.1.1.1

当这条命令打印出 Certbot 所要求的那个值时,验证就能成功。当它什么都没打印时,请稍候再运行一次。

先看它跑通一次:手动模式

手动模式让您亲自完成 DNS 编辑,这是在自动化之前理解整个机制的最好方式:

sudo certbot certonly --manual --preferred-challenges dns -d example.com -d '*.example.com'

通配符两侧的引号,是为了阻止您的 shell 把 * 当作文件名通配模式来处理。Certbot 会暂停并给出说明:

Please deploy a DNS TXT record under the name:
_acme-challenge.example.com.
with the following value:
Jx9mQ2wLr8vTn5cKp0aYdG3hB7fZs4eN1oiRuXqMk6E

在您的 DNS 服务商面板中创建这条 TXT 记录,用上面的 dig 命令确认它已可见,然后才按回车。由于这次运行同时申请了裸域名和通配符,Certbot 会提示两次;在签发完成之前,请保留这两条记录。成功会以熟悉的这几行结束:

Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/example.com/fullchain.pem

为什么手动模式无法自我续期

每一次续期都是一次全新的验证,带着全新的令牌,所以 TXT 值每次都会变。您今天粘贴的记录,60 天后毫无用处。续期定时器每天两次无人值守地运行 Certbot,而没有人守在键盘旁去粘贴新值,于是手动签发的证书会以下面这个确切的错误在续期时失败:

Failed to renew certificate example.com with error: The manual plugin is not
working; there may be problems with your existing configuration.
The error was: PluginError('An authentication script must be provided with
--manual-auth-hook when using the manual plugin non-interactively.')

您可以通过编写调用 DNS 服务商 API 的 --manual-auth-hook 脚本来满足这个要求,但到那一步,您其实是在手工重造一个 DNS 插件。手动模式适合用来学习流程,或者用于一次真正的一次性签发,针对某个您暂时还无法自动化其 DNS 的域名,并且要在第 90 天之前早早设好提醒,因为 Let's Encrypt 已不再发送到期邮件。其余所有情况,都请用插件。

插件方案:在 Ubuntu 24.04 上使用 certbot-dns-cloudflare

DNS 插件持有您 DNS 服务商的一个 API 凭据,并自己完成整套 TXT 记录操作,签发时如此,每次续期时也如此。这里以 Cloudflare 作为示范,因为它是多数人需要的服务商插件,而且它已被打包进 Ubuntu。

我们的 Certbot 指南在 Ubuntu 24.04 上推荐使用 apt 包,这一立场对 Cloudflare 同样成立:

sudo apt update
sudo apt install certbot python3-certbot-dns-cloudflare

关于版本有一点老实话。24.04 的软件仓库把这个插件打包为 2.0.0 版,与之搭配的是 Certbot 2.9.0;apt policy python3-certbot-dns-cloudflare 会显示您这边的版本。这个版本差异是无害的,而且带作用域的 API 令牌可以正常工作,因为 24.04 中底层的 python3-cloudflare 库是 2.11.1,高于插件支持令牌所需的 2.3.1。在更早的 Ubuntu 版本中,这个库太旧、不支持令牌,这正是您在网上可能看到的那些警告的来源,说 apt 插件会强制使用 Global API Key。在 24.04 上,这些警告已不再适用。

在 Cloudflare 控制台中创建一个带作用域的 API 令牌,而不是 Global API Key:My Profile,然后 API Tokens,再 Create Token,只授予单一权限 Zone / DNS / Edit,并限定在您要为之签发证书的那一个区域。把它放进一个只有 root 能读的文件里:

sudo mkdir -p /root/.secrets
sudo tee /root/.secrets/cloudflare.ini > /dev/null <<'EOF'
dns_cloudflare_api_token = paste_your_scoped_token_here
EOF
sudo chmod 600 /root/.secrets/cloudflare.ini

如果该文件可被他人读取,Certbot 会检查其权限,并就 Unsafe permissions on credentials configuration file 发出警告。现在开始签发:

sudo certbot certonly \
  --dns-cloudflare \
  --dns-cloudflare-credentials /root/.secrets/cloudflare.ini \
  -d example.com -d '*.example.com'

插件通过 API 创建 TXT 记录,等待一段短暂的传播延迟,放行验证,然后再把记录删除。如果您区域的名称服务器接收变更较慢,可以用 --dns-cloudflare-propagation-seconds 60 加长等待时间。证书会落在 /etc/letsencrypt/live/example.com/ 中,您把 nginx 或 Apache 指向 fullchain.pemprivkey.pem,做法与基础指南所示完全一致,连同部署钩子一起配置。

如果 apt 里没有您服务商的插件

24.04 的软件仓库只打包了少数几家服务商的插件,其中包括 Cloudflare、Route 53、DigitalOcean 以及通用的 RFC 2136 接口。运行 apt search certbot-dns 可以查看这份列表。如果缺少您的服务商,这就是我们 apt 优先的建议唯一需要变通的地方:改用 snap 安装 Certbot 和插件,并且要先移除 apt 版 Certbot,以免两个续期定时器争抢 /etc/letsencrypt:

sudo apt remove certbot python3-certbot-dns-cloudflare
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot
sudo snap set certbot trust-plugin-with-root=ok
sudo snap install certbot-dns-yourprovider

snap 插件只会连接到 snap 版 Certbot;它无法扩展 apt 版,这正是这两种安装不能共存的原因。而如果您的 DNS 托管方根本不提供任何 API,您现实的选择是:把域名的 DNS 迁到有 API 的服务商,或者自己运行一台名称服务器并让 rfc2136 插件指向它。

续期:现在就验证,而不是等 60 天后

Certbot 会在 /etc/letsencrypt/renewal/example.com.conf 中记录每张证书是如何签发的,其中包括 authenticator = dns-cloudflare 和凭据路径,所以标准的每日两次定时器无需您帮忙就能续期。请对着 staging 环境把整个流程演练一遍:

sudo certbot renew --dry-run

通过就意味着凭据可用、验证从头到尾都能完成;60 天后真正的续期会走同一条路径。今天还有两件后续工作值得做。第一,磁盘上续好的证书,在 Web 服务器重新加载它之前不会改变任何东西,所以要按 nginx 和 Apache 指南所述接好部署钩子。第二,对待凭据文件要慎重:任何能读取它的人都能编辑您的 DNS 区域,这足以劫持您的邮件,或者通过他们自己的 DNS-01 验证。把它保持在 /root 下的 600 权限,把令牌限定在一个区域,一旦您怀疑泄露就轮换它。

什么时候您并不需要通配符

对许多子域,或对您无法预测的子域来说,通配符是正确的工具。但对其余一切情况,它都不是好的默认选择。

  • 只有一个子域,或只有寥寥几个已知子域:一张普通的 SAN(主体备用名称)证书更简单。certbot --nginx -d example.com -d www.example.com -d app.example.com 通过普通的 HTTP-01 就能覆盖多达 100 个名称,而且服务器上从不需要放一个 DNS API 凭据。
  • 通配符只匹配恰好一层标签。*.example.com 不覆盖裸域名 example.com,这正是上面的命令同时申请两者的原因;它也不覆盖 a.b.example.com,那需要 *.b.example.com
  • 每一个子域背后都是同一把私钥。如果持有它的机器被攻破,通配符覆盖的每一个名称都会同时受影响。
  • 如果由 Traefik 为您的容器终止 TLS(传输层安全),那您根本用不到 Certbot:Traefik 会自己通过 DNS-01 申请通配符证书,用的是同一类服务商令牌。

通配符真正物有所值的地方在于:按客户或按应用创建的子域,其速度快于您愿意重新签发证书的节奏;以及没有公开 80 端口的内部主机,比如只有通过 WireGuard VPN 才能访问的服务。DNS-01 从不连接被签发证书的那台主机,所以即便是一台完全私有的机器,也能持有一张受公开信任的证书。

FAQ

Certbot 能用 HTTP-01 签发通配符证书吗?

不能。HTTP-01 证明的是对一个主机名的控制,因为验证服务器会从那个确切的名称取回一个令牌文件。而通配符覆盖域名下的每一个名称,所以 Let's Encrypt 要求它必须用 DNS-01 验证,--nginx--apache--webroot--standalone 这几个验证器全都是基于 HTTP 的。唯一的途径是在 _acme-challenge.example.com 处放一条 TXT 记录,手动放置或由 DNS 插件放置。

通配符证书覆盖根域名吗?

不覆盖。通配符只匹配恰好一层标签,所以 *.example.com 覆盖 www.example.com,但不覆盖裸域名 example.com,也不覆盖 a.b.example.com。用 -d example.com -d '*.example.com' 在一张证书上同时申请这两个名称。这会产生两个验证,而两条 TXT 记录都位于同一个 _acme-challenge.example.com 名称下,所以要追加第二条记录而不删除第一条。

为什么我的通配符证书不能自动续期?

因为它是用 --manual 签发的。每次续期都需要一个全新的 TXT 值,而无人值守的定时器无从粘贴它,于是续期会以错误 An authentication script must be provided with --manual-auth-hook when using the manual plugin non-interactively 停下。请用诸如 certbot-dns-cloudflare 这样的 DNS 插件重新签发证书,或者提供通过服务商 API 编辑记录的 --manual-auth-hook--manual-cleanup-hook 脚本。

_acme-challenge TXT 记录多久才会出现?

这取决于您的 DNS 服务商:从几秒到几分钟不等。验证读取的是您区域的权威服务器,所以在手动运行时,请用 dig +short TXT _acme-challenge.example.com @1.1.1.1 检查,等到期望的值出现后再继续。用插件时,如果验证报告找不到记录,就通过插件的传播选项加长其内置的等待时间,例如 --dns-cloudflare-propagation-seconds 60

通配符证书比普通证书更不安全吗?

密码学上是完全一样的。差别在于运维层面:一把私钥覆盖每一个子域,所以一次被攻破波及的范围更广;而且自动化所需的 DNS API 凭据本身就是一个存放在服务器上的敏感机密。如果您只运行寥寥几个已知子域,一张 SAN 证书能避开这两处顾虑,而这恰恰正是本指南建议跳过通配符的场景。