CAA: Treat NXDOMAIN for a TLD as an error (#7104)
Change the CAA NXDOMAIN carve-out to only apply to registered domains
and their subdomains, not to TLDs.
Our CAA lookup function has a carveout that allows queries which receive
an NXDOMAIN response to be treated as though they received a successful
empty response. This is important due to the confluence of three
circumstances:
1) many clients use the DNS-01 method to validate issuance for names
which generally don't have publicly-visible A/AAAA records;
2) many ACME clients remove their DNS-01 TXT record promptly after
validation has completed; and
3) authorizations may be reused more than 8 hours after they were first
validated and CAA was first checked.
When these circumstances combine, the DNS rightly returns NXDOMAIN when
we re-check CAA at issuance time, because no records exist at all for
that name. We have to treat this as permission to issue, the same as any
other domain which has no CAA records.
However, this should never be the case for TLDs: those should always
have at least one record. If a TLD returns NXDOMAIN, something much
worse has happened -- such as a gTLD being unlisted by ICANN -- and we
should treat it as a failure.
This change adds a check that the name in question contains at least one
dot (".") before triggering the existing carve-out, to make it so that
the carve-out does not apply to TLDs.
Fixes https://github.com/letsencrypt/boulder/issues/7056
This commit is contained in:
parent
ebd87d4352
commit
6c92c3041a
14
bdns/dns.go
14
bdns/dns.go
|
|
@ -530,13 +530,13 @@ func (dnsClient *impl) LookupCAA(ctx context.Context, hostname string) ([]*dns.C
|
|||
dnsType := dns.TypeCAA
|
||||
r, err := dnsClient.exchangeOne(ctx, hostname, dnsType)
|
||||
|
||||
// Special case: for CAA, treat NXDOMAIN as a successful response
|
||||
// containing an empty set of records. This can come up in
|
||||
// situations where records were provisioned for validation (e.g.
|
||||
// TXT records for DNS-01 challenge) and then removed after
|
||||
// validation but before CAA rechecking.
|
||||
if err == nil && r.Rcode == dns.RcodeNameError {
|
||||
dnsClient.log.Infof("LookupCAA: got NXDOMAIN looking up %q", hostname)
|
||||
// Special case: when checking CAA for non-TLD names, treat NXDOMAIN as a
|
||||
// successful response containing an empty set of records. This can come up in
|
||||
// situations where records were provisioned for validation (e.g. TXT records
|
||||
// for DNS-01 challenge) and then removed after validation but before CAA
|
||||
// rechecking. But allow NXDOMAIN for TLDs to fall through to the error code
|
||||
// below, so we don't issue for gTLDs that have been removed by ICANN.
|
||||
if err == nil && r.Rcode == dns.RcodeNameError && strings.Contains(hostname, ".") {
|
||||
return nil, "", nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -142,6 +142,9 @@ func mockDNSQuery(w dns.ResponseWriter, r *dns.Msg) {
|
|||
record.Flag = 1
|
||||
appendAnswer(record)
|
||||
}
|
||||
if q.Name == "gonetld." {
|
||||
m.SetRcode(r, dns.RcodeNameError)
|
||||
}
|
||||
case dns.TypeTXT:
|
||||
if q.Name == "split-txt.letsencrypt.org." {
|
||||
record := new(dns.TXT)
|
||||
|
|
@ -444,6 +447,10 @@ bracewel.net. 0 IN CAA 1 issue "letsencrypt.org"
|
|||
caa.example.com. 0 IN CAA 1 issue "letsencrypt.org"
|
||||
`
|
||||
test.AssertEquals(t, removeIDExp.ReplaceAllString(resp, " id: XXXX"), expectedResp)
|
||||
|
||||
_, _, err = obj.LookupCAA(context.Background(), "gonetld")
|
||||
test.AssertError(t, err, "should fail for TLD NXDOMAIN")
|
||||
test.AssertContains(t, err.Error(), "NXDOMAIN")
|
||||
}
|
||||
|
||||
func TestIsPrivateIP(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -58,6 +58,8 @@ func (mock caaMockDNS) LookupCAA(_ context.Context, domain string) ([]*dns.CAA,
|
|||
case "com":
|
||||
// com has no CAA records.
|
||||
return nil, "", nil
|
||||
case "gonetld":
|
||||
return nil, "", fmt.Errorf("NXDOMAIN")
|
||||
case "servfail.com", "servfail.present.com":
|
||||
return results, "", fmt.Errorf("SERVFAIL")
|
||||
case "multi-crit-present.com":
|
||||
|
|
@ -589,6 +591,13 @@ func TestCAAFailure(t *testing.T) {
|
|||
t.Fatalf("Expected CAA rejection for reserved.com, got success")
|
||||
}
|
||||
test.AssertEquals(t, prob.Type, probs.CAAProblem)
|
||||
|
||||
_, prob = va.validate(ctx, dnsi("example.gonetld"), 1, chall)
|
||||
if prob == nil {
|
||||
t.Fatalf("Expected CAA rejection for gonetld, got success")
|
||||
}
|
||||
test.AssertEquals(t, prob.Type, probs.DNSProblem)
|
||||
test.AssertContains(t, prob.Error(), "NXDOMAIN")
|
||||
}
|
||||
|
||||
func TestFilterCAA(t *testing.T) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue