Understanding DNSSEC validation using Pi-hole's Query Log

Originally published at: https://pi-hole.net/2021/12/12/understanding-dnssec-validation-using-pi-holes-query-log/

The Domain Name System Security Extensions (DNSSEC) is an Internet standard that adds security mechanisms to the Domain Name System (DNS). It ensures both the authenticity and integrity of the DNS data. From FTL v5.9 on, Pi-hole shows and analyzes the internally generated DNSSEC queries needed to build the chain-to-trust. This feature is enabled by default and can be useful to provide additional details about DNSSEC transactions.

A (local) DNS resolver can use DNSSEC to verify that the DNS zone data it receives has not been modified and is indeed identical to the authoritative zone. DNSSEC was developed mainly as means against cache poisoning. It secures the transmission of resource records by means of digital signatures using asymmetric so-called public-key cryptography. If you are not familiar with the concept, think of it as a cleverly designed lock, where one key locks and one key unlocks. In DNSSEC, you get the unlocking public key, while the locking key is kept private. The owners of the authoritative server on which the zone to be secured is located signs each individual record using their private keys. DNS clients can validate this signature with the owner’s public key to verify said authenticity and integrity.

Technical details

A separate zone signing key (a pair consisting of a public and private key) is generated for each zone to be secured. The public part of the zone key is included in the zone file as a DNSKEY resource record. The private key is used to digitally sign each individual record of this zone. For this purpose, a new record type is provided, the RRSIG Resource Record, which contains the signature to the associated DNS record. For each transaction, the associated RRSIG record is supplied in addition to the actual resource record. The requesting resolver can then validate the signature using the public zone key.

DNSKEY records are used to propagate public keys through DNS: The owner of the key stores it as DNSKEY record on a publicly accessible DNS server. Anyone who needs this public key sends a corresponding DNSKEY request. You receive the public key in response. The procedure thus corresponds to any other DNS requests, for instance ordinary IP addresses. In practice, however, this type of propagation is not sufficient, since a complete zone could be forged. The public key must therefore either be introduced manually into the resolver as a trusted key or the associated DS resource record must be published in the overlying zone. The trusted key of the root zone (the uppermost level of the DNS hierarchy) is known by your Pi-hole and is hard-coded.

For a complete picture, we also need DS (Delegation Signer) records. Those are used to chain DNSSEC-signed zones. This allows multiple DNS zones to be combined into a chain of trust and validated via a single public key. The basic idea is to chain all the zones involved and use only the topmost one as the secure entry point. The security-providing and critical part is that DS record can be calculated from the DNSKEY, but not vice versa thanks to the used asynchronous cryptography.

How does verification work?

Example of a signed A record (requested on Nov 30, 2021):

$ dig +dnssec A www.pi-hole.net
[...]
;; ANSWER SECTION:
www.pi-hole.net. 3600 IN CNAME pi-hole.net.
www.pi-hole.net. 3600 IN RRSIG CNAME          # Signed record type is CNAME
                               13             # Hash type (in this case ECDSAP256-SHA256)
                               3              # Signed name consists of three labels ("www", "pi-hole" and "net")
                               3600           # Original TTL
                               20211229232302 # Not valid after 2021-12-29 23:23:02
                               20211129232302 # Not valid before 2021-11-29 23:23:02
                               36126          # Unique number/key tag
                               pi-hole.net.   # Name of the signer
                               +x/jPBz4Yyhnkb/b1dze3Sz4WhEhyk4/bs38Zjpo67Ep1fLmj8KLR1CE qpNMBpG/Y+29SYma4sDTF+pvMk4fHQ== # The hash
pi-hole.net.     300  IN A     3.18.136.52
pi-hole.net.     300  IN RRSIG A              # Signed record type is A
                               13             # Hash type (in this case ECDSAP256-SHA256)
                               2              # Signed name consists of two labels ("pi-hole" and "net")
                               300            # Original TTL
                               20211229232302 # Not valid after 2021-12-29 23:23:02
                               20211129232302 # Not valid before 2021-11-29 23:23:02
                               36126          # Unique number/key tag
                               pi-hole.net.   # Name of the signer
                               LGyR9ZHaH6UUO3KK7y6lwxzIbe3KbwQ9mzkaH+LZQLA+XJBfM2Vc5Bbd SWoYMOq063qUd0GYRDDCzrWjEvQVtQ== # The hash

 

When the validating resolver queries for pi-hole.net, it receives both the record itself (CNAME pi-hole.net and A 3.18.136.52) and the digital signatures (RRSIG CNAME 13 2 3600 ... and RRSIG A 13 2 300 ...). The digital signature records contain the ID of the used hash function (in our example, 13 = SHA256). Your Pi-hole can take the plain-text message (A 3.18.136.52) and run it through SHA-256 to produce a hashed value itself. The validating resolver then obtains the public key (DNSKEY records), decrypts the digital signature, and gets back the original hashed value produced by the authoritative server. If both hash values are identical, the answer is valid, meaning this answer came from the authoritative server (authenticity), and the content remained intact during transit (integrity).

