ACME DNS-01报NXDOMAIN:_acme-challenge记录与权威DNS排查

申请通配符或多域名证书时,ACME DNS-01常见的第一句报错就是 NXDOMAIN。它不是"TXT值不对"的同义词,而是查询链路里连这个名字都没有找到。先把重试按钮放一边:查错名称,重试只是在给DNS服务器增加心理负担。本文把验证域名、CNAME委托、权威NS、TXT传播和DNSSEC拆开,定位到哪一层再修哪一层。

一、NXDOMAIN到底说明了什么

DNS-01要求客户端把客户端根据token与账户密钥派生出的验证值放在 _acme-challenge.待验证域名 的TXT记录中,ACME服务器再查询该名称并比对值。Let's Encrypt官方文档明确了这个名称和TXT验证方式;RFC 8555也把dns-01定义为由ACME服务器查询DNS记录完成验证。

NXDOMAIN的重点是"名称不存在",和NOERROR但没有TXT、SERVFAIL、超时不是一回事。不同解析器可能缓存负结果,所以改完记录后不能只看本机一次查询。

二、先确认ACME真正查询的名称

最容易犯的错是把待签发域名直接加TXT,或者通配符写成带星号的查询名。验证 *.example.com 时,常规DNS-01查询仍围绕 _acme-challenge.example.com,不是 _acme-challenge.*.example.com。实际名称以ACME客户端日志和订单授权对象为准,不要凭记忆拼接。

bash 复制代码
DOMAIN=example.com
NAME="_acme-challenge.${DOMAIN}"
printf 'query=%s\n' "$NAME"
dig +noall +answer "$NAME" TXT
dig +noall +authority "$NAME" TXT

第一条看答案,第二条看权威区是否返回SOA。若查询名写错,后面的CNAME和TXT检查都没有意义。生产脚本应把最终规范化FQDN写入日志,避免把根域、子域和通配符混成一锅。

三、区分NOERROR空答案与NXDOMAIN

两者在排障上完全不同:NXDOMAIN通常表示权威服务器认为该名字不存在;NOERROR/NODATA表示名字存在,但没有所请求类型的记录。若父域存在而子域不存在,权威响应里的SOA通常能帮助判断否定缓存边界。

bash 复制代码
dig +noall +comments +authority _acme-challenge.example.com TXT
dig +trace _acme-challenge.example.com TXT
# 只把实际返回的状态写入诊断日志,不用grep到空输出就判定成功
dig +noall +answer _acme-challenge.example.com TXT

不要只执行 dig TXT 后看"没有输出"。保存 status、ANSWER、AUTHORITY 和 SERVER,才能区分没有记录、被权威拒答和本地递归缓存异常。+trace适合定位委派链路,不代表ACME服务器一定使用同一个递归解析器。

四、沿权威NS一路查,别被本地缓存带偏

先查域名的NS,再直接询问权威服务器。权威服务器没有记录而公共递归仍有旧TXT,说明缓存尚未过期;权威已有记录而本地仍NXDOMAIN,则优先检查负缓存、查询线路和DNSSEC。

bash 复制代码
ZONE=example.com
dig +short NS "$ZONE"
for ns in $(dig +short NS "$ZONE"); do
  printf "\nserver=%s\n" "$ns"
  dig +noall +comments +answer +authority \
    @"$ns" _acme-challenge."$ZONE" TXT
done

本例验证区域根域;查子域时单独指定NAME,不能把查询名改成区域根域。ZONE应为实际权威区域,子区委派需用+trace确认。若DNS服务商控制台显示记录,但权威NS回答没有它,常见原因是改错了账户、区域或记录名;若权威回答正确,再看递归缓存和传播,不要继续改记录。

五、CNAME委托时查目标,不要同时塞CNAME和TXT

如果把 _acme-challenge.example.com CNAME到专用验证域,TXT通常应放在CNAME目标上。DNS名称不能同时把同名CNAME和其他数据记录当成普通并列项使用;委托目标也必须再追到它自己的权威NS。

