When the VA encounters CAA records, it logs the contents of those records. When those records were the result of following a chain of CNAMEs, the CNAMEs are included as part of the response from our recursive resolver. However, the current flow for logging the responses logs only the CAA records, not the CNAMEs. The additional detail would be informative when examining records of CAA checks that involve CNAMEs.
Notes from our deep dive through this today:
The point where we want to log the CNAMEs is
https://github.com/letsencrypt/boulder/blob/63eafa4a3b3c736b2ef4844d71a125036b1c2faa/va/caa.go#L67-L68
The call stack from there to the actual DNS CAA lookup is pretty deep:
va.checkCAA --> va.checkCAARecords --> va.getCAASet --> va.parallelCAALookup --> dns.LookupCAA --> dns.exchangeOne
There are two problems here:
Potential solution (predicated on learning a lot more about how unbound actually works, and how to get info out of it):
LookupCNAME method to boulder's dns.go libraryva.checkCAA, all the way at the top of the stack, to get any CNAMEs that need to be loggedEven the lowest level of that call stack assumes that the remote dns resolver (unbound) will automatically follow all CNAMEs it encounters while trying to look up the requested records
I believe this is part of the RFC in terms of what a recursive resolver is specified to return to its clients. Worth double checking. But it is definitely true in practice.
Add a new LookupCNAME method to boulder's dns.go library
I would prefer to avoid this solution, since we will wind up reimplementing the CNAME chasing part of DNS in Boulder; right now we get to leave most of the DNS semantics to our recursive resolver. This would also mean additional queries (potentially a lot of them), and the possibility of getting different answers for those queries than the ones Unbound relied on.
I mentioned on a call a few weeks back, but should have written in the issue: This is not so much "log CNAMEs" as "log the whole DNS response that Boulder received in response to a CAA query, not just the CAA records."
I definitely agree that there's a lot of call stack to pass the whole response through - that makes this a challenging task. But I think it's worth the effort to save on the network calls.
bdns.LookupCAA:
https://github.com/letsencrypt/boulder/blob/aa09c1661cd1d313d0a0d41b93c38407f4314713/bdns/dns.go#L153
CAAs, response, nilresponse (docs: https://godoc.org/github.com/miekg/dns#Msg.String)CAAs, response, nilva.caaResult and va.parallelCAALookup:
https://github.com/letsencrypt/boulder/blob/aa09c1661cd1d313d0a0d41b93c38407f4314713/va/caa.go#L122-L125
response, a string containing the response returned by bdns.LookupCAAhttps://github.com/letsencrypt/boulder/blob/aa09c1661cd1d313d0a0d41b93c38407f4314713/va/caa.go#L140-L156
bdns.LookupCAA to add responseva.getCAASet:
https://github.com/letsencrypt/boulder/blob/aa09c1661cd1d313d0a0d41b93c38407f4314713/va/caa.go#L158-L172
responseva.CheckCAARecords:
https://github.com/letsencrypt/boulder/blob/aa09c1661cd1d313d0a0d41b93c38407f4314713/va/caa.go#L184-L200
va.getCAASet, adding responseresponseva.checkCAA:
https://github.com/letsencrypt/boulder/blob/aa09c1661cd1d313d0a0d41b93c38407f4314713/va/caa.go#L49-L52
va.CheckCAARecords, adding responsehttps://github.com/letsencrypt/boulder/blob/aa09c1661cd1d313d0a0d41b93c38407f4314713/va/caa.go#L67-L70
response to the va.log.AuditInfof callI like this approach! One thing to watch out for: The String() output for the message may be kind of verbose. I think that's fine on balance, since nonempty CAA responses are fairly rare, and the String() output (i.e. dig-like output) is more readable than we we currently emit. I would just make sure that in the empty case we log something that's quite compact, or we'll increase our log size a lot.
Most helpful comment
I believe this is part of the RFC in terms of what a recursive resolver is specified to return to its clients. Worth double checking. But it is definitely true in practice.
I would prefer to avoid this solution, since we will wind up reimplementing the CNAME chasing part of DNS in Boulder; right now we get to leave most of the DNS semantics to our recursive resolver. This would also mean additional queries (potentially a lot of them), and the possibility of getting different answers for those queries than the ones Unbound relied on.
I mentioned on a call a few weeks back, but should have written in the issue: This is not so much "log CNAMEs" as "log the whole DNS response that Boulder received in response to a CAA query, not just the CAA records."
I definitely agree that there's a lot of call stack to pass the whole response through - that makes this a challenging task. But I think it's worth the effort to save on the network calls.