This verification is repeated for every component up to the root zone of the DNS system to build the chain of trust. Furthermore, the validating resolver’s current system time needs to fall between the two not-valid-after and not-valid-before timestamps. If it does not, the validation fails, because it could be an attacker replaying an old captured answer set from the past, or feeding us a crafted one with incorrect future timestamps. This is the most often seen issue with DNSSEC validation on devices lacking a reliable clock (such as the widely used Raspberry Pi’s without a real-time clock (RTC) extension).

If the answer passes both the hash value check and the timestamp check, it is validated and the response is sent to the client; if it does not verify, a SERVFAIL is returned to the client instead of any records.

Examples

We will shortly mention what is going on using three simple examples for typical DNSSEC results you may see in your Pi-hole’s Query Log:

sigok.verteiltesysteme.net (DNSSEC test domain) – SECURE

  1. Client localhost requests A sigok.verteiltesysteme.net – the server responds with the A record and three RRSIG (one for the A, the AAAA and the NSEC records of this domain)
  2. DS net is requested – the server responds with the DS record and the associated RRSIG
  3. The root zone key (DNSKEY .) is requested, the reply type is DNSSEC – the server responds with the DNSKEY of the root zone and its RRSIG
  4. DS verteiltesysteme.net is requested – the server responds with the DS record and the associated RRSIG
  5. DNSKEY net is requested, the reply type is DNSSEC – the server responds with the DNSKEY of net and its RRSIG
  6. DNSKEY verteiltesysteme.net is requested – the server responds with the DNSKEY of verteiltesysteme.net and its RRSIG

