ACME 客户端已完成域名验证,却在签发阶段报 urn:ietf:params:acme:error:caa,或提示 CAA record for example.com prevents issuance。这通常和 Nginx、证书文件权限无关,真正该查的是 DNS:父域继承、CNAME、DNSSEC 和权威服务器响应。别急着重装 Certbot,方向错了,只是在给键盘加班。

一、CAA 到底拦了什么
CAA 是一种 DNS 资源记录,用来声明哪些 CA 可以为某个域名签发公开信任证书。它发生在 CA 的签发决策阶段,不是浏览器拿到证书后的信任校验。按照 RFC 8659,CAA 是“签发前授权控制”:记录允许某个 CA,只代表满足了一项必要条件,并不等于域名验证一定通过。
一个常见记录如下:
example.com. 300 IN CAA 0 issue "letsencrypt.org"
0 是 flags,issue 是属性标签,letsencrypt.org 是被允许的 CA 标识。没有配置任何 CAA 时,公开 CA 通常可在完成其他验证后签发;一旦配置,就要确保目标 CA 明确被允许。
二、先把错误分成“拒绝”与“查不到”
看到 CAA error,先别一股脑修改记录。它大致分两类:
| 现象 | 含义 | 优先检查 |
|---|---|---|
| CAA record prevents issuance | 查到了有效 CAA,但目标 CA 未被允许 | issue、issuewild、父域继承、CNAME 目标 |
| SERVFAIL | 解析链无法给出可信结果 | DNSSEC、权威 NS、错误签名 |
| timeout | 权威 DNS 没及时响应 | 53/UDP、53/TCP、防火墙、NS 可用性 |
| unknown critical tag | 设置了 critical flag,但 CA 不认识该标签 | flags=128 与自定义标签 |
CA 在无法确认“是否被允许”时不会赌一把继续签发。这个机制看起来有点轴,但证书签发宁可谨慎,也不能靠猜。
三、用 dig 查当前域名与父域继承
先查申请证书的精确域名,再逐级查父域:
dig CAA api.example.com +noall +answer
dig CAA example.com +noall +answer
dig CAA api.example.com +trace
CAA 查询从待签发域名向父级查找,并采用离目标最近的一组有效记录。若 api.example.com 没有 CAA,而 example.com 有记录,父域规则通常会被继承;子域有 CAA 时则可覆盖父域。
只在 DNS 控制台里搜 api,很容易漏掉根域限制。完整域名、注册域和委托子区都要看。
四、看懂 issue 与 issuewild
issue 控制普通证书签发;在没有任何 issuewild 记录时,它也控制通配符签发。只有需要为通配符设置不同权限时,才单独使用 issuewild。例如:
example.com. 300 IN CAA 0 issue "letsencrypt.org"
example.com. 300 IN CAA 0 issuewild ";"
这组配置允许 Let’s Encrypt 签发普通证书,但空的 issuewild 值会禁止所有 CA 签发通配符证书。如果申请的是 *.example.com,普通证书能签、通配符却报 CAA 错误,就应重点检查这一项。
同一属性可以有多条记录,权限是“加法”:其中任意一条允许目标 CA,就可通过该项授权。不要把多条 CAA 误当成后写的覆盖前写的,它不是普通配置文件的最后一行生效。
五、CNAME 场景别只查原域名
CAA 检查会跟随 CNAME。假设:
app.example.com. 300 IN CNAME service.vendor.example.
service.vendor.example. 300 IN CAA 0 issue "other-ca.example"
即使 example.com 允许 Let’s Encrypt,CNAME 目标上的 CAA 仍可能影响签发。可以这样确认:
dig CNAME app.example.com +short
dig CAA service.vendor.example +noall +answer
DNS-01 的 _acme-challenge CNAME 委托和业务域名 CNAME 不同:前者主要改变 TXT 验证的查询位置,CAA 仍应按实际申请标识与规范检查。
六、SERVFAIL 与 timeout 怎么定位
如果返回的是 SERVFAIL,而不是明确的“CA 未授权”,先比较递归 DNS 与权威 DNS 的结果:
dig CAA example.com @1.1.1.1
dig CAA example.com @8.8.8.8
dig NS example.com +short
dig CAA example.com @ns1.example-dns.com +dnssec
Let’s Encrypt 的 CAA 官方说明指出,SERVFAIL 常见原因包括 DNSSEC 验证失败、权威服务器错误处理空 CAA 响应,以及权威 NS 故障。timeout 则常见于防火墙丢弃不熟悉的 DNS 查询类型、某台 NS 不响应,或 TCP 53 被漏放。
修复时要逐台查询权威 NS。如果三台中两台正常、一台超时,也不能当作“多数通过就行”;CA 可能刚好问到那台掉队的服务器。
七、修改记录后别立刻无限重试
确认要允许 Let’s Encrypt 后,可在 DNS 服务商中添加:
example.com. 300 IN CAA 0 issue "letsencrypt.org"
保存后先从多个公共解析器查询,再触发 ACME:
dig CAA example.com @1.1.1.1 +short
dig CAA example.com @8.8.8.8 +short
certbot renew --dry-run
--dry-run 会走测试环境,可验证续期链路,避免连续撞生产限额。若 DNS 有缓存,要等旧 TTL 消退;本机看到新值,不代表 CA 已经看到。
其他 ACME 客户端也一样:先查权威 DNS,再用 staging 复跑,最后才生产签发。
八、自动续期系统要把 CAA 做成前置检查
续期前才发现 CAA 被改了更麻烦。自动化系统可在创建 ACME Order 前预检:
- 枚举证书中的每个 SAN,而不只是主域名;
- 查询 CAA、CNAME、NS 与 DNSSEC 状态;
- 区分明确拒绝、SERVFAIL、timeout 和临时网络错误;
- 保存命中的域名、记录值、权威 NS 与查询时间;
- 临时错误退避重试,明确拒绝立即告警,不盲目创建新 Order。
CAA 记录还可能带 validationmethods 或 accounturi 参数。前者限制可用的 ACME 验证方式,后者可限制特定 ACME Account。只检查是否包含 letsencrypt.org 字符串还不够,参数不匹配时照样可能失败。
九、最终验收清单
- 申请证书的每个域名都已单独查询 CAA;
- 父域、子域覆盖与 CNAME 目标均已检查;
- 普通证书核对
issue,通配符同时核对issuewild; - 所有权威 NS 都能通过 UDP/TCP 53 正常回答;
- DNSSEC 无 SERVFAIL、过期签名或委托链错误;
- 修改后的记录已在多个递归 DNS 可见;
- 先通过 staging 或 dry-run,再执行生产签发;
- 最终确认证书 SAN、签发者和有效期符合预期。
CAA 报错并不神秘:它要么明确说“不允许”,要么因为 DNS 异常无法确认“允许”。先把这两类拆开,再沿着精确域名、父域、CNAME 和权威 NS 往下查,通常比重装客户端快得多。

466

被折叠的 条评论
为什么被折叠?



