💾 Archived View for dece.space › notes › 20220617-dns.gmi captured on 2023-07-22 at 16:22:27. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2023-01-29)
-=-=-=-=-=-=-
I wanted to get a better idea of how DNS worked and if it was possible to setup some secure DNS servers without too much hassle, maybe a bit of filtering and ad-blocking, etc. As usual I did something way too complex 👏
(last update 2022-09-24, see you at the end of file!)
In this section I define a bunch of stuff, in case you don't know what DNS is or need some reminders.
DNS (Domain Name System) is the protocol that gives you IP addresses when you give it some domain name. To make a simplified example, when your Gemini browser wishes to visit Medusae.space, it needs the IP address of the server where Medusae.space runs, but you only know the domain name, `medusae.space`. Your device first makes a request to a DNS server that means “hey what's the IP address for medusae.space?” and the server answers “you can reach it at 51.195.119.109”; finally, your browser opens a connection toward this address to get the capsule's content.
Some technical details: DNS servers usually listen on port 53 and the protocol supports requests over both UDP and TCP.
DNS is a very old system — RFC 1034, the first about DNS, dates back to 1987 — so it was not really done with privacy in mind. Indeed, the DNS traffic is by default completely unencrypted, which means that everyone on the network path from you to a DNS server know exactly what server you are interested in. Not great! This is why DNS encryption exists.
DNS-over-TLS (DoT) is roughly the same thing as DNS but messages are wrapped into a TLS connection, making the request confidential. Requests are usually done on a specific port, 853, so DoT traffic is easy to spot even though it's hard to eavesdrop on you. Recent Android versions support the system-wide configuration of a DoT server, under a feature named “Private DNS”.
DNS-over-HTTPS (DoH) is DNS traffic wrapped into HTTPS requests, usually on port 443. Demonic? Sure, but one of the advantages is that running inside the HTTPS layer makes DNS requests hard to distinguish from regular Web traffic, so it's a bit harder to guess what you are browsing, but it's also harder for firewalls to block your DNS requests.
DNSCrypt is an interesting protocol attempting to fix more issues than confidentiality, such as UDP-based amplification attacks, but it is not widely adopted.
DNSSEC is not a secure method for sending DNS requests, but a mechanism to verify answers from DNS servers.
Something to keep in mind though is that even with encrypted DNS it can be very easy to guess what the requested domain name is for an external actor. Take our example with medusae.space above: your ISP sees you send two requests, one encrypted toward a DNS server, and right after that, an encrypted request over medusae.space. It is not hard to guess what the content of your first request was!
There is also an issue with TLS 1.2 and previous versions, where the domain name of the target server you want to reach — whether it's a Gemini capsule or a website — will appear clearly in the request, before the exchange is encrypted…
So yeah, DNS encryption is not magic and it should not give you a false sense of privacy. Still I think it is nice to have and an opportunity to try some DNS software!
It runs Unbound configured to handle only DNS-over-TLS from downstream clients, no classic DNS on port 53. It forwards everything upstream to Quad9 servers on a DNS-over-TLS connection. This way, a client requesting my server will have complete confidentiality, as long as the upstream server is trusted — and for now Quad9 seems fine.
Unbound uses its own blocklist that I pull from StevenBlack's hosts files repo along with some extensions and pipe through a formatting script. This way I get DNS blocking for clients outside of my local network!
StevenBlack hosts files repository on Github
How to use public blocklists with Unbound
My ISP's router is stupid: when you disable DHCP, it only disables it for IPv4 and the DHCPv6 daemon still runs… I can disable IPv6 altogether but it sounds like a defeat to do that in 2022. I have a Netgear N300 with FreshTomato sleeping in a box somewhere but it's a huge pain to connect it to my ISP's network. I mean it, like there are people on forums actually reversing the DHCP frames needed to connect to the rest of the network?!
There is a technique I found where you define an extremely small DHCP address range, like 2 addresses only, and fill it with some always-on devices so that it stops responding to DHCP requests and a better router can answer to your other devices with its own DHCP range. Fun but cursed, and I do not like always-on devices anyway, beside one small Raspi'.
This Pi has been running LibreELEC with Kodi for a few months as my media center, which is neat because of how simple and speedy it is, but it is impractical for running anything else on it. I ran Pi-hole through Docker (Kodi has a nice Docker addon), using the `--network host` option but meh. Also LibreELEC does not support IPv6 yet and I thought it was causing issues on the network, but it turned out to be the router's fault. Oh well, Raspberry Pi OS (a Debian derivative) works great for Kodi and it has IPv6 so I went that way even though we won't do DHCP with this.
I'm installing Pi-hole on the Pi because it is a nice self-contained piece of software and I like the idea of blocking ads at the local network level: no need for an UDP round-trip to get NXDOMAINs. Sadly because of the router shenanigans, I can't take advantage of its internal DHCP server and advertised DNS, so I will have to configure my devices manually.
Pi-hole cannot resolve domains on its own. When you request a domain from it, if it's in the blocklist, you get a blocking answer, else the request is forwarded to an upstream DNS server (and the result is cached). It is not possible to make Pi-hole communicate confidentially with upstream servers, but you can setup another DNS server locally to do this on behalf of Pi-hole. This could be Unbound like on the server, but because we just want some confidential DNS forwarding, Stubby is a nice tool for the job.
Some advice on using Stubby with Pi-hole
In short:
Android lets you pick your own DNS when you configure a Wi-Fi connection statically (without DHCP) but it seems to only let you set the IPv4 parts of the connection so you get the specified IPv4 DNS but have no control over the DHCPv6-advertised DNS. As said earlier, I have no control over the DHCPv6 mess on the network so I'm trapped!
I could not find a solution to this problem so in the end I used the Private DNS feature, set to use my VPS. I don't get the benefits of local-level blocking but I spent enough time on this already! 😄 Because nothing can be simple, Android refused my server certificate because apparently Let's Encrypt is not good enough for this snowflake. Something about an expired intermediate CA that arcane certbot commands failed to solve.
Fortunately, Gandi offered me a simple certificate for my domain, and after I configured Unbound to use this one, Android was happy to use my resolver. At least I know that only my server is used whether I'm at home or through 4G somewhere.
I'm happy that I was able to shed some light on the topic of DNS, and that in the end my stuff seems to work. I expected some latency due to the multiple parts involved but things actually seem slightly faster now.
I did not put config files because I think my setup is too specific to matter for you, but if you want to achieve something similar and run into issues, don't hesitate to ping me on IRC or by mail.
Well, see you in a few months to see if this was indeed a good use of my time or if everything exploded!
I thought Debian had some kind of DNS caching set up but it seems to not be the case by default. On a desktop/laptop, you can configure NetworkManager to let dnsmasq manage its DNS settings, and dnsmasq has DNS caching abilities. There also is systemd-resolved which is present on many systems but for some reason it is disabled on every machine I have, and I don't remember doing it myself. Heh. Anyway Pi-hole has a small cache for the local network, and Unbound also has one.
On Debian 11, dig from bind9-dnsutils does not support the `+tls` option to do DoT, but you can install the package knot-dnsutils, which contains the utility kdig, and this one has the `+tls` option.
For some reason my RPi became a bit unstable lately, and DNS requests would sometime block for several seconds before being handled, so I decided to remove it from my DNS setup, which is not a big deal as I could not profit from the automatic DNS setup through DHCP anyway. My phone still uses my server, and now computers are setup to do DoT directly to my server as well.