引入
之前在聊聊证书中提到,在申请时遇到了Type: caa Detail: CAA record for domain.cn prevents issuance
问题。到底CAA是什么,为什么当时不能申请证书?真的是挖了好久的一个坑,发现最近一直都在填坑诶,这次我们来解释一下这个问题。
CAA(Certification Authority Authorization,即证书颁发机构授权)是一项防止HTTPS证书错误签发的安全措施,于2013年1月通过互联网工程任务组(IETF)的批准列为RFC6844,2017年3月,CA浏览器论坛投票通过187号提案,要求CA机构从2017年9月8日起执行CAA强制性检查。
CAA标准是指域名所有者在其域名DNS记录的CAA字段中,授权指定CA机构为其域名签发证书,CA机构签发证书时强制性检查CAA记录,如果检查发现未获得授权,将拒绝为该域名签发证书,从而防止未授权HTTPS证书错误签发。如果域名所有者没有为其域名设置CAA记录,那么任何CA都可以为其域名颁发证书。
要顺利执行CAA标准,需要三方共同完成:
- DNS服务商:升级DNS系统使其支持CAA记录设置;
- 域名所有者:在DNS记录的CAA字段中,授权指定的几家CA机构为域名签发证书;
- CA机构: CA机构签发证书之前,强制性检查DNS CAA记录。
这是沃通在2017年的一篇资讯,介绍了CAA的作用,简单来讲就是域名证书的申请,要进一步的进行约束。证书的发放,会使得获得私钥的人有代表证书持有人的身份,如果让第三方在持有人不知道的情况下申请,则会带来不必要的麻烦。因此IETF组织认为有必要让域名持有者认定,自己的域名到底可以让哪些证书颁发机构CA(Certification Authority)签发,则白名单之外的CA则不能签发,以起到对域名持有者的保护作用。
CAA记录的构成
先简单介绍一下CAA记录长什么样子。根据规范,一条CAA记录可以用CAA <flag> <tag> <value>
表示,是由一个标志字节的<flag>
和一个被称为属性的 <tag>
-<value>
(标签-值)对组成。每个标签的意义和范围如下:
标签 | 描述 |
---|---|
flag | 可填写0或128,用于标志认证机构。 默认情况下填写0,表示如果颁发证书机构无法识别本条信息,进行忽略。 |
tag | 支持 issue、issuewild 和 iodef。 |
value | CA 的域名或用于违规通知的电子邮箱。 |
而定义的tag
标签的意义如下:
字段 | 说明 |
---|---|
issue | CA 授权单个证书颁发机构发布的任何类型域名证书。 |
issuewild | CA 授权单个证书颁发机构发布主机名的通配符证书。 |
iodef | CA 可以将违规的颁发记录 URL 发送给某个电子邮箱。 |
CAA记录的检索规则
根据cab论坛的文章,设被查询的域名为X,X域名直接关联CAA记录为CAA(X),X域名CNAME指向的域名为A(X),X域名父级域名(如a.b.com
的父级域名为b.com
,而b.com
没有父级域名)为P(X),则检索规则如下:
- 如CAA(X)直接存在,则返回CAA(X)
- 若A(X)存在,在规定的步数(最少8步)内不能找到X在CNAME链条的最终域名A^n^(X),则返回报错
CNAME reaches the maximum number of 8
- 若A(X)存在,在规定的步数(最少8步)内能找到X在CNAME链条的最终域名A^n^(X),而且CAA(A^n^(X))存在,则返回CAA(A^n^(X))
- 若X不为主域名
xxx.com
,且CAA(P(X))存在,则返回CAA(P(X)),否则依次找CAA(P(P(X)))等,直到检索主域名的CAA记录 - 如第四步找不到CAA记录,返回空
用Python代码写起来就是
# step 1 & 3 |
可能有的人想问了,如果在第3步中,CNAME链条的某个域名配置了CAA记录呢?根据测试,CNAME和CAA记录不共存,在DNSPods添加记录时会报错。
失误的复现
当时申请的是*.oops-sdu.cn
及oops-sdu.cn
域名的SSL证书,而oops-sdu.cn
设置的是oops-sdu.github.io
的CNAME解析。根据上面的规则,CA会查询oops-sdu.github.io
的CAA记录,查询结果如下:
;; ANSWER SECTION: |
而根据上面的介绍,网站只允许digicert.com
申请单域名及泛域名的证书,而letsencrypt.org
只能申请单域名证书、不能申请泛域名证书。因此使用letsencrypt.org
同时申请*.oops-sdu.cn
及oops-sdu.cn
是会失败的,不过只申请oops-sdu.cn
会成功。当然,根据上述第四步,在子域名没有额外设置 CNAME 和 CAA 相关记录的时候,那么我们的子域名也会继承 apex 域名的 CAA 记录,使得子域名下还是只能单网站申请证书,不能申请子域名下的泛域名证书,影响还是蛮大的。同样,这也是意味着,在子域名没有额外设置 CNAME 和 CAA 相关记录的时候,在 apex 域名下的 CAA 限制会影响整个域名,尤其是对于不在 CAA 记录里许可的证书发行商。
其实,GitHub Pages在配置域名的时候,提起过对apex域名的解析配置方法。方法提出填写A记录和AAAA记录(IPv6),就是防止CAA记录被同步转移。其实同步转移的不只是CAA记录,TXT记录和MX记录(用于域名绑定企业邮箱)等记录也会被同步重定向(甚至有时候重定向的优先级更高,忽略域名本身的记录),因此提醒大家在配置@域名时慎重选择CNAME记录。
更新:阿里云和腾讯云的域名解析上,充分考虑到了域名CNAME影响MX和TXT记录的问题,于是他们分别对@主机的解析规则进行了一定的修改,并列出冲突的情况(见腾讯云冲突规则,腾讯云CNAME加速,阿里云冲突规则)。
设置CAA记录
虽然上面说2013年这个CAA就已经出现了,但是到2017年11月沃通资讯发布的时候,国内的DNS厂商还不支持CAA记录的添加。不过目前来看,国内阿里云、腾讯云都已经支持相关记录的添加了。
按照CAA <flag> <tag> <value>
的规则,我们一般填写的时候,第一个直接写0
,第二个从三个里选一个,例如issue
,第三个要写一个英文引号引起来的域名"letsencrypt.org"
,如果是邮箱则是mailto:email@icys.top
,连起来则是0 issue "letsencrypt.org"
,填写在属性值里即可。至于其他的项目,基本按默认填写即可。关于DNSPods配置CAA记录的文档,见CAA记录。
一条记录可以设置一条信息(一个CA或一个网址),我们可以通过创建多条记录来允许更多的CA(创建多条iodef
的记录是被允许的,但不知道CA会怎么处理)。
致谢
感谢Ephen的文章SSL 证书颁发机构将对域名强制 CAA 检查,到底什么是 CAA ? CAA 记录详解,Deepzz的文章DNS 一站到家之 CAA 记录的启发,也欢迎大家了解一下这两位的文章。
感谢DNSPods小泽和龙及纯两位朋友的帮助,帮忙排查出证书申请的问题,才有了这一篇文章。