Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 2 additions & 10 deletions explanation/crypto/bind-9-dnssec-cryptography-selection.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,9 @@ Domain Name System Security Extensions (DNSSEC), which provides a set of securit

## DNSSEC validation

Out of the box, the BIND 9 DNS server is configured to try to use DNSSEC whenever it's available, doing all the validation checks automatically. This is done via the `dnssec-validation` setting in `/etc/bind/named.conf.options`:
Out of the box, the BIND 9 DNS server is configured to try to use DNSSEC whenever it's available, doing all the validation checks automatically. This is done via the `dnssec-validation auto` setting in `/etc/bind/named.conf.options`, which became the implicit default as of version `1:9:18.34-1` in Ubuntu 24.10 and above.

```text
options {
(...)
dnssec-validation auto;
(...)
};
```

This can be quickly checked with the help of `dig`. Right after you installed `bind9`, you can run `dig` and ask it about the `isc.org` domain:
DNSSEC can be quickly checked with the help of `dig`. Right after you installed `bind9`, you can run `dig` and ask it about the `isc.org` domain:

```text
$ dig @127.0.0.1 isc.org +dnssec +multiline
Expand Down
59 changes: 55 additions & 4 deletions explanation/dnssec/dnssec.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,47 @@ The public key cryptography underpinning SSL/TLS operates in a similar manner, b

For a more detailed explanation of how the DNSSEC validation is performed, please refer to the [Simplified 12-step DNSSEC validation process](https://bind9.readthedocs.io/en/latest/dnssec-guide.html#the-12-step-dnssec-validation-process-simplified) guide from ISC.

## DNS daemons
Ubuntu supports multiple DNS resolvers, covering a variety of use cases. Most of them support DNSSEC validation, but it might not be activated and set up with valid trust-anchors automatically.

<!-- Using non-breaking hyphen & non-breaking space for improved table spacing. -->
| Daemon | Type | DNSSEC support |
| --- | --- | --- |
| systemd&#8209;resolved | Stub&nbsp;Resolver&nbsp;(local) | Yes. Enabled by default via `DNSSEC=allow-downgrade` setting. |
| dnsmasq | Stub Resolver | Yes. Disabled by default. Controlled via `dnssec` and `conf-file=../trust-anchors.conf` settings. |
| bind9 | Recursive Resolver | Yes. Enabled by default via `dnssec-validation auto;` setting. |
<!-- TODO: What about "unbound"? -->

The **systemd-resolved** stub resolver is pre-installed by default and comes with DNSSEC enabled in fallback mode (as of Ubuntu 25.10). This mode configures the `DNSSEC=allow-downgrade` setting in `/usr/lib/systemd/resolved.conf.d/00-enable-dnssec.conf` (installed by the `systemd-resolved-dnssec` package) and tries to validate the DNSSEC records whenever possible, but at the same time accepts unsigned responses for backwards compatibility with unsigned zones.

The functionality of DNSSEC in **systemd-resolved** can be confirmed, using the `dig` command on the local stub resolver at `127.0.0.53:53`, by checking for the existence of the **ad** (Authenticated Data) flag:
```
$ dig @127.0.0.53 isc.org +dnssec
[...]
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 5, AUTHORITY: 0, ADDITIONAL: 1
```

Should authenticity of DNS records be a concern to you, it's advised to override the default DNSSEC validation settings through an additional drop-in configuration in `/etc/systemd/resolved.conf.d/10-dnssec.conf`, which would reject any unsigned records, after reloading the configuration:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reject any unsigned records part is a bit misleading as that's not what DNSSEC=yes does. It insists on valid signatures for records that are supposed to be signed. Domains not signed remain resolvable.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that this needs clarification. The manpage uses the expression "support DNSSEC properly", and that leaves a lot of room for interpretation, both in the context of documentation, but also in what the correct response of the service is. For the "yes" value, it says "If the DNS server does not properly support DNSSEC all validations will fail.".
Similarly, the "allow-downgrade" option also uses the "support DNSSEC properly" expression.
I guess the behavior we all want is: if records are signed on the server, we want that signature to be valid, and reject if not. If records are not signed (or not supposed to be signed), then we won't require a signature.
And finally, there might be systems out there that definitely want to only deal with signed records, and reject any DNS response that is not authenticated. Is that what "yes" means here?

```
$ cat /etc/systemd/resolved.conf.d/10-dnssec.conf
[Resolve]
DNSSEC=yes

