DNSSEC24
Status: Final
Purpose
- Verify the authenticated DNSSEC bootstrapping signals of RFC 9615 for a signed zone that has no DS at its parent.
- A child DNS operator copublishes the apex CDS and CDNSKEY RRsets of the zone at a signaling name under each nameserver hostname, in a signaling zone with a valid chain of trust from the root (RFC 9615 sections 3.1, 3.2, 4.1). A parental agent that validates these copies publishes the DS without the acceptance delay of RFC 8078 section 3. The testcase executes the four validation steps of RFC 9615 section 4.2 and reports every condition on which a parental agent aborts.
- DNSSEC15 to DNSSEC18 evaluate the apex records. This testcase evaluates the signaling records and their agreement with the apex.
Preconditions And Inputs
- Preconditions:
- A
zone.Zoneobject is available.
- A
- Required inputs:
- The DS RRset of the zone from each nameserver of
ParentNameservers, or the fake DS data of an undelegated run. In a full run the responses are served from the per-nameserver query cache (DNSSEC07). - The apex CDS and CDNSKEY RRsets from each nameserver of
GlueNameservers, deduplicated by IP. In a full run the responses are served from the per-nameserver query cache (DNSSEC15). - The parent-side delegation
NSnames fromGlueNames. - The root server addresses from the root hints, and the root trust anchors: the IANA root DS records for key tags 20326 and 38696 (algorithm 8, digest type 2), embedded in the engine.
- Per signaling name:
CDS,CDNSKEY,DSandDNSKEYresponses from the servers of each zone cut between the root and the signaling zone, all with DNSSEC enabled.
- The DS RRset of the zone from each nameserver of
- Profile/config knobs that affect behavior:
net.ipv4andnet.ipv6: disabled transports are skipped. Parent and apex nameservers are reported with transport debug tags; servers on a signaling path are left out without a tag.dnssec.signature_validity_skew: clock-skew tolerance used byverifyRRSIG.
Algorithm And Decision Flow
- Emit
TEST_CASE_START. - Parent DS view:
- If a nameserver of the parent zone holds fake DS data for the zone name,
emit
DS24_DELEGATION_SECUREwith those nameservers andTEST_CASE_END, then stop. - Otherwise, for each parent nameserver P: if the transport is disabled,
emit
IPV4_DISABLEDorIPV6_DISABLEDwithquery_typeDSand skip P. Query<zone> DSat P with DNSSEC enabled (UDP, retry over TCP onTC). P holds a DS when the response isAANOERRORand its answer section carries a DS record owned by the zone name. - At least one P holds a DS: emit
DS24_DELEGATION_SECUREwith those nameservers andTEST_CASE_END, then stop. A zone whose parent servers disagree counts as securely delegated; DNSSEC07 reports the disagreement.
- If a nameserver of the parent zone holds fake DS data for the zone name,
emit
- Apex view: for each delegation nameserver X, deduplicated by IP:
- If the transport is disabled, emit
IPV4_DISABLEDorIPV6_DISABLEDwithquery_typeCDSandCDNSKEY, and skip X. - Query
<zone> CDSand<zone> CDNSKEYat X with DNSSEC enabled (UDP, retry over TCP onTC). X answers a type when the response isAANOERROR; its RRset of that type is the set of answer-section records of that type owned by the zone name, possibly empty. - No X returned a non-empty CDS or CDNSKEY RRset: emit
DS24_NO_CDS_CDNSKEYandTEST_CASE_END, then stop. - Every record of every returned RRset is an RFC 8078 section 4 delete
record, recognised by algorithm 0 (CDS
0 0 0 00, CDNSKEY0 3 0 AA==): emitDS24_DELETE_REQUESTEDandTEST_CASE_END, then stop. A delete request with no DS to delete is not a bootstrapping request. - Some X answered neither type, or only one of them: emit
DS24_APEX_UNAVAILABLEwith those nameservers. The testcase continues.
- If the transport is disabled, emit
- Signaling domains: take the delegation
NSnames, lower-cased and deduplicated, and drop every name H for whichz.Name.IsInBailiwick(H)holds (RFC 9615 section 4.1).- No name remains: emit
DS24_ONLY_IN_DOMAIN_NSwith every delegationNSname andTEST_CASE_END, then stop (RFC 9615 section 4.4).
- No name remains: emit
- For each remaining name H, in lexicographic order:
- The signaling name N is
_dsboot, the labels of the zone name,_signal, then the labels of H. If the wire form of N exceeds 255 octets (RFC 1035 section 3.1), emitDS24_SIGNAL_NAME_TOO_LONGand continue with the next name. No query is made. - Locate N by the descent below. On
at cut, emitDS24_SIGNAL_AT_ZONE_CUT. Onunreachableat cut C, emitDS24_SIGNAL_ZONE_UNREACHABLEwithzoneC and the servers tried. Onabsent, record H as absent. In these cases noDNSKEYis queried. - On
signal, validate the chain of trust of the signaling zone Z by the verification below. The first fault from the root down ends the evaluation of H:- a cut without DS:
DS24_SIGNAL_ZONE_INSECUREwithzonethat cut; - a DS, DNSKEY or RRSIG that fails to validate:
DS24_SIGNAL_CHAIN_BROKENwithzonethat cut; - no server of a cut answers its
DNSKEYquestion:DS24_SIGNAL_ZONE_UNREACHABLEwithzonethat cut.
- a cut without DS:
- For each non-empty signaling RRset, of type T: at least one RRSIG in the
answer section covering T at N, with Z as Signer’s Name, MUST verify
against the DNSKEY RRset of Z, with
packetTimeof the response as the reference time. Otherwise emitDS24_SIGNAL_UNSIGNEDwithquery_typeT. - For each type T in
CDS,CDNSKEYwithoutDS24_SIGNAL_UNSIGNED: compare the signaling RRset of T with the apex RRset of T of every delegation nameserver that answered T (RFC 9615 section 4.2 step 4). Comparison is by content: owner name and TTL are ignored, an empty RRset equals only an empty RRset. A difference from any of them: emitDS24_SIGNAL_MISMATCHwithquery_typeT. - No tag of steps 5.3 to 5.5 for H, and no indeterminate result: emit
DS24_SIGNAL_VALIDATEDwithzoneZ.
- The signaling name N is
- Aggregate the absent names:
- At least one name reached
signal: emitDS24_SIGNAL_MISSINGfor each absent name. - No name reached
signal: emit oneDS24_NO_SIGNALwith the absent names.
- At least one name reached
- Every name of step 5 emitted
DS24_SIGNAL_VALIDATEDandDS24_APEX_UNAVAILABLEwas not emitted: emitDS24_BOOTSTRAP_READY. - Emit
TEST_CASE_END.
Descent
A level is a zone cut C on the path to N, with the servers that serve C, the DS RRset of C with its RRSIG records as the parent of C served them, and the level of that parent. The root level has the root servers and no DS. Levels are memoized by cut name for the whole testcase and shared between signaling names.
Servers of a level: the NS names of the referral that created it, in
lexicographic order. The addresses of a name are the A and AAAA records
owned by it in the additional section of that referral, when the name lies at
or below the referring cut; otherwise they are resolved through the
recursor. Servers on a disabled transport are left out. The first address of
each name precedes the second address of any name. At most two servers are
asked per question.
Descent for N:
- Start at the deepest memoized level whose cut is a proper ancestor of N; the root level when none is.
- Query
N CDSat the servers of the level in turn, with DNSSEC enabled (UDP, retry over TCP onTC). Classify the response:- Referral (not
AA,NOERROR, empty answer section, anNSRRset in the authority section) with owner C:- C equals N:
at cut. RFC 9615 section 4.1 forbids a zone cut at a signaling name. - C strictly below the current cut and a proper ancestor of N: the level of C is created from the referral, unless memoized, and the descent continues there at step 2. The DS RRset of C and the RRSIG records covering it are taken from the authority section.
- Otherwise, the same cut, a cut above it or a name off the path: no answer.
- C equals N:
AANXDOMAIN:absent.AANOERROR: queryN CDNSKEYat the same server. AnAANXDOMAINgives an empty CDNSKEY RRset, anAANOERRORits answer-section CDNSKEY records owned by N; any other response is no answer from this server. Both RRsets empty:absent. Otherwisesignal.- Any other response, or none: no answer.
- Referral (not
- No server of the level gave an answer: the level is unreachable, and
Nisunreachableat its cut. The state is memoized: a later name below the same level is unreachable without a query. - The descent is bounded by the label count of N.
The signaling zone Z of a signal result is the Signer’s Name of the first
answer-section RRSIG covering a non-empty signaling RRset, CDS first, when that
name is at or below the cut C of the answering level and an ancestor of N.
Otherwise Z is C.
- Z equals N:
at cut. - Z strictly below C: one server serves both C and Z, and no referral marked
the cut. Query
Z DSat the servers of C, with DNSSEC enabled:- A referral to a cut M strictly below C, at or above Z: the level of M is created from the referral; if M is Z the level of Z is found, otherwise the query repeats from M.
AANOERRORwith a DS RRset for Z: the level of Z is created with that DS RRset and its RRSIG records. When their Signer’s Name P lies strictly between C and Z, the level of P is located the same way first and becomes the parent of Z. The servers of Z are the servers of C, the answering server first.AANOERRORwithout a DS RRset: the level of Z is created without DS.AANXDOMAIN: the level of Z is created as broken.- No answer from both servers:
unreachableat C.
Verification
Verification of a level, memoized per level, after its parent level verified secure:
- Root level: query
. DNSKEYat the root servers. A DNSKEY MUST match a root trust anchor by key tag, algorithm and digest, and that key MUST verify an RRSIG covering the DNSKEY RRset. Failure: broken at.. - Any other level C:
- No DS RRset: insecure at C. The denial of existence of the DS is not verified.
- An RRSIG covering the DS RRset, with the parent cut as Signer’s Name, MUST verify against the DNSKEY RRset of the parent. Failure: broken at C.
- Query
C DNSKEYat the servers of C. NoAANOERRORresponse: unreachable at C. A DNSKEY MUST match a DS record of C by key tag, algorithm and digest, and that key MUST verify an RRSIG covering the DNSKEY RRset. Failure: broken at C.
- A level created as broken: broken at its cut.
- A signature whose algorithm the local verifier cannot process is indeterminate: the signaling name gets no tag of steps 5.3 to 5.6.
Signature checks use packetTime of the response carrying the signature.
Emitted Tags (Possible Set)
| Tag | Emitted when |
|---|---|
DS24_APEX_UNAVAILABLE | A delegation nameserver gave no AA NOERROR response to the apex CDS or CDNSKEY question. |
DS24_BOOTSTRAP_READY | Every signaling name validated and every delegation nameserver answered both apex questions. |
DS24_DELEGATION_SECURE | A parent nameserver returned a DS record for the zone, or fake DS data exists. |
DS24_DELETE_REQUESTED | Every apex CDS and CDNSKEY record is an RFC 8078 delete record. |
DS24_NO_CDS_CDNSKEY | No delegation nameserver returned a non-empty apex CDS or CDNSKEY RRset. |
DS24_NO_SIGNAL | No signaling name carries CDS or CDNSKEY records, and at least one is absent. |
DS24_ONLY_IN_DOMAIN_NS | Every delegation nameserver name is at or below the zone name. |
DS24_SIGNAL_AT_ZONE_CUT | A zone cut exists at the signaling name. |
DS24_SIGNAL_CHAIN_BROKEN | A DS, DNSKEY or RRSIG on the path to the signaling zone fails to validate. |
DS24_SIGNAL_MISMATCH | The signaling RRset of a type differs from the apex RRset of that type on at least one delegation nameserver. |
DS24_SIGNAL_MISSING | The signaling name of a nameserver is absent while another signaling name carries records. |
DS24_SIGNAL_NAME_TOO_LONG | The signaling name exceeds 255 octets in wire form. |
DS24_SIGNAL_UNSIGNED | No RRSIG by the signaling zone verifies a non-empty signaling RRset. |
DS24_SIGNAL_VALIDATED | The signaling name validates from the root and its RRsets equal the apex RRsets. |
DS24_SIGNAL_ZONE_INSECURE | A zone cut on the path to the signaling zone has no DS. |
DS24_SIGNAL_ZONE_UNREACHABLE | No server of a zone cut on the path gave a usable answer. |
IPV4_DISABLED | IPv4 transport is disabled for a parent (DS) or delegation (CDS, CDNSKEY) nameserver. |
IPV6_DISABLED | IPv6 transport is disabled for a parent (DS) or delegation (CDS, CDNSKEY) nameserver. |
TEST_CASE_END | Testcase completion marker is emitted. |
TEST_CASE_START | Testcase start marker is emitted. |
Tag Arguments
| Tag | Argument key | Type | Meaning |
|---|---|---|---|
DS24_APEX_UNAVAILABLE | servers | array<object> | Delegation nameservers ({ns,address}) without an AA NOERROR response for at least one of the two types. |
DS24_DELEGATION_SECURE | servers | array<object> | Parent nameservers ({ns,address}) that returned or hold a DS record. |
DS24_NO_SIGNAL | servers | array<object> | Nameserver names ({ns}) whose signaling name is absent. |
DS24_ONLY_IN_DOMAIN_NS | servers | array<object> | The delegation nameserver names ({ns}). |
DS24_SIGNAL_AT_ZONE_CUT | ns | string | Nameserver name under whose signaling domain the name lies. |
DS24_SIGNAL_AT_ZONE_CUT | query_name | string | Signaling name. |
DS24_SIGNAL_CHAIN_BROKEN | ns | string | Nameserver name under whose signaling domain the name lies. |
DS24_SIGNAL_CHAIN_BROKEN | zone | string | Zone cut where validation fails. |
DS24_SIGNAL_MISMATCH | ns | string | Nameserver name under whose signaling domain the name lies. |
DS24_SIGNAL_MISMATCH | query_name | string | Signaling name. |
DS24_SIGNAL_MISMATCH | query_type | string | CDS or CDNSKEY. |
DS24_SIGNAL_MISSING | ns | string | Nameserver name under whose signaling domain the name lies. |
DS24_SIGNAL_MISSING | query_name | string | Signaling name. |
DS24_SIGNAL_NAME_TOO_LONG | ns | string | Nameserver name under whose signaling domain the name lies. |
DS24_SIGNAL_NAME_TOO_LONG | query_name | string | Signaling name. |
DS24_SIGNAL_UNSIGNED | ns | string | Nameserver name under whose signaling domain the name lies. |
DS24_SIGNAL_UNSIGNED | query_name | string | Signaling name. |
DS24_SIGNAL_UNSIGNED | query_type | string | CDS or CDNSKEY. |
DS24_SIGNAL_UNSIGNED | zone | string | Signaling zone. |
DS24_SIGNAL_VALIDATED | ns | string | Nameserver name under whose signaling domain the name lies. |
DS24_SIGNAL_VALIDATED | query_name | string | Signaling name. |
DS24_SIGNAL_VALIDATED | zone | string | Signaling zone. |
DS24_SIGNAL_ZONE_INSECURE | ns | string | Nameserver name under whose signaling domain the name lies. |
DS24_SIGNAL_ZONE_INSECURE | zone | string | Zone cut without DS. |
DS24_SIGNAL_ZONE_UNREACHABLE | ns | string | Nameserver name under whose signaling domain the name lies. |
DS24_SIGNAL_ZONE_UNREACHABLE | zone | string | Zone cut whose servers gave no usable answer. |
DS24_SIGNAL_ZONE_UNREACHABLE | servers | array<object> | Servers ({ns,address}) asked at that cut. |
IPV4_DISABLED | ns | string | Nameserver identity skipped on IPv4. |
IPV4_DISABLED | address | string | Nameserver IP address for the same endpoint. |
IPV4_DISABLED | query_type | string | rrtype skipped (DS, CDS or CDNSKEY). |
IPV6_DISABLED | ns | string | Nameserver identity skipped on IPv6. |
IPV6_DISABLED | address | string | Nameserver IP address for the same endpoint. |
IPV6_DISABLED | query_type | string | rrtype skipped (DS, CDS or CDNSKEY). |
TEST_CASE_END | testcase | string | Testcase display name (DNSSEC24). |
TEST_CASE_START | testcase | string | Testcase display name (DNSSEC24). |
DS24_BOOTSTRAP_READY, DS24_DELETE_REQUESTED and DS24_NO_CDS_CDNSKEY
carry no arguments.
Severity Levels Per Tag
A parental agent aborts the procedure of RFC 9615 section 4.2 on every
WARNING-level condition. A zone that publishes no signal waits for the RFC 8078
acceptance delay, which DS07_NO_DS_FOR_SIGNED_ZONE already reports.
| Tag | Level | Notes |
|---|---|---|
DS24_APEX_UNAVAILABLE | WARNING | Default from share/profile.json (test_levels.DNSSEC). |
DS24_BOOTSTRAP_READY | INFO | Default from share/profile.json (test_levels.DNSSEC). |
DS24_DELEGATION_SECURE | INFO | Default from share/profile.json (test_levels.DNSSEC). |
DS24_DELETE_REQUESTED | INFO | Default from share/profile.json (test_levels.DNSSEC). |
DS24_NO_CDS_CDNSKEY | INFO | Default from share/profile.json (test_levels.DNSSEC). |
DS24_NO_SIGNAL | NOTICE | Default from share/profile.json (test_levels.DNSSEC). |
DS24_ONLY_IN_DOMAIN_NS | NOTICE | Default from share/profile.json (test_levels.DNSSEC). |
DS24_SIGNAL_AT_ZONE_CUT | WARNING | Default from share/profile.json (test_levels.DNSSEC). |
DS24_SIGNAL_CHAIN_BROKEN | WARNING | Default from share/profile.json (test_levels.DNSSEC). |
DS24_SIGNAL_MISMATCH | WARNING | Default from share/profile.json (test_levels.DNSSEC). |
DS24_SIGNAL_MISSING | WARNING | Default from share/profile.json (test_levels.DNSSEC). |
DS24_SIGNAL_NAME_TOO_LONG | NOTICE | Default from share/profile.json (test_levels.DNSSEC). |
DS24_SIGNAL_UNSIGNED | WARNING | Default from share/profile.json (test_levels.DNSSEC). |
DS24_SIGNAL_VALIDATED | INFO | Default from share/profile.json (test_levels.DNSSEC). |
DS24_SIGNAL_ZONE_INSECURE | WARNING | Default from share/profile.json (test_levels.DNSSEC). |
DS24_SIGNAL_ZONE_UNREACHABLE | WARNING | Default from share/profile.json (test_levels.DNSSEC). |
IPV4_DISABLED | DEBUG2 | Default from share/profile.json (test_levels.DNSSEC). |
IPV6_DISABLED | DEBUG2 | Default from share/profile.json (test_levels.DNSSEC). |
TEST_CASE_END | DEBUG | Default from share/profile.json (test_levels.DNSSEC). |
TEST_CASE_START | DEBUG | Default from share/profile.json (test_levels.DNSSEC). |
Scoring takes the severity default in the dnssec dimension. No
TagPenalties override is defined.
Differences From Upstream
- Upstream reference: no upstream equivalent. No upstream testcase reads an RFC 9615 signaling name.
- Potential upstream report:
yes- Upstream expected behavior: the bootstrapping signals of a signed zone without DS are validated as a parental agent validates them.
- Gonemaster observed behavior: DNSSEC24 validates them.
- evidence:
engine/test/dnssec/dnssec24_test.go. - report status:
not filed.
Edge Cases And Limitations
- The root zone has no parent DS and publishes no CDS; it stops at
DS24_NO_CDS_CDNSKEY. The DS view of a top-level domain comes from the root servers. - A nameserver name equal to the zone name is in-domain.
- A wildcard in the signaling zone synthesizes signaling RRsets that validate as any other; the testcase does not distinguish them.
- Apex delete records alone stop at the gate. Mixed with other records they are content as any other, and DNSSEC16 and DNSSEC17 report the mix.
- A signaling name that does not exist MAY be answered
NODATAwith compact denial instead ofNXDOMAIN; both areabsent. AfterNODATAonCDStheCDNSKEYquestion is still asked. - A
CNAMEat the signaling name is not followed; the name counts by the records owned by N. - The denial of existence that marks a cut insecure is not verified. A cut without DS is insecure whether or not the denial would validate.
- A server whose address the non-global target guard refuses gives no answer, which can leave a cut unreachable.
- The signaling zones are probed from one vantage point. A lame anycast instance is reported as seen.
- An inconsistent apex RRset of one type yields
DS24_SIGNAL_MISMATCHfor that type at every signaling name, beside the DNSSEC15 inconsistency tags. Content is compared over every digest type; the RFC 9975 digest filter of DNSSEC15 does not apply. - A top-level domain passes the gate like any zone. Its parental agent is the root zone operator.
Implementation Notes
Implementation-defined choices, none of them mandated by the protocol:
- Validation is an own walk from the root hints with the embedded IANA root trust anchors, not the AD bit of a resolver. The verdict does not depend on the host, and the finding names the zone cut that fails.
- The
DNSKEYRRsets of the path are queried only for a signaling name that carries records. An absent signaling name costs one query per zone cut above it, and a name sharing the cuts of an earlier one costs one query. - Signaling names are walked in sequence, sharing the level memo. A level found unreachable stays unreachable for the rest of the testcase.
- At most two servers are asked per question; the first address of each name precedes the second address of any name.
- The apex queries share the option set of DNSSEC15, and the parent
DSqueries that of DNSSEC07, so a full run serves them from the cache. DS24_BOOTSTRAP_READYis OK-tag gated on every signaling name validating.
Evidence In Gonemaster
- Code paths:
engine/test/dnssec/dnssec.go(DNSSEC24 testcase function and signaling walker).engine/dsboot/dsboot.go(signaling name, signaling hosts, delete records, content comparison).engine/dnssecutil/anchors.go(root trust anchors).
- Related tests:
engine/test/dnssec/dnssec24_test.go.engine/dsboot/dsboot_test.go.
- References:
- RFC 9615 sections 3.1, 3.2, 4.1, 4.2, 4.4 (authenticated bootstrapping).
- RFC 8078 sections 3 and 4 (acceptance policy, delete records).
- RFC 7344 (CDS and CDNSKEY).
- RFC 4035 section 5 (authenticating responses, chain of trust).
- RFC 1035 section 3.1 (name length).
- IANA root trust anchors,
https://data.iana.org/root-anchors/root-anchors.xml.