DNSSEC09
Status: Final
Purpose
- Verify that SOA responses are signed and that SOA RRSIG records are time-valid, algorithm-supported, and cryptographically matched by DNSKEY records from the same nameserver.
Preconditions And Inputs
- Preconditions:
- A
zone.Zoneobject is available.
- A
- Required inputs:
- Child nameserver sets from
GlueNameserversandApexNameservers. - DNSKEY and SOA responses with DNSSEC enabled from child nameservers.
- Child nameserver sets from
- Profile/config knobs that affect behavior:
net.ipv4andnet.ipv6: disabled transports are skipped with transport debug tags.resolver.defaults.parallel: parallel nameserver execution fanout.
Algorithm And Decision Flow
- Emit
TEST_CASE_START. - Build nameserver set from the union of
GlueNameserversandApexNameservers(deduplicated byns.String()), then deduplicate by IP. - For each unique nameserver IP (parallelized):
- If transport is disabled, emit
IPV4_DISABLEDorIPV6_DISABLEDfor rrtypeDNSKEYand skip. - Query
DNSKEYat child apex with DNSSEC enabled. - Skip nameserver if response is absent, non-
NOERROR, non-AA, or has no apex DNSKEY records. - Query
SOAat child apex with DNSSEC enabled over UDP (UseVC=false). - Skip nameserver if response is absent, non-
NOERROR, non-AA, or has no apex SOA records. - If answer section has no
RRSIG, mark nameserver forDS09_MISSING_RRSIG_IN_RESPONSE. - Else, for each answer-section RRSIG:
- If inception is after test time, mark
DS09_SOA_RRSIG_NOT_YET_VALIDby keytag. - Else if expiration is before test time, mark
DS09_SOA_RRSIG_EXPIREDby keytag. - Else if algorithm is unsupported, mark
DS09_ALGO_NOT_SUPPORTED_BY_ZMby keytag+algo. - Else find matching DNSKEY records by keytag:
- If none found, mark
DS09_NO_MATCHING_DNSKEY. - Else verify signature over SOA RRset; if no candidate validates, mark
DS09_RRSIG_NOT_VALID_BY_DNSKEY.
- If none found, mark
- If inception is after test time, mark
- If transport is disabled, emit
- Emit accumulated DS09 findings grouped by keytag/algo as applicable.
- Collect nameserver IPs that had RRSIGs and emitted no DS09 failure; if non-empty emit
DS09_SOA_RRSIG_VALID. - Emit
TEST_CASE_END.
Per-NS SOA RRSIG Verification (steps 2-6)
Emitted Tags (Possible Set)
| Tag | Emitted when |
|---|---|
DS09_ALGO_NOT_SUPPORTED_BY_ZM | RRSIG verification requires an unsupported algorithm. |
DS09_SOA_RRSIG_VALID | At least one nameserver returned SOA with RRSIG and all RRSIG checks passed. |
DS09_MISSING_RRSIG_IN_RESPONSE | SOA response has no answer-section RRSIG records. |
DS09_NO_MATCHING_DNSKEY | RRSIG keytag has no matching DNSKEY keytag. |
DS09_RRSIG_NOT_VALID_BY_DNSKEY | Matching DNSKEY candidates exist but none verify the SOA signature. |
DS09_RSA_EXPONENT_UNSUPPORTED | SOA RRSIG could not be checked only because the matching DNSKEY is an RSA key whose public exponent exceeds what the local verifier supports (more than 64 bits). |
DS09_SOA_RRSIG_EXPIRED | SOA-related RRSIG expiration is before test time. |
DS09_SOA_RRSIG_NOT_YET_VALID | SOA-related RRSIG inception is after test time. |
IPV4_DISABLED | IPv4 transport is disabled for a queried nameserver (DNSKEY). |
IPV6_DISABLED | IPv6 transport is disabled for a queried nameserver (DNSKEY). |
TEST_CASE_END | Testcase completion marker is emitted. |
TEST_CASE_START | Testcase start marker is emitted. |
Tag Arguments
| Tag | Argument key | Type | Meaning |
|---|---|---|---|
DS09_ALGO_NOT_SUPPORTED_BY_ZM | keytag | int | RRSIG keytag associated with unsupported algorithm. |
DS09_ALGO_NOT_SUPPORTED_BY_ZM | algo_num | int | Unsupported DNSSEC algorithm number. |
DS09_ALGO_NOT_SUPPORTED_BY_ZM | algo_mnemo | string | Unsupported DNSSEC algorithm mnemonic. |
DS09_ALGO_NOT_SUPPORTED_BY_ZM | addresses | array<string> | Structured child nameserver IP list. |
DS09_MISSING_RRSIG_IN_RESPONSE | addresses | array<string> | Structured child nameserver IP list lacking SOA RRSIG. |
DS09_NO_MATCHING_DNSKEY | keytag | int | RRSIG keytag with no matching DNSKEY keytag. |
DS09_NO_MATCHING_DNSKEY | addresses | array<string> | Structured child nameserver IP list. |
DS09_RRSIG_NOT_VALID_BY_DNSKEY | keytag | int | RRSIG keytag that failed DNSKEY verification. |
DS09_RRSIG_NOT_VALID_BY_DNSKEY | addresses | array<string> | Structured child nameserver IP list. |
DS09_RSA_EXPONENT_UNSUPPORTED | keytag | int | RRSIG keytag whose matching DNSKEY RSA public exponent the local verifier cannot use. |
DS09_RSA_EXPONENT_UNSUPPORTED | addresses | array<string> | Structured child nameserver IP list. |
DS09_SOA_RRSIG_EXPIRED | keytag | int | RRSIG keytag with expired validity window. |
DS09_SOA_RRSIG_EXPIRED | addresses | array<string> | Structured child nameserver IP list. |
DS09_SOA_RRSIG_NOT_YET_VALID | keytag | int | RRSIG keytag with not-yet-valid validity window. |
DS09_SOA_RRSIG_NOT_YET_VALID | addresses | array<string> | Structured child nameserver IP list. |
DS09_SOA_RRSIG_VALID | addresses | array<string> | Structured child nameserver IP list where all SOA RRSIG checks passed. |
IPV4_DISABLED | ns | string | Nameserver identity (ns name only; use address for IP) skipped on IPv4. |
IPV4_DISABLED | address | string | Nameserver IP address for the same endpoint. |
IPV4_DISABLED | rrtype | string | rrtype skipped (DNSKEY). |
IPV6_DISABLED | ns | string | Nameserver identity (ns name only; use address for IP) skipped on IPv6. |
IPV6_DISABLED | address | string | Nameserver IP address for the same endpoint. |
IPV6_DISABLED | rrtype | string | rrtype skipped (DNSKEY). |
TEST_CASE_END | testcase | string | Testcase display name (DNSSEC09). |
TEST_CASE_START | testcase | string | Testcase display name (DNSSEC09). |
Severity Levels Per Tag
| Tag | Level | Notes |
|---|---|---|
DS09_ALGO_NOT_SUPPORTED_BY_ZM | NOTICE | Default from share/profile.json (test_levels.DNSSEC). |
DS09_MISSING_RRSIG_IN_RESPONSE | ERROR | Default from share/profile.json (test_levels.DNSSEC). |
DS09_NO_MATCHING_DNSKEY | ERROR | Default from share/profile.json (test_levels.DNSSEC). |
DS09_RRSIG_NOT_VALID_BY_DNSKEY | ERROR | Default from share/profile.json (test_levels.DNSSEC). |
DS09_RSA_EXPONENT_UNSUPPORTED | NOTICE | Default from share/profile.json (test_levels.DNSSEC); carries zero score penalty (scoring TagPenalties). |
DS09_SOA_RRSIG_EXPIRED | ERROR | Default from share/profile.json (test_levels.DNSSEC). |
DS09_SOA_RRSIG_NOT_YET_VALID | ERROR | Default from share/profile.json (test_levels.DNSSEC). |
DS09_SOA_RRSIG_VALID | INFO | 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). |
Differences From Upstream
- Differences (Upstream vs Gonemaster):
- Upstream: procedure text describes evaluating SOA RRSIG records. Gonemaster: iterates all answer-section RRSIG records and does not explicitly filter by
TypeCovered == SOAbefore DS09 classification. - Upstream: describes validity checks against test execution time. Gonemaster: uses the DNSKEY response packet timestamp (
packetTime(dnskeyResp)) as the reference time. - Upstream: does not explicitly specify testcase boundary and per-query transport debug emissions in this testcase summary. Gonemaster: emits
TEST_CASE_START,TEST_CASE_END,IPV4_DISABLED, andIPV6_DISABLED.
- Upstream: procedure text describes evaluating SOA RRSIG records. Gonemaster: iterates all answer-section RRSIG records and does not explicitly filter by
- Potential upstream report:
no
Edge Cases And Limitations
- Nameserver evaluation is deduplicated by IP; repeated names on one IP share one DS09 outcome.
- Nameservers failing response-shape checks (
Msg,NOERROR,AA, apex owner match) are skipped for DS09 findings. - If no usable DNSKEY records are found for a nameserver, SOA signing checks are skipped for that nameserver.
- Large RSA public exponent handling: an RSA DNSKEY the DNS library refuses although RFC 3110 permits it (an exponent of more than 4 bytes or greater than 2^31-1, or a leading zero byte in the exponent or modulus) is verified by gonemaster’s own RSA path instead, so the SOA RRSIG passes or fails like any other. Only when the exponent exceeds 64 bits is the finding reclassified from the
ERRORDS09_RRSIG_NOT_VALID_BY_DNSKEYto theNOTICEDS09_RSA_EXPONENT_UNSUPPORTED. Such a nameserver is treated as indeterminate and is not counted as aDS09_SOA_RRSIG_VALIDpass.