$ sudo systemctl reload systemd-resolved.service
```
```{warning}
Be aware that enforcing DNSSEC can lead to errors, especially for local, unsigned domains and you would only be able to reach such services by accessing them through their IP address directly. For example this could manifest itself by errors like `DNS_PROBE_FINISHED_NXDOMAIN` in your browser, when trying to access services in the local network.
<!-- TODO: What about DNS64? (https://blog.apnic.net/2016/06/09/lets-talk-ipv6-dns64-dnssec/) -->
```

Once DNSSEC validation is enforced, you should also be able to confirm it through higher level checks in your browser, e.g. using those 3rd party services:
* https://internet.nl/test-connection/
* https://wander.science/projects/dns/dnssec-resolver-test/

```{note}
In case of issues with Domain Name resolution, make sure to remove any drop-in configs for **resolved.conf.d**, execute `systemctl reload systemd-resolved.service` and `apt remove systemd-resolved-dnssec`. This will reset any DNSSEC configuration to `DNSSEC=no` and can be confirmed by executing `resolvectl dnssec`.
```

## New Resource Records (RRs)
DNSSEC introduces a set of new Resource Records. Here are the most important ones:

Expand Down Expand Up @@ -112,6 +153,10 @@ This stub resolver has its own configuration for which recursive DNS servers to
DNS Servers: 10.10.17.1
DNS Domain: lxd

```{note}
Starting with Ubuntu 25.10 the `systemd-resolved-dnssec` package is pre-installed enabling the fallback mode as `DNSSEC=allow-downgrade/supported`
```

This configuration is usually provided via {term}`DHCP`, but could also be set via other means. In this particular example, the DNS server that the stub resolver (`systemd-resolved`) will use for all queries that go out on that network interface is 10.10.17.1. The output above also has `DNSSEC=no/unsupported`: we will get back to that in a moment, but it means that `systemd-resolved` is not performing the DNSSEC cryptographic validation.

Given what we have:
Expand All @@ -138,6 +183,10 @@ This is the case if you install the BIND9 DNS server: the default configuration
...
};

```{note}
Starting with version `1:9.18.34-1` in Ubuntu 24.10 and above, the `dnssec-validation auto` setting became the implicit default and does not need to be set explicitly in `named.conf.options` anymore.
```

A critical aspect of this deployment model is the trust in the network segment between the stub resolver and the Validating Resolver. If this network is compromised, the security benefits of DNSSEC can be undermined. While the Validating Resolver performs DNSSEC checks and returns only verified responses, the response could still be tampered with on the final ("last mile") network segment.

This is where the `trust-ad` setting from `/etc/resolv.conf` comes into play:
Expand All @@ -159,7 +208,7 @@ Specifying `trust-ad` in `/etc/resolv.conf` implies in these assumptions:

When using `systemd-resolved` as a stub resolver, as configured above, the network path to the local DNS resolver is inherently trusted, as it is a localhost interface. However, the actual nameserver used is not 127.0.0.53; it depends on `systemd-resolved`'s configuration. Unless local DNSSEC validation is enabled, `systemd-resolved` will strip the ad bit from queries sent to the Validating Resolver and from the received responses.

This is the default case in Ubuntu systems.
This is the default case in Ubuntu systems until Ubuntu 25.04.

Another valid configuration is to not use `systemd-resolved`, but rather point at the Validating Resolver of the network directly, like in this example:

Expand All @@ -176,14 +225,15 @@ As these assumptions have a higher chance of not being true, this is not the def
In any case, having a Validating Resolver in the network is a valid and very useful scenario, and good enough for most cases. And it has the extra benefit that the DNSSEC validation is done only once, at the resolver, for all clients on the network.

### Local DNSSEC validation
Some stub resolvers, such as systemd-resolved, can perform DNSSEC validation locally. This eliminates the risk of network attacks between the resolver and the client, as they reside on the same system. However, local DNSSEC validation introduces additional overhead in the form of multiple DNS queries. For each DNS query, the resolver must fetch the desired record, its digital signature, and the corresponding public key. This process can significantly increase latency, and with multiple clients on the same network request the same record, that's duplicated work.
Some stub resolvers, such as systemd-resolved, can perform DNSSEC validation locally. This eliminates the risk of network attacks between the resolver and the client, as they reside on the same system. However, local DNSSEC validation introduces additional overhead in the form of multiple DNS queries. For each DNS query, the resolver must fetch the desired record, its digital signature, and the corresponding public key. This process can increase latency, and with multiple clients on the same network request the same record, that's duplicated work.

In general, local DNSSEC validation is only required in more specific secure environments.
In general, local DNSSEC validation is still the more secure approach, validating and authenticating the DNS resource records end-to-end, without the need to trust any DNS server along the way. Besides this, DNS-over-TLS (DoT) or DNS-over-HTTPS (DoH) could be used to increase privacy, by encrypting the DNS connection between your local client and the remote Recursive Resolver.

As an example, let's perform the same query using `systemd-resolved` with and without local DNSSEC validation enabled.

Without local DNSSEC validation. First, let's show it's disabled indeed:

$ sudo resolvectl dnssec eth0 false
$ resolvectl dnssec
Global: no
Link 44 (eth0): no
Expand All @@ -194,7 +244,7 @@ Now we perform the query:
isc.org IN MX 10 mx.ams1.isc.org -- link: eth0
isc.org IN MX 5 mx.pao1.isc.org -- link: eth0

-- Information acquired via protocol DNS in 229.5ms.
-- Information acquired via protocol DNS in 37.2ms.
-- Data is authenticated: no; Data was acquired via local or encrypted transport: no
-- Data from: network

Expand Down Expand Up @@ -252,3 +302,4 @@ But even when the validation is local, simpler clients might not get the full pi
* [Tool to visualize the DNSSEC chain of trust of a domain](https://dnsviz.net/)
* [DANE](https://en.wikipedia.org/wiki/DNS-based_Authentication_of_Named_Entities)
* [RFC 4255](https://datatracker.ietf.org/doc/html/rfc4255) - Using DNS to Securely Publish Secure Shell (SSH) Key Fingerprints
* {ref}`dnssec-troubleshooting`
Loading