Add DNSSEC validation check via delv (#3034)#3035
Conversation
Adds a "DNSSEC" line in the certificate-info block, right next to the
DNS CAA RR check. The new get_dnssec_status() function calls delv (the
BIND validating lookup utility) on $NODE and parses delv's status comment
lines:
- "; fully validated" -> pr_svrty_good, JSON severity OK
- "; partially validated" -> pr_svrty_good, JSON severity OK
- "; unsigned answer" -> pr_svrty_low, JSON severity LOW
- ";; resolution failed:" -> pr_svrty_high if the verdict text matches
a real DNSSEC failure ("trust chain",
"no valid signature", "bogus", "DNSKEY",
"NSEC", "insecure"); otherwise treated
as a transient resolver problem and
surfaced as pr_warning so we don't
misreport a network glitch as a domain
that's been tampered with.
If delv is not installed, the line prints a hint pointing at bind9 /
bind-utils and continues. --nodns and --ip=proxy short-circuit the
check the same way the CAA block already does.
A new HAS_DELV global is initialised next to the other resolver
HAS_* vars and set in check_resolver_bins(); it is also dumped under
--debug so users can see why the DNSSEC check was skipped.
Documentation in doc/testssl.1.md is updated under the certificate-info
section listing.
The Docker images are deliberately left alone in this PR; adding
bind-utils (opensuse) / bind-tools (alpine) is a separate sizing
decision for the maintainer.
Closes testssl#3034
|
Also Some But I don't think "partial validation" is an appropriate term even in such a case. A record secured with DNSSEC either has a valid trust chain leading up to the root trust anchor, or it doesn't and therefore can be tampered with. When "partial validation" term is used in the DNSSEC context it usually means: |
|
@ChrisJr404 : Thanks a lot, much appreciated! Your PR needs some massage, I should get to it by tomorrow. As far as the technical aspects are concerned. I am not the expert here, but maybe you can answer some of the questions from @Delicates . Thanks for your input @Delicates ! At this moment I 'd like not to have every record checked. It should be rather a matter of context. So first check should be A and AAAA. Then e.g. MX if STARTTLS via port 25 is being requested. More: step by step. |
|
I was thinking more in terms of establishing a scaffolding for DNS lookups which provides with it DNSSEC validation status that can be re-used regardless of which DNS record it is, and that can be later inserted into parts of Either way, the Without specifying the RR type I think |
|
All good points. To unpack:
If that direction sounds right, I will redo the PR along those lines. If you would rather hold for a broader DNS refactor or have it land in pieces, just let me know which order makes sense. |
|
Thanks! Agree to 1 to 3. Then: NODNS can have three values: empty / min / none . IIRC min is only used to avoid CAA and PTR lookups . Do we consider DNSSEC validations for A and AAAA records to be kind of mandatory? In any case: the user needs to know why it wasn't checked. That can be done via return value but feedback should be like in |
Previously validated at startup DNSSEC state of the That's why every mention of DNSSEC in With DNSSEC the absence of a resource record can also be validated. That's the difference between these two
I'm starting to think that maybe this warrants a separate DNS section in The contents of different resource records themselves can be later examined in the relevant sections of the
Though you could even have a situation when the Once TLS connection to an IP is established, the certificate presented by the server may also provide hostname validation. But with PKIX validation having thousands of public CAs in hundreds of mass-surveilling jurisdictions, you can't really trust it that much in the absence of DANE validation. |
|
Good points. I think you are right that the cleaner direction is a dedicated DNS section per RR type with its own validated/unsigned/absence states, not bolted into the cert section. The scope here is now clearly bigger than the single NODNS handling and the |
|
@ChrisJr404 : per RR validation is fine, but please no function per RR -- maybe I misunderstood that. That function should just serve as a scaffolding for every RR. Arguments should be FQDN + RR . Return code can be either like you did here or just an error code when the function failed. In that case the message should be returned via echo or printf (fixed strings is best to avoid user determined values). Where the output appears depends on the RR. As @Delicates suggested for A / AAAA records -- which is the first step we worry about it first -- I'd rather would place that after rDNS . If the client has no IPv6 networking enabled (IPv6_OK equals false) I would just skip that. Check of RR via DNSSEC can be introduced later and likely their output will be placed where appropriate. |

What this does
Adds a
DNSSECline to the certificate-info section (right afterDNS CAA RRand beforeCertificate Transparency) that callsdelvagainst$NODEand reports one of:fully validated/partially validated— pr_svrty_good, JSON severityOKnot signed— pr_svrty_low, JSON severityLOW, fileout textdomain not DNSSEC-signedbroken trust chain,no valid signature found,bogus*,DNSKEY*,NSEC*,*insecure*) — pr_svrty_high, severityHIGHWARNThe two buckets at the bottom matter: I didn't want a flaky upstream resolver to make
testssl.shclaim a domain has been tampered with. Soget_dnssec_status()separates "delv really said the chain is bogus" (return 2 → red) from "delv couldn't get an answer" (return 3 → orange warning).--nodns=min|noneand--ip=proxyshort-circuit the check before we shell out, mirroring the existing CAA block one screen above. Ifdelvisn't installed, the user sees(no "delv" binary, install bind9 / bind-utils)and the rest of the report continues as before.A new
HAS_DELVglobal is initialised next to the other resolverHAS_*vars and set incheck_resolver_bins(); it's also dumped under--debugso users can quickly see why the DNSSEC check was skipped.doc/testssl.1.mdis updated under the certificate-info bullet list.Why delv
dig +dnsseconly sets the DO bit and trusts whatever AD bit the configured resolver hands back, which is exactly the answer you can't trust if the resolver is the threat.delvperforms validation locally against the root trust anchor shipped with BIND, which is the right primitive for an authenticity check. That matches the framing in the issue.Open question for the maintainer
I deliberately left
Dockerfile(opensuse leap) andDockerfile.alpinealone. Addingbind-utils/bind-toolsto the images is a sizing decision (a few extra MB on a deliberately small container) and the existing image already shipsldnsfordrill. Happy to push a follow-up commit adding them to either or both images, or to leave it for a separate PR. The fallback path means today's Docker users see the "no delv binary" hint and nothing else changes.Caveats
DNS CAA RR (experimental)block since [Feature request] Implement DNSSEC test with delv? #3034 referenced PR DNS HTTPS RR (RFC 9460) for 3.3dev #2866 where that pattern already exists. Happy to iterate on naming, severity choices, or where the line lives.pr_svrty_highfor a known-bogus chain is my best guess at the right weight. If you'd rather have it aspr_svrty_mediumor as apr_warning, that's a one-line change.get_caa_rr_record()to fall back todelvwhendig/drill/host/nslookupfail, but that's well outside the scope of this PR (one functional change per PR perCoding_Convention.md).Tests
Local checks against:
bash -n testssl.shandshellcheck -S error testssl.share clean.Closes #3034.