My current Linux distributions (Ubuntu 23.04 and the Ubuntu-derived Pop!_OS 22.04) use NetworkManager for managing connections, and systemd-resolved
for resolving DNS queries. I’ve set up Cloudflare’s public DNS service with DoT (DNS over TLS) support twice… and I don’t really have a solid conclusion. Is one “better?” 🤷🏻
Contents
- Per-connection mode with NetworkManager only
- Globally with systemd-resolved / NetworkManager
- Useful background info
Per-Connection Mode with NetworkManager
First, I set the IP addresses according to instructions via the NetworkManager GUI, although I could have used the command line as well. The latter would have been like:
$ nmcli c modify id "AirTubes WiFi" \
ipv4.dns 1.1.1.1,1.0.0.1
And similar for the IPv6 addresses with the ipv6.dns
setting. The “AirTubes WiFi” is the connection name, visible as the “NAME” column of nmcli c show
. Using nmcli c
is short for nmcli connection
. For brevity, this post will always use the abbreviation.
The laptop also has an outbound firewall, so I added a rule to allow port 853 out:
$ sudo ufw allow out \
proto tcp to any port 853 \
comment "DNS over TLS"
Finally, activating DNS over TLS required the NetworkManager command line (this setting is not available in other interfaces):
$ nmcli c modify id "AirTubes WiFi" \
connection.dns-over-tls opportunistic
I chose “opportunistic” mode for DoT instead of “yes”, reasoning that for the most part, I’m at home. I’ll forget about this entire thing if I’m out, and that’s when I need the internet to just work, even if the network is blocking port 853.
Only then did I realize the flaw in per-connection settings: these would not apply if I were actually out. There’s no way a public network would be called “AirTubes WiFi,” so NetworkManager would consider it a different connection.
I put the nmcli settings back to their defaults:
$ nmcli c modify id "AirTubes WiFi" \
connection.dns-over-tls ""
$ nmcli c modify id "AirTubes WiFi" \
ipv4.dns ""
And one more time, for ipv6.dns
.
Then, I went for global mode.
Globally with systemd-resolved / NetworkManager
The firewall settings to allow port 853 out, above, remained in place.
This approach uses a drop-in file for systemd-resolved
, configuring it to do DNS over TLS with CloudFlare by default. The file looks like this, without comments, and with the DNS line abbreviated:
[Resolve]
DNS=1.1.1.1#cloudflare-dns.com ... ...
DNSOverTLS=yes
In fact, the DNS line is from the example value for CloudFlare, given in /etc/systemd/resolve.conf
. It was abbreviated here for the blog to display better on phones.
Also note the capitalization here. DNSoverTLS
with lowercase O
is wrong, and will be ignored.
Once it’s ready, the file gets copied into place:
$ sudo mkdir /etc/systemd/resolve.conf.d
$ sudo cp local-resolve.conf \
/etc/systemd/resolve.conf.d
I changed NetworkManager from fully “Automatic” mode to “Automatic (addresses only)”, then left the DNS Servers configuration blank. It appears that the command-line method to do this (if necessary) is:
$ nmcli c modify id "AirTubes WiFi" \
ipv4.ignore-auto-dns yes
$ nmcli c modify id "AirTubes WiFi" \
ipv6.ignore-auto-dns yes
Then, it was a matter of reloading the configurations:
$ sudo systemctl restart systemd-resolved.service
$ nmcli c down id "AirTubes WiFi"
$ nmcli c up id "AirTubes WiFi"
After that, using tcpdump to show traffic to port 853 started reporting packets captured! I was in business.
The limitation of this approach is that I have to remember to set up any new network connections this way: addresses only, no DNS. Otherwise, NetworkManager will tell systemd-resolved
what DNS settings it wants to use, and they will be applied to the connection.
Useful Background Info
As mentioned in passing above, a NetworkManager setting can be reverted to its default value by setting the empty string:
$ nmcli c modify id "AirTubes WiFi" \
connection.dns-over-tls ""
A detailed listing of all of a connection’s settings can be seen with:
$ nmcli c show id "AirTubes WiFi" | less
More information about the settings are also available via man page.
$ man nm-settings-nmcli
Getting somewhat unrelated, there is a command to search man pages for keywords. This is how I discovered that there is an nmtui
(text user interface) command, which gives a terminal-based menu similar to the GUI. Anyway, to search the man pages for NetworkManager:
$ man -k NetworkManager
Firewall rules can be restricted by IP. It was straightforward enough to do IPv4, but for IPv6, the address needs to be “complete” (note the double colon):
$ sudo ufw allow out proto tcp \
to 1.0.0.0/15 port 853 \
comment 'CloudFlare DoT'
$ sudo ufw allow out proto tcp \
to 2606:4700:4700::/112 port 853 \
comment 'CloudFlare DoT v6'
I chose the network masks so that one rule would cover both respective addresses, without allowing the port to the entire Internet. (Discussion of network masks and CIDR notation for them—the /15 and /112—are out of scope for this blog post.)
When using systemd-resolved
for name resolution, the status can be inspected with its own command:
$ resolvectl status
Here, the output should say things like +DNSOverTLS
, and there shouldn’t be any per-link overrides of the “Global” section. It should be more like (reformatted to reduce width):
Global
Protocols: -LLMNR -mDNS +DNSOverTLS
DNSSEC=no/unsupported
resolv.conf mode: stub
DNS Servers 1.1.1.1#cloudflare-dns.com
1.0.0.1#cloudflare-dns.com
2606:4700:4700::1111#cloudflare-dns.com
2606:4700:4700::1001#cloudflare-dns.com
Link 2 (enp2s0)
Current Scopes: none
Protocols: -DefaultRoute +LLMNR -mDNS
+DNSOverTLS DNSSEC=no/unsupported
Link 3 (wlx........)
Current Scopes: none
Protocols: -DefaultRoute +LLMNR -mDNS
+DNSOverTLS DNSSEC=no/unsupported
I hope that covers it!
No comments:
Post a Comment