Certbot 如何通过 DNS-01 申请通配符证书
了解 Certbot 使用 DNS-01 申请通配符证书的完整流程:创建 _acme-challenge TXT 记录、安装 DNS 插件,并通过 DNS 提供商 API 实现自动续期。
为什么通配符证书需要 DNS-01
通配符证书覆盖域名的所有一级子域名:*.example.com 匹配 app.example.com、blog.example.com 以及其他任何下一级名称。Let's Encrypt 只能通过 DNS-01 challenge 签发通配符证书,因此 Certbot 必须在 _acme-challenge.example.com 发布 TXT 记录,以证明您控制该域名的 DNS。HTTP-01 challenge 无法满足要求,因为提供 token 文件只能证明您控制验证服务器获取该文件的那个主机名。通配符证书声明的是该域名下所有可能的名称,而能够代表整个命名空间的唯一公开记录就是 DNS。
这一项要求决定了本页的其他所有内容。要通过 DNS-01,您必须能够在该域名的 DNS zone 中创建 TXT 记录,可以手动创建,也可以通过 DNS 提供商的 API(application programming interface)创建。手动方式只能成功一次,续期时会失败,具体原因如下所示。通过 Certbot DNS plugin 使用 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下。两个记录必须同时存在。添加第二个记录是正确的;用第二个记录替换第一个记录会导致第一个质询失败。 - 验证过程会查询您的权威服务器,但 DNS 提供商的控制面板可能需要 1 分钟或更长时间,才能将新记录推送到这些服务器。在运行验证前,请从外部检查记录:
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 命令确认记录已生效,然后再按 Enter。由于本次运行同时请求裸域名和通配符域名,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.')您可以编写 --manual-auth-hook 脚本调用 DNS 提供商的 API 来满足此要求,但这样实际上是在手动重新实现 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 token 也可以正常使用,因为 24.04 中底层的 python3-cloudflare 库版本为 2.11.1,高于插件支持 token 所需的 2.3.1。较旧的 Ubuntu 版本中,该库版本过低,无法支持 token。这就是网上关于 apt 插件会强制使用 Global API Key 的警告来源。在 24.04 中,这些警告已不再适用。
在 Cloudflare 控制面板中创建限定范围的 API token,不要使用 Global API Key:依次进入 My Profile、API Tokens 和 Create Token,设置唯一权限 Zone / DNS / Edit,并将其限制为您要签发证书的那个 zone。将 token 保存到只有 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 记录,等待一小段传播时间,执行验证,然后再次删除这些记录。如果您的 zone 的名称服务器处理变更较慢,请通过 --dns-cloudflare-propagation-seconds 60 增加等待时间。证书会保存到 /etc/letsencrypt/live/example.com/ 中。按照基础指南中的说明,将 nginx 或 Apache 指向 fullchain.pem 和 privkey.pem,并配置 deploy hook。
如果服务商的插件不在 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-yourprovidersnap 插件只能连接 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 指南中的说明配置 deploy hook。其次,请妥善保护凭据文件:任何能够读取该文件的人都可以修改您的 DNS 区域,从而重定向您的邮件,或自行通过 DNS-01 挑战验证。请将该文件保存在 /root 下,并设置为 mode 600;将令牌权限限制为单个区域。如果怀疑凭据泄露,请立即轮换令牌。
不需要通配符时
对于许多子域名,或无法预先确定的子域名,通配符是合适的工具。但对于其他情况,不应默认使用通配符。
- 只有一个子域名,或只有少量已知子域名:普通 SAN(subject alternative name)证书更简单。
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(transport layer security),则完全不需要使用 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 而停止。请使用 DNS 插件(例如 certbot-dns-cloudflare)重新签发证书,或者提供 --manual-auth-hook 和 --manual-cleanup-hook 脚本,通过服务商的 API 修改该记录。
_acme-challenge TXT 记录需要多长时间才会出现?
这取决于 DNS 服务商,可能需要几秒到几分钟。验证会查询区域的权威服务器,因此请使用 dig +short TXT _acme-challenge.example.com @1.1.1.1 检查,并等待预期值出现后再继续手动运行。使用插件时,如果验证报告未找到该记录,可以通过插件的传播选项增加内置等待时间,例如 --dns-cloudflare-propagation-seconds 60。
通配符证书是否不如普通证书安全?
密码学机制完全相同。差异在于运维方式:一把私钥覆盖所有子域名,因此密钥泄露后影响范围更大;自动化所需的 DNS API 凭据本身也是敏感机密,且会存储在服务器上。如果只运行少量已知子域名,SAN 证书可以避免这两个问题。这正是本指南建议跳过通配符证书的情况。