-- Leo's gemini proxy

-- Connecting to dece.space:1965...

-- Connected

-- Sending request

-- Meta line: 20 text/gemini;lang=en

DNS adventures


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!)


Foreword


In this section I define a bunch of stuff, in case you don't know what DNS is or need some reminders.


DNS


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.


Encrypted DNS


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.


A word on privacy


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!


Objectives


I want blocklists at a DNS level. Preferably locally for instant blocking. I want some blocklists for ads and malware to be sourced from external, reliable sources.

I want personal blocklists to do my own experiments, like what happens when you black-hole Google or Twitter.

I want to try Pi-hole, to see if it's really easy to setup.

I want a bit more privacy, at least not give my whole browsing history to my ISP for free.

I want something efficient, stable and working on a wide variety of devices: Linux, Android, but also macOS and iOS if possible (nobody's perfect!).


Realisation


On my server “solaire” (OVH)


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


Cursing my router


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'.


On my Raspberry Pi 3B “selen”


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:


Pi-hole takes incoming DNS requests from the local network on port 53.

If the site is blocked, it answers instantaneously.

Else it forwards the request to Stubby, which listens for requests from the Pi only on localhost port 5300.

Stubby forwards everything through DNS-over-TLS to my server.


On my Android phone


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.

Someone explaining about that Let's Encrypt issue on Reddit (please read this and come back to tell me that CAs are awesome and TOFU is a mistake)


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.


Conclusion


The Raspberry Pi running my media center now also has Pi-hole for network-wide ad-blocking and some DNS caching. Manual setup for every device is required though, but it's easy and done only once. When I leave this ISP maybe I'll get a sane router to avoid this mess.

It also runs Stubby to forward unblocked domain requests to my server through DNS-over-TLS.

My server forwards requests to Quad9 servers and caches results for efficiency.

Phones of myself, friends and family can be configured to always use my server regardless of the connection used.

Additional blocking rules are set solely on the server so they are applied everywhere.


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!


Additional stuff


DNS caching


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.


Testing DNS-over-TLS


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.


Update (2022-09-24)


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.

-- Response ended

-- Page fetched on Thu May 9 19:28:18 2024