DNSSEC (ed448)

Hi!
I can't find any information in the documentation about pi-hole and DNSSEC algorithm 16 (Ed448) support.

My pi-hole instance will not resolve ed448.no, this is probably expected?

Here are the logs:

Nov  2 09:26:19: query[A] ed448.no from <IP>
Nov  2 09:26:19: forwarded ed448.no to <IP>
Nov  2 09:26:19: dnssec-query[DNSKEY] ed448.no to <IP>
Nov  2 09:26:19: reply ed448.no is BOGUS DNSKEY
Nov  2 09:26:19: validation ed448.no is BOGUS
Nov  2 09:26:19: reply ed448.no is 194.63.248.47

Shouldn't pi-hole implement it?
Quoting https://ed448.no:

Ed448 was standardized for use with DNSSEC in February 2017 (RFC8080) and has been a RECOMMENDED algorithm since June 2019 (RFC8624). It has been supported in the .no zone since February 2020.

Thanks!

The option for Use DNSSEC just tells Pi-hole to request and cache DNSSEC records. Pi-hole is then returning the results from the upstream server.

The reason you're seeing a BOGUS return is because the upstream DNSSEC-enabled DNS server(s) which you are using does not support ED448.

I found a thread on Google from 2021 asking if Google has plans to support ED448. Their reply then shows that, as of last year, it's not widely adopted, though that may change over time.

"The KSK, ZSK tables at https://stats.dnssec-tools.org/ show that there are less than 1000 domains using algorithm 16."

You will need to switch to using an upstream DNSSEC-enabled server which supports ED448, or else turn off Pi-hole's DNSSEC option, in order to resolve ED448-enabled domains.

You may be able to enable ED448 support using your own Unbound recursive resolver. Instructions for adding Unbound to Pi-hole are here - unbound - Pi-hole documentation

I use this and I still get BOGUS on that example domain ed448.no. It looks like Unbound can in theory be compiled to enable it, or perhaps there's an option to enable it, but I cannot find it. The Unbound source code references it. Maybe someone can advise how to see if this support is compiled in as standard, and how to turn it on.

  if test $use_ed448 = "yes"; then
  		AC_DEFINE_UNQUOTED([USE_ED448], [1], [Define this to enable ED448 support.])
  fi

Probably best to wait to see if this is possible, and in the meantime look for an upstream resolver you can trust which supports it.

This has been discussed in 2018, see here. You need to run the test, described in the topic), as you can see, in the topic (screenshot), ED448 wasn't really supported at that time.

Things have changed, the current situation, on my system, using pihole-FTL version dnsmasq vDev-14cffae (on request of DL6ER) + compiled unbound (see github), is:

It looks like the domain has a perfect score, see here, so if you can't resolve the domain, either dnsmasq (= piholeFTL) or your upstream resolver doesn't handle the DNSSEC records as expected. Note that I use DNSSEC on unbound (disabled on pihole)

I get

dig ed448.no

; <<>> DiG 9.16.33-Raspbian <<>> ed448.no
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 27031
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;ed448.no.                      IN      A

;; ANSWER SECTION:
ed448.no.               3369    IN      A       194.63.248.47

;; Query time: 0 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Wed Nov 02 16:55:13 CET 2022
;; MSG SIZE  rcvd: 53

Why do think this is expected? That domain has a valid DNSSEC record:

https://dnsviz.net/d/ed448.no/dnssec/

My local instance of unbound (running DNSSEC) is able to resolve the domain:

dig ed448.no @127.0.0.1 -p5335

; <<>> DiG 9.16.33-Raspbian <<>> ed448.no @127.0.0.1 -p5335
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 15103
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;ed448.no.			IN	A

;; ANSWER SECTION:
ed448.no.		3600	IN	A	194.63.248.47

;; Query time: 479 msec
;; SERVER: 127.0.0.1#5335(127.0.0.1)
;; WHEN: Wed Nov 02 17:20:20 CDT 2022
;; MSG SIZE  rcvd: 53

So does mine, but I think this is because no DNSSEC records have been requested with this dig command?

pi@pihole:~ $ dig ed448.no @127.0.0.1 -p 5335