Pi-hole verifies the A record for sigok.verteiltesysteme.net the following way:

  1. The A record A sigok.verteiltesysteme.net (reply #1) is validated through its RRSIG (in same reply) using the DNSKEY of verteiltesysteme.net (reply #6)
  2. The DNSKEY of verteiltesysteme.net (reply #6) is validated through its RRSIG using the DS of verteiltesysteme.net (reply #4)
  3. The DNSKEY of net (reply #5) is validated through its RRSIG using the DS of net (reply #2)
  4. The DNSKEY of the root zone (reply #3) is validated through its RRSIG using the trust-anchor hard-wired into Pi-hole

The result is SECURE as Pi-hole successfully build the chain of trust up from the queried domain to the (known) root zone.

![DNSSEC validation for sigok.verteilesysteme.net|980x393](upload://lYQvmuX5LX6sQMFGReeXVhwFwKv.png)

 

textsecure-service.whispersystems.org (Signal messenger) – INSECURE

  1. Client localhost requests textsecure-service.whispersystems.org – the server responds with the A record and its RRSIG
  2. DS org is requested – the server responds with the DS record and the associated RRSIG
  3. The root zone key (DNSKEY .) is requested – the server responds with the DNSKEY of the root zone and its RRSIG
  4. DS whispersystems.org is requested (at the org DNS server), the reply is NODATA, the autoritative server also appends some other information
  5. DNSKEY org is requested – the server responds with the DNSKEY record and the associated RRSIG

Note the absence of a sixth query for DNSKEY whispersystems.org. As DS whispersystems.org returned NODATA, we already know that the entire zone whispersystems.org is not DNSSEC-signed.

Pi-hole verifies the A record for textsecure-service.whispersystems.org the following way:

  1. The A record A textsecure-service.whispersystems.org (reply #1) does not contain RRSIG – it is not signed. To rule out possible manipulation that might be going on (a malicious server may just have stripped the RRSIG), Pi-hole continues building the chain of trust
  2. DS of whispersystems.org (reply #4) contains additional information (NSEC3 and corresponding RRSIG) to prove the non-existence of a DSrecord for this zone
  3. The DNSKEY of org (reply #5) is validated through its RRSIG using the DS of org (reply #2)
  4. The DNSKEY of the root zone (reply #3) is validated through its RRSIG using the root zone’s trust-anchor known by Pi-hole

The result is INSECURE as Pi-hole successfully verified the non-existence of DS whispersystems.org . This verified the absence of DNSSEC signing for the entirety of whispersystems.org .
![DNSSEC validation for textsecure-service.whispersystems.org|980x337](upload://2CMjhIYCFVsGz9nYUy5Nzlyn5Tb.png)

If you want to learn more about the tricky business of authenticated denial of existence in DNS, check out RFC 7129.

sigfail.verteiltesysteme.net (DNSSEC test domain) – BOGUS

  1. Client localhost requests A sigfail.verteiltesysteme.net – the server responds with the A record and three RRSIG (one for the A, the AAAA and the NSEC records of this domain)
  2. DS net is requested – the server responds with the DS record and the associated RRSIG
  3. The root zone key (DNSKEY .) is requested, the reply type is DNSSEC – the server responds with the DNSKEY of the root zone and its RRSIG
  4. DS verteiltesysteme.net is requested – the server responds with the DS record and the associated RRSIG
  5. DNSKEY net is requested, the reply type is DNSSEC – the server responds with the DNSKEY of net and its RRSIG
  6. DNSKEY verteiltesysteme.net is requested – the server responds with the DNSKEY of verteiltesysteme.net and its RRSIG

Pi-hole verifies the A record for sigfail.verteiltesysteme.net the following way:

  1. The A record A sigfail.verteiltesysteme.net (reply #1) cannot be validated through its RRSIG (in same reply) – as the computed hash is not the same as the hash decrypted with the DNSKEY of verteiltesysteme.net (reply #6)
  2. The DNSKEY of verteiltesysteme.net (reply #6) is validated through its RRSIG using the DS of verteiltesysteme.net (reply #4)
  3. The DNSKEY of net (reply #5) is validated through its RRSIG using the DS of net (reply #2)
  4. The DNSKEY of the root zone (reply #3) is validated through its RRSIG using the trust-anchor hard-wired into Pi-hole

The result is BOGUS as the A sigfail.verteiltesysteme.net is not signed by any trusted key even though DNSSEC is enabled for this zone.

![Exemplary DNSSEC validation for sigfail.verteilesysteme.net|977x395](upload://aqHBSurpXgmSSULYd5BtTfDHIHv.png)

Other DNSSEC test domains result in the following BOGUS codes:

  • dnssec-failed.org: BOGUS (DNSKEY missing)
    • None of the 2 DNSKEY dnssec-failed.org records could be validated by any of the 2 DS dnssec-failed.org records.
    • The DNSKEY dnssec-failed.org was not signed by any trusted keys and, hence, rejected.
  • rhybar.cz: BOGUS (DNSSEC signature expired)
    • RRSIG rhybar.cz: The Signature Expiration field of the RRSIG record is in the past (2008-10-30 08:00:58+00:00).

If your selected upstream does not provide any reasons, Pi-hole can only show BOGUS (refused upstream). In this case, the upstream server is rejecting the query without providing any further details. Your Pi-hole cannot analyse what went wrong.

By default, a local unbound instance as upstream resolver will not provide any details. To enable the full validation inside Pi-hole, you have to add

    # Does not actually turn off DNSSEC, but stops the resolver from
    # withholding bogus answers from clients. 
    val-permissive-mode: yes

to the server section of your config file. However, be aware that this can void the protection provided by DNSSEC for devices querying unbound directly without using your Pi-hole. Hence, we do not recommend enabling permissive mode in production systems.

2 Likes

Thanks for the in-depth explanation. As I have read, you have to trust someone.

Yes, that's true. You have to trust the highest authority. Pi-hole ships this "trust anchor" hard-coded:

You can compare it to this official document of the Internet Assigned Numbers Authority (IANA): DNSSEC Key Signing Key

<KeyDigest id="Klajeyz" validFrom="2017-02-02T00:00:00+00:00">
<KeyTag>20326</KeyTag>
<Algorithm>8</Algorithm>
<DigestType>2</DigestType>
<Digest>E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC683457104237C7F8EC8D</Digest>
</KeyDigest>

Especially the official document with all the original signatures from the Trusted Community Representatives:

1 Like

If one uses the default unbound config from the docs, DNSSEC is enabled at the unbound level correct?

Should I also enable it in pilhole or is that unnecessary?

BTW thank you for the post. This is super informative.

It is your option. Enabling DNSSEC in Pi-hole will add the DNSSEC information into the query log, but Pi-hole won't be doing the DNSSEC task.

Note that when you enable DNSSEC in Pi-hole, this will make the query database grow in size at a slightly faster rate due to the additional information being stored.

1 Like

Very interesting read, thank you. What is the explanation for the DNSSEC queries showing up with the client hostname "pi.hole" and ip "::" versus localhost?

If we have an IPtables rule on our router redirecting all DNS to Pi-hole and blocking DoH (I get these mixed up a lot) then enabling this feature in unbound would be fine for home use right?

These DNSSEC queries are generated internally by FTL and are directly sent (in contrast to going the usual way through the operating system resolver routines). This also helps you telling them apart from other queries done on the same machine (localhost) but another process.

Only if you are sure nothing is using unbound in your home network (unlikely to happen). You gain nothing but some more insight, the protection should be the same*.

*) Plus/minus some bugs that can always be there. Without this option, you have both unbound and Pi-hole do the validation. This is basically guaranteed to catch any false-positives whereas taking unbound out of the equation, theoretically, reduces the level of protection from two to one (pseudo-units).