bash 复制代码
NAME=_acme-challenge.example.com
dig +noall +answer "$NAME" CNAME
TARGET=$(dig +short "$NAME" CNAME | sed 's/\.$//')
if [ -n "$TARGET" ]; then
  dig +noall +answer "$TARGET" TXT
  dig +noall +authority "$TARGET" TXT
fi

这里有个很隐蔽的坑:CNAME目标末尾的点表示完整域名;API填值时有的服务商要求相对名,有的要求FQDN。最终以权威查询结果为准。若历史TXT残留,先确认是否属于仍在进行的并发验证,再按服务商语义删除,不能用"清空全部TXT"这种粗暴方案。

六、DNSSEC、SERVFAIL和传播延迟怎么分层

NXDOMAIN修正后变成SERVFAIL,方向可能已经从记录名转到DNSSEC、签名过期或委派不一致。对比普通解析器、权威NS和带DNSSEC校验的结果;不要把SERVFAIL当作TXT值错误。

响应现象 通常含义 先做什么
NXDOMAIN 权威认为名称不存在 查验证名称、区域和委派
NOERROR无TXT 名称存在但没有TXT 查记录类型、目标和生效区
SERVFAIL 解析链或DNSSEC失败 查权威状态、签名和委派
NOERROR有旧TXT 能查到但值不匹配 保留并发值,核对token后重试

DNS传播不是一个固定秒数。TTL影响缓存,但负缓存还受SOA中的否定缓存参数影响;权威服务器、递归解析器和CA验证器看到的时间可能不同。修改后等待并重复查询,比连续创建新订单更稳妥。

七、用三种方案做中立对照

纯命令行可以直接调用DNS Provider API,acme.sh等开源客户端也能通过DNS API自动写入TXT;平台化方案则通常把订单、权限、重试和多目标部署统一管理。无论选哪种,核心验收都一样:写入的是正确验证名称,权威NS能查到正确TXT,验证完成后按策略清理旧值。本文只作技术边界对照,不把某个方案写成唯一答案。参考:Let's Encrypt DNS-01文档、acme.sh开源仓库 与 CertbotX。

八、续期前验收清单

  • 从ACME日志取出准确授权域名,确认没有把星号写进查询名。
  • 递归查询与至少一台权威NS分别读取TXT,并记录status、ANSWER和AUTHORITY。
  • 存在CNAME时只沿目标查TXT,确认目标区域和末尾点语义。
  • 把NXDOMAIN、NOERROR空答案、SERVFAIL和旧TXT分开处理。
  • 确认验证完成后再清理对应TXT,不删除并发订单仍需要的值。
  • 最终用ACME staging或受控测试订单复验,再接入自动续期和告警。

事实依据:RFC 8555 第8.4节、Let's Encrypt Challenge Types文档。线上证书是否真正更新,还要另行读取部署节点的证书指纹和有效期;DNS查询成功不等于Nginx已经加载新证书。

相关推荐
Lsetea10 小时前
curl指定CA目录仍报证书不受信任:--capath与OpenSSL rehash排查
运维·https·ssl·openssl·curl
Zelman15 小时前
协议栈数据流全景分析
网络协议·tcp/ip·https
Lsetea20 小时前
OpenSSL报path length constraint exceeded:CA层级与pathlen排查
运维·https·ssl证书·openssl·证书链
Lsetea1 天前
OpenSSL s_client退出码为0却证书验证失败:严格校验与Shell管道排查
https·shell·ssl证书·openssl·tls
Lsetea1 天前
OpenSSL报permitted subtree violation:证书SAN与CA名称约束排查
运维·https·ssl证书·openssl·证书链
2501_916007472 天前
苹果应用商店App Store上架费用标准及原因解析
android·ios·小程序·https·uni-app·iphone·webview
zxanz12 天前
https 的基本原理是什么样的?
网络协议·http·https
Lsetea2 天前
OpenSSL verify报error 20:CAfile、fullchain与本地信任链分层排查
nginx·https·ssl证书·openssl·证书链
00后程序员张3 天前
苹果App Store上架指南:费用、原理与步骤详解
android·ios·小程序·https·uni-app·iphone·webview