; <<>> DiG 9.16.33-Debian <<>> ed448.no @127.0.0.1 -p 5335
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 11054
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;ed448.no.			IN	A

;; ANSWER SECTION:
ed448.no.		3600	IN	A	194.63.248.47

;; Query time: 476 msec
;; SERVER: 127.0.0.1#5335(127.0.0.1)
;; WHEN: Wed Nov 02 22:26:21 GMT 2022
;; MSG SIZE  rcvd: 53

If I go through Pi-hole, which has DNSSEC enabled and which in turn uses the same Unbound, it fails, presumably because now DNSSEC is being requested and Unbound appears to not be supporting this algorithm unless explicitly enabled or compiled in.

pi@pihole:~ $ dig ed448.no @127.0.0.1

; <<>> DiG 9.16.33-Debian <<>> ed448.no @127.0.0.1
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 46068
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;ed448.no.			IN	A

;; Query time: 32 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Wed Nov 02 22:29:49 GMT 2022
;; MSG SIZE  rcvd: 37

Enabling DNSSEC in Pi-hole should have no effect on DNSSEC behavior in unbound. {Edit} It turns out that it does affect the reporting in the query log. {/Edit} Enabling DNSSEC in Pi-hole adds the DNSSEC column to the query log and stores the DNSSEC information in the query database.

Unbound (when configured per our guide) has DNSSEC enabled. This is validated with the test commands provided in our guide, both of which produce correct replies in my unbound install.

The unbound install from which I successfully resolved that domain is a stock install via apt on Bullseye, configured per the Pi-hole guide.

pi@Pi-3B-DEV:~ $ unbound -V
Version 1.13.1

What explains the behaviour I'm seeing here? Pi-hole's query log shows that it has received a BOGUS response from Unbound. Yet if I query Unbound directly it works fine. In otherwords, testing the assertion that Pi-hole is not 'resolving' ED448 cannot be tested by simply querying the DNSSEC-enabled recursive DNS server with a simple dig A.

The Verisign Labs tester reports that the domain is correctly configured, so the only reason I would expect to see a BOGUS response would be if Unbound is not supporting ED448 out the box, which it apparently doesn't (ref earlier source code).

I see exactly the same results if I use Cloudflare (which is DNSSEC-enabled) in Pi-hole and dig.

Requesting from Cloudflare directly, no problem:

pi@pihole:~ $ dig ed448.no @1.1.1.1

; <<>> DiG 9.16.33-Debian <<>> ed448.no @1.1.1.1
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 29187
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
; EDE: 1 (Unsupported DNSKEY Algorithm): (for DNSKEY ed448.no., id = 61468)
;; QUESTION SECTION:
;ed448.no.			IN	A

;; ANSWER SECTION:
ed448.no.		3600	IN	A	194.63.248.47

;; Query time: 76 msec
;; SERVER: 1.1.1.1#53(1.1.1.1)
;; WHEN: Thu Nov 03 00:16:48 GMT 2022
;; MSG SIZE  rcvd: 91

Requesting from Pi-hole with DNSSEC enabled and with Cloudflare set as upstream, fails as BOGUS:

pi@pihole:~ $ dig ed448.no @127.0.0.1

; <<>> DiG 9.16.33-Debian <<>> ed448.no @127.0.0.1
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 38535
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
; EDE: 1 (Unsupported DNSKEY Algorithm): (for DNSKEY ed448.no., id = 61468)
;; QUESTION SECTION:
;ed448.no.			IN	A

;; Query time: 68 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Thu Nov 03 00:17:33 GMT 2022
;; MSG SIZE  rcvd: 75

bogus

Requesting from Pi-hole with DNSSEC disabled and with Cloudflare set as upstream, no problem:

pi@pihole:~ $ dig ed448.no @127.0.0.1

; <<>> DiG 9.16.33-Debian <<>> ed448.no @127.0.0.1
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 61377
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
; EDE: 1 (Unsupported DNSKEY Algorithm): (for DNSKEY ed448.no., id = 61468)
;; QUESTION SECTION:
;ed448.no.			IN	A

;; ANSWER SECTION:
ed448.no.		3600	IN	A	194.63.248.47

