Add DNSSEC online signing for APP and ANAME/ALIAS records - #2109
Conversation
Signs the per-query dynamic answer from APP records (Split Horizon, GeoDistance/Country/Continent, Failover, WeightedRoundRobin, etc.) and resolved ANAME/ALIAS records, and secures NODATA responses at the record's own owner name, so both record types can now exist and work correctly in DNSSEC signed primary zones. Covers non-wildcard and wildcard owners, and both NSEC and NSEC3. Previously PrimaryZone.SetRecords/AddRecord/SignRRSet explicitly refused APP and ANAME records in a signed zone, and the dynamic answer itself was never signed even apart from that guard. Verified with delv against a real signed test zone under both NSEC and NSEC3: positive answers, wildcard positive answers, and NODATA at the record's own name all validate cleanly for both record types. Out of scope: proving nonexistence of an arbitrary name under a wildcard owned dynamic record (true NXDOMAIN) needs per query NSEC synthesis (RFC 4470/4471 "white lies"), which this does not attempt. As a result, a resolver doing RFC 8198 aggressive NSEC/NSEC3 caching may occasionally skip querying for a dynamic name it has unrelated cached denial records for. That is a known, accepted limitation until white lies are implemented separately.
A non-wildcard APP record answering for a subdomain beneath its own owner name (via the closest-ancestor fallback in InternalQuery) that declines to answer returns NXDOMAIN, but was attaching the NODATA proof for the record's own owner name instead of a real NXDOMAIN proof of cover for the queried name. A validator has no way to accept that as valid, so the response was bogus - confirmed with delv (only one NSEC3 record was attached, and it didn't even cover the queried name). Adds AuthZoneManager.GetNSecProofOfNonExistenceNxDomain, generalized from the existing GetNSecProofOfWildcardAnswer (now a thin wrapper around it), and uses it from ProcessAPPAsync's NxDomain branch. Reuses the same static NSEC/NSEC3 chain machinery as the rest of the DNSSEC online signing work, no new denial-of-existence logic needed. Verified with delv against dnssectest.local on 10.0.7.1 under both NSEC and NSEC3: the fixed NXDOMAIN case now fully validates, and the existing positive/NODATA test matrix from this branch still validates cleanly (no regression).
|
Closing the loop on tier 3 (white lies, RFC 4470/4471) as a follow-up to I implemented it against a wildcard-owned APP record that explicitly Both RFC 4470 and RFC 4471 require testing whether a synthesized covering The non-wildcard case (a plain APP record explicitly returning NXDOMAIN, One genuine, unrelated bug did fall out of building the test setup for Marking tier 3 as evaluated and not viable as scoped, rather than leaving |
|
Thanks for the PR. This will require studying things in details and it may differ significantly from what I have in my mind. Support for online signing is not immediately planned as there are several other things that are planned to be implemented before it. Thus, I am not sure if/when I can take look at this topic and come back to this PR. |
Summary
Adds DNSSEC "online" (dynamic, per-query) signing for
APPrecordsand
ANAME/ALIASrecords, so both can now exist and workcorrectly in a DNSSEC signed primary zone. Today
PrimaryZone.SetRecords/AddRecord/SignRRSetexplicitly refuseboth types when the zone is signed, and even setting that aside, the
dynamically resolved answer was never signed.
This follows up on #1928, and discussions #825, #1357, and #1346,
where online signing has been described as a planned feature for
exactly this purpose. #1346 tied it to clustering support for key
distribution across nodes; clustering has since shipped (November
2025) and is stable, so that dependency shouldn't block the scope
covered here.
Scope
Two tiers, both reusing the zone's existing static NSEC/NSEC3 chain
and the existing
SignRRSetpath (the same one the offline re-signcycle already uses) rather than needing any new denial of existence
machinery:
signed at query time with the zone's active ZSK, including the
correct RFC 4035 Labels field and the RFC 4035 5.3.4 wildcard
proof of cover for wildcard owned records.
ANAME resolution finds nothing, the response now includes a signed
SOA and the zone's existing NSEC/NSEC3 proof of nonexistence at
the record's own owner name.
Not in scope: proving nonexistence of an arbitrary name under a
wildcard owned dynamic record (a true NXDOMAIN, for a name the app
or ANAME never had an answer for) needs per query NSEC synthesis,
RFC 4470/4471 "white lies". That needs genuinely new denial of
existence machinery rather than reuse, so I scoped it out as a
follow up PR rather than bundling it here, especially since #1346
suggests you may already have a specific design in mind for it.
One real consequence of leaving white lies out: a resolver doing RFC
8198 aggressive NSEC/NSEC3 caching (the Unbound/BIND default) may
occasionally skip querying for a dynamic name if it already has an
unrelated cached NSEC/NSEC3 covering it. I looked at partially
working around this by declaring A/AAAA in the type bitmap at
APP/ANAME owned names, but that directly contradicts a NODATA
response at that same name (the bitmap can't simultaneously claim a
type exists and deny it). Confirmed with delv that a validator
correctly rejects that as bogus, so I left it out rather than ship
something half right.
Verification
Deployed to a scratch signed zone and validated with delv (BIND's
validating resolver tool) against a manually configured trust
anchor, real cryptographic chain of trust validation rather than
just checking an RRSIG is present. Covered non-wildcard and wildcard
owners, both record types, and both NSEC and NSEC3:
wildcard x NSEC/NSEC3
combinations
Open to adjusting scope
If you'd rather this be shaped differently, tied to specific
clustering key distribution work, or folded into a broader design
you already have in mind, happy to rework it.