No description
  • Go 98.1%
  • Dockerfile 1.2%
  • Makefile 0.7%
Find a file
Pierre-Olivier Mercier 77dfb82313 fix: return ErrNoCollector instead of panicking on nil CollectFn
Hosts that register the provider only for its definition (externalizable
checker) construct it with a nil CollectFn. If the local ObservationContext
ends up calling Collect, the previous code dereferenced a nil function
value and crashed the goroutine. Surface a typed error so rules degrade
to a clean StatusError state.
2026-04-30 09:23:26 +07:00
checker fix: return ErrNoCollector instead of panicking on nil CollectFn 2026-04-30 09:23:26 +07:00
internal/collect Initial commit 2026-04-26 21:54:28 +07:00
plugin Initial commit 2026-04-26 21:54:28 +07:00
.gitignore Initial commit 2026-04-26 21:54:28 +07:00
Dockerfile Initial commit 2026-04-26 21:54:28 +07:00
go.mod Initial commit 2026-04-26 21:54:28 +07:00
go.sum Initial commit 2026-04-26 21:54:28 +07:00
LICENSE Initial commit 2026-04-26 21:54:28 +07:00
main.go Initial commit 2026-04-26 21:54:28 +07:00
Makefile Initial commit 2026-04-26 21:54:28 +07:00
NOTICE Initial commit 2026-04-26 21:54:28 +07:00
README.md Initial commit 2026-04-26 21:54:28 +07:00

checker-dnsviz

DNSSEC checker for happyDomain, implemented as a thin wrapper around DNSViz.

The container ships dnsviz (Python) alongside the Go binary that exposes the standard happyDomain checker HTTP API (/health, /definition, /collect, /evaluate, /report).

How it works

For each check run, the Go binary invokes:

dnsviz probe -A . <ancestors…> <domain> | dnsviz grok -t <root-trust-anchor>

The queried name and every ancestor up to the root are passed to dnsviz probe, in root → leaf order. dnsviz only emits a full analysis (DNSKEY set, DS at parent, queries) for names listed on the command line; ancestors stumbled upon implicitly are kept as "stub" entries that grok ignores. Listing them explicitly is what makes every link of the chain appear in the report.

dnsviz grok -t <file> is given a BIND-format DNSKEY trust anchor for the root zone. Without it, the root has no parent to chain against and stays classified as NOERROR (DNS rcode) instead of SECURE (DNSSEC).

The output is then parsed: per-zone errors and warnings are walked out of the nested record tree (delegation.errors, dnskey[i].errors, queries/.../rrsig[j].errors, …) and turned into individual CheckState entries tagged with the JSON path where they were found. A curated catalog of common DNSSEC failure scenarios (broken chain, expired RRSIG, DS digest mismatch, deprecated algorithm, …) is matched against the findings to generate a "Fix these first" section in the HTML report with plain-language remediation hints.

The HTML report renders one block per zone in the chain (root → TLD → intermediates → leaf), each with its delegation/DS records, DNSKEY set, authoritative servers, and per-query analysis (RRsets, RRSIG validity, NSEC proofs) so a recursive DNSSEC failure can be located at the exact level, and the exact record, where it broke.

Scope

This checker is intentionally limited to what DNSViz reports. NSEC/NSEC3 zone-walk hardening and NSEC3PARAM iteration policy (RFC 9276) are delivered by a separate checker-dnssec module.

Usage

Standalone server

make
./checker-dnsviz -listen :8080

Runtime requirements:

  • dnsviz on PATH (the Python CLI: pip install dnsviz, plus pygraphviz and graphviz since dnsviz grok -t imports the graph module even when only producing JSON).
  • A BIND-format root DNSKEY trust anchor file. The collector auto-detects /usr/share/dnssec-root/trusted-key.key (Alpine's dnssec-root package, Debian/Ubuntu's dns-root-data ships an equivalent at /usr/share/dns/root.key); pass -trust-anchors-file <path> to override. Without it, the root zone in the report stays at NOERROR instead of SECURE.

Docker

make docker
docker run -p 8080:8080 happydomain/checker-dnsviz

The image bundles dnsviz, pygraphviz, graphviz and the alpine dnssec-root package, so the trust anchor is in place out of the box. When ICANN rolls a new root KSK (KSK-2024 is scheduled to begin signing in late 2026), rebuild the image once the upstream alpine package ships the new key.

happyDomain plugin

make plugin
# produces checker-dnsviz.so

Options

Scope Id Default Description
admin dnsvizBin dnsviz Path to the dnsviz CLI.
admin probeTimeoutSeconds 120 Hard timeout for dnsviz probe.
admin extraProbeArgs -A Extra arguments appended verbatim to dnsviz probe.
admin trustAnchorsFile /usr/share/dnssec-root/trusted-key.key (auto-detected) BIND DNSKEY file passed to dnsviz grok -t. When unset, the collector falls back to the Alpine dnssec-root package path if it exists.
domain domain_name auto-fill Domain to analyse.

Rules

Rule Description
dnsviz_overall_status DNSViz status of the queried domain (SECURE/INSECURE/BOGUS/INDETERMINATE).
dnsviz_per_zone_status One state per zone in the chain (root, TLD, intermediates, leaf).
dnsviz_zone_errors Every error reported by DNSViz, scoped to the zone where it was found.
dnsviz_zone_warnings Every warning reported by DNSViz, scoped to the zone where it was found.
dnsviz_common_failures Pattern-matches findings against a catalog of common DNSSEC failures.

Licensing

This repository is split into two licensing zones:

Path License Reason
checker/ MIT Pure analysis logic (types, rules, HTML report). Can be imported by third-party Go projects without GPL obligations.
internal/collect/ GPL v2 Invokes the dnsviz subprocess. Covered by DNSViz's GPL v2 licence.
main.go, plugin/ GPL v2 Wire the two together; distributed binaries include the GPL layer.

If you only need the analysis primitives (parse grok output, evaluate rules, render the HTML report), import git.happydns.org/checker-dnsviz/checker and supply your own checker.CollectFn, for example one that calls the HTTP API instead of running DNSViz locally.