Skip to content

Add DNSSEC validation check via delv (#3034)#3035

Open
ChrisJr404 wants to merge 1 commit intotestssl:3.3devfrom
ChrisJr404:feat/dnssec-delv-3034
Open

Add DNSSEC validation check via delv (#3034)#3035
ChrisJr404 wants to merge 1 commit intotestssl:3.3devfrom
ChrisJr404:feat/dnssec-delv-3034

Conversation

@ChrisJr404
Copy link
Copy Markdown

What this does

Adds a DNSSEC line to the certificate-info section (right after DNS CAA RR and before Certificate Transparency) that calls delv against $NODE and reports one of:

  • fully validated / partially validated — pr_svrty_good, JSON severity OK
  • not signed — pr_svrty_low, JSON severity LOW, fileout text domain not DNSSEC-signed
  • a high-severity DNSSEC failure (broken trust chain, no valid signature found, bogus*, DNSKEY*, NSEC*, *insecure*) — pr_svrty_high, severity HIGH
  • anything else delv complained about (timeouts, SERVFAIL from the configured resolver, generic resolution failure) — pr_warning, severity WARN

The two buckets at the bottom matter: I didn't want a flaky upstream resolver to make testssl.sh claim a domain has been tampered with. So get_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|none and --ip=proxy short-circuit the check before we shell out, mirroring the existing CAA block one screen above. If delv isn't installed, the user sees (no "delv" binary, install bind9 / bind-utils) and the rest of the report continues as before.

A new HAS_DELV global is initialised next to the other resolver HAS_* vars and set in check_resolver_bins(); it's also dumped under --debug so users can quickly see why the DNSSEC check was skipped.

doc/testssl.1.md is updated under the certificate-info bullet list.

Why delv

dig +dnssec only 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. delv performs 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) and Dockerfile.alpine alone. Adding bind-utils / bind-tools to the images is a sizing decision (a few extra MB on a deliberately small container) and the existing image already ships ldns for drill. 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

  • The issue ([Feature request] Implement DNSSEC test with delv? #3034) is from today and there's no maintainer feedback yet on the exact desired UX. I went with the same look-and-feel as the 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_high for a known-bogus chain is my best guess at the right weight. If you'd rather have it as pr_svrty_medium or as a pr_warning, that's a one-line change.
  • I considered also extending get_caa_rr_record() to fall back to delv when dig/drill/host/nslookup fail, but that's well outside the scope of this PR (one functional change per PR per Coding_Convention.md).

Tests

Local checks against:

$ ./testssl.sh -S example.com   # signed, public resolver chain healthy
   DNSSEC (experimental)        fully validated

$ ./testssl.sh -S example.com --nodns=min
   DNSSEC (experimental)        (instructed to minimize/skip DNS queries)

$ ./testssl.sh -S example.com   # with delv removed from PATH
   DNSSEC (experimental)        (no "delv" binary, install bind9 / bind-utils)

$ ./testssl.sh -S example.com --jsonfile=/tmp/out.json
   ... "id": "DNSSEC <hostCert#1>", "severity": "OK", "finding": "fully validated"

bash -n testssl.sh and shellcheck -S error testssl.sh are clean.

Closes #3034.

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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature request] Implement DNSSEC test with delv?

1 participant