;; Query time: 20 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Thu Nov 03 00:21:49 GMT 2022
;; MSG SIZE  rcvd: 91

My interpretation is that just because a DNS server is DNSSEC-enabled, it still requires the requesting party to process the relevant validation. Using dig domain @dnsserver to query Unbound won't do that, and since the domain is correctly configured with ED448, the conclusion is that Unbound, or the other upstream servers tested, are unable to process ED448.

The tester at https://rootcanary.org/test.html says that Cloudflare 1.1.1.1 is not supporting ED448, and neither is a stock apt install of Unbound.

I would contend that unbound (at least the version of unbound that ships with Raspbian Bullseye) does support this. This is evidenced by the results of my direct dig to unbound which correctly resolved the IP.

[Edit]

However, if I enable DNSSEC in Pi-hole and run the query through Pi-hole, and from Pi-hole to unbound, I also see the BOGUS reply in Pi-hole.

So, it appears that somewhere between unbound correctly resolving the domain and Pi-hole reporting the domain as BOGUS lies the problem.

[/Edit]

[Edit 2]

If I restart both unbound and Pi-hole after deselecting DNSSEC in Pi-hole, the domain once again is shown as correctly resolved in Pi-hole, still using unbound as the upstream DNS server.

In the graphic below, the top line is without DNSSEC enabled in Pi-hole, the lower lines have DNSSEC enabled.

[/Edit 2]

I am using the stock Unbound, installed via apt as per the docs guide, same as you.

pi@pihole~ $ unbound -V
Version 1.13.1

If this was an Unbound issue I would not expect to see the same results using Cloudflare, nor the algo test results seen, nor the OP's original observations regarding a BOGUS reply from their upstream.

Conclusion: the OP's upstream, Cloudflare, stock Unbound do not support ED448, and this means that either they need to use 1) an upstream which does support ED448 or else 2) disable DNSSEC in Pi-hole so that the BOGUS results are not utilised, and that querying the upstream directly is the same as that second option (ie no conclusion can be reached about algo support in that case).

I'm seeing the exact same results as you, as described. My conclusion differs though.

Edit: Unbound does support ED448 out the box, more info in posts below.

I think we have established that unbound (at least the version shipped with Bullseye) does support this.

The problem appears to lie in the DNSSEC toggle in Pi-hole.

With DNSSEC toggled OFF - the query is correctly reported as resolved by the upstream DNS resolver (in my case unbound).

With DNSSEC toggled ON - the query is reported as BOGUS by Pi-hole, but not by unbound.

Perhaps this is due to how FTL (or dnsmasq) processes this reply.

@DL6ER

My apologies @jfb you are quite correct, Pi-hole's behaviour is odd and I have been misinterpreting these results as Pi-hole being correct and meaning that the resolvers are not supporting ED448. Has all this exposed a FTL bug?

I added val-log-level: 2 to the Unbound conf and if I test this with a known SERVFAIL it indeed reports the failure mode for both Unbound directly and Unbound via Pi-hole.

$ sudo unbound-control flush_zone .
$ dig sigfail.verteiltesysteme.net @127.0.0.1 -p 5335

log -> info: validation failure <sigfail.verteiltesysteme.net. A IN>: signature crypto failed from 134.91.78.141

$ sudo unbound-control flush_zone .
$ dig sigfail.verteiltesysteme.net @127.0.0.1

log -> info: validation failure <sigfail.verteiltesysteme.net. A IN>: signature crypto failed from 134.91.78.139

In the Pi-hole case, Pi-hole's query log shows:

OK (answered by localhost#5335)
BOGUS (refused upstream)

HOWEVER doing the same test with ed448.no gives no errors logged by Unbound. Unbound correctly processes the domain. Using Unbound directly it returns a result and NOERROR, but using Unbound via Pi-hole it returns SERVFAIL and the query log shows, for both the A and DNSKEY records:

OK (answered by localhost#5335)
BOGUS

Note that doesn't say "refused upstream" anymore?

Increasing the Unbound log verbosity to try and see what's happening shows a number of referrals, answers and some nxdomain lines, and shows the ED448 being processed. Both queries end with

info: validate(positive): sec_status_secure

So why would Pi-hole receive that answer, whether from Unbound or from Cloudflare or any other such resolver, and show BOGUS and return a SERVFAIL? Turning off DNSSEC in Pi-hole prevents whatever is happening from occuring, and the query result is passed to the client as it should be.

I suspect then this must be what the OP is seeing in his opening post.

Years ago, ref the discussion (topic) in my first reply, I decided to stop enabeling DNSSEC on pihole (dnsmasq) and rely on the, in my view, better implementation from unbound to verify DNSSEC integrity. The downside of course:

  • I no longer have the information in the query log, get SERVFAIL on error, and thus have to match the SERVFAIL query with the unbound log, this to verify if it is actually a DNSSEC problem.
  • From time to time, even unbound has problems, processing DNSSEC records, resulting in a false report.

To overcome the second problem (unbound false positive), I found a solution. Once I verified the DNSSEC chain for a specific domain is valid (check here), that unbound doesn't appear to validate correctly, I simply add an entry to my dnsmasq config files, example:

server=/www.raspberrypi.org/1.1.1.1

As a result, all queries for the domain 'www.raspberrypi.org' will no longer be handled by unbound but forwarded to 1.1.1.1. You can even run a second resolver (bind, knot-resolver) on your system, listening on a different port, and redirect to that resolver, example:

server=/www.raspberrypi.org/127.10.10.5#5555

That solves the second problem...

What I'm still hoping for, to solve the first problem: dnsmasq has a setting proxy-dnssec, see the dnsmasq man.

Copy the DNSSEC Authenticated Data bit from upstream servers to downstream clients. This is an alternative to having dnsmasq validate DNSSEC, but it depends on the security of the network between dnsmasq and the upstream servers, and the trustworthiness of the upstream servers. Note that caching the Authenticated Data bit correctly in all cases is not technically possible. If the AD bit is to be relied upon when using this option, then the cache should be disabled using --cache-size=0. In most cases, enabling DNSSEC validation within dnsmasq is a better option. See --dnssec for details.

I wonder if pihole-FTL could be modified to allow for either a full DNSSEC evaluation (possible now) OR the proxy-dnssec option. Looking at the AD bit, as described in the dnsmasq man would allow pihole-FTL to evaluate the DNSSEC result from upstream and show this in the query log.

Wishful thinking?

Hi!
Thank you all for those clarification.

My pi-hole instance was already using Unbound. I just forget that part, pi-hole just works and then you forget how you did the setup years ago, and that's what I like of it!

My unbound server (Debian 11, 1.13.1), is able to use DNSSEC and resolve ed448.no:

❯ sudo netstat -plantu | grep unbound
tcp        0      0 127.0.0.1:53            0.0.0.0:*               LISTEN      3565562/unbound

❯ dig @127.0.0.1 ed448.no

; <<>> DiG 9.16.27-Debian <<>> @127.0.0.1 ed448.no
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 62323
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;ed448.no.			IN	A

;; ANSWER SECTION:
ed448.no.		3380	IN	A	194.63.248.47

;; Query time: 0 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Thu Nov 03 09:59:38 CET 2022
;; MSG SIZE  rcvd: 53

And I have also applied recommendations from unbound - Pi-hole documentation.

Finally, I have disabled DNSSEC on the pi-hole and will let Unbound handle it, and now everything works:

❯ dig @100.64.20.1 ed448.no

; <<>> DiG 9.16.27-Debian <<>> @100.64.20.1 ed448.no
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 4153
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;ed448.no.			IN	A

;; ANSWER SECTION:
ed448.no.		2791	IN	A	194.63.248.47

;; Query time: 44 msec
;; SERVER: 100.64.20.1#53(100.64.20.1)
;; WHEN: Thu Nov 03 10:03:41 CET 2022
;; MSG SIZE  rcvd: 53

Thanks again!

Well, "odd" and "bug" surely depends on your point of view :wink: dnsmasq may simply not implement ED448 (mind that every cryptographic algorithm needs to be implemented on its own). Is it an optional or a mandatory curve for DNSSEC? Also, is this more a academic question or are there real-world examples with pages signed using this algorithm?

Indeed, I'm so used to Pi-hole working seamlessly that I hadn't considered that FTL/dnsmasq may not support a specific algorithm.

It's optional like ED25519

The Google link I posted earlier suggests that uptake is low. The RFC lists some advantages which may make it preferable for some deployments. I think that not supporting it is less than ideal, given that all the DNSSEC-enabled upstreams in Pi-hole do support it, but that the negative consequence of that is probably limited at this time, though it was enough for the OP @fauust to raise the matter.

While you were typing, I gathered some additional information, too. I'll now put them up as a new post so they aren't lost in an edit above.

The elliptic curve signatures we are discussing here are proposed in RFC 8080. Both Ed448 and Ed25519 are actually supported by dnsmasq (and, hence, FTL) given it was compiled with a sufficiently recent version of libnettle: at least 3.1 for Ed25519, at least 3.6 for Ed448 support.

And here is the problem: FTL has to be compiled for the oldest release we still want to support. At this time, this is Debian Buster which is shipping only nettle 3.4.1-1 which is the very reason for why the pre-compiled FTL binary does not support Ed448. The next upgrade of the minimum supported operating system (to Debian Bullseye) will bring this to nettle 3.7.3-1 and only then officially include Ed448 support.

An obvious solution would be to compile FTL from source but we could theoretically discuss other alternatives (like the pre-compiled musl variant that already has nettle 3.7.3 as it is built on Alpine 3.16 rather than on Debian). However, to date there is no automated way to change the architecture from "generic" to "musl".

2 Likes

Thanks for the details, that explains everything. What does FTL do when it sees the ED448 DNSKEYs? Does it try to process them and fail because it doesn't have the algorithm support compiled in? Is that why the Pi-hole query log reports "BOGUS" instead of "BOGUS (refused upstream)" – ie upstream is happy and returned a result but FTL cannot process it?

Does the minimum supported OS move to Bullseye once Bookworm is released (rough ETA mid-2023)? At which point, if I'm understanding right, the issue will disappear.

I'll clone my Pi-hole and experiment to see how easy it is to compile FTL on Bullseye and test it with that ED448 domain (which has turned out to be a handy test target). If I can get it working I'll document it from a user POV and submit, so it's easy for anyone needing ED448 validation support in the short-term.

This affects other tools shipping with Buster as well, e.g. dnsutils' delv doesn't know how to handle ed448 as well:

~ $ delv ed448.ch @127.0.0.1 -p 5335
;; validating ed448.ch/A: no valid signature found
; unsigned answer
ed448.ch.               3600    IN      A       35.185.44.232
ed448.ch.               3200171710 IN   RRSIG   A 16 2 3600 20221117000000 20221027000000 19736 ed448.ch. /S0pwrKIeKlUVw8PmFr5vEqJ4TLGHktr8KHOFiSSCRmNw1Aqe5msdw+1 K0hO0bXQD1DLou6CY3IALuOOsyvCzzcLjr1ifeOK0zDoMl7qucyDDv3N 4z2pfBPS9f6ff7C20qJw7HNwnSBfWY3Iy12sWRgA

Currently, I'd tend to treat that as the former.

A short serach only brought up two domains, and those seem to be explictily designed to test ed488 algorithm support , ed448.no and ed448.ch.
Now that's far from hard evidence.

But more significantly, DNSSEC and DANE Deployment Statistics still do not list any entries for ed448/algorithm 16:

That site claims to register algorithm usage only once surveyed counts exceeds a threshhold of 1,000 installations (of about 20 million), meaning that ed448 adoption is still quite slow (less than 0.005% in the surveyed sample).

Just so you are all aware: We are working hard behind the scenes to get a more recent nettle library into the pre-compiled FTL binaries so you can have Ed448 in FTL already now.

This PR adds the most recent crypto library for all architectures except armv6hf (Raspberry Pi Model 1 and Zero + Zero W/WH) and armv5te (some very old 32bit Synology drives). We cannot have a more recent crypto library (easily enough) due to heavy limitations of the old cross-compiling suite. Anyway, the vast majority of devices used for Pi-hole these days seems to be x86_64 and armv7 which will include the new versions.

You can check for yourself after the next update as pihole-FTL -vv will now print the crypto library version.

1 Like