===== Setting up Unbound on OpenWrt ===== OpenWrt has a very nice default DNS setup. It uses **dnsmasq** to automatically generate A and AAAA records for all DHCPv4 and DHCPv6 leases given out to clients, and queries upstream resolvers (provided by the ISP via DHCP or PPPoE on the WAN interface) for any records outside of that zone. However, those ISP resolvers can become an issue when they are unstable, slow, privacy-unfriendly, are used for censorship, or do a combination of these things. In any of those cases, it would be a lot better to run your own recursive DNS server, talking to authoritative servers directly, while keeping a large DNS cache on your device. [[https://www.nlnetlabs.nl/projects/unbound/about/|Unbound]] is an ideal solution for that. The only issue is that, by default, **Unbound** also functions as a caching and forwarding DNS server running on port 53. We already have **dnsmasq** running there, and we don't want to get rid of it, because it also handles DHCPv4 and the local DNS pool. Moreover, it's tightly integrated in OpenWrt's UCI / LuCI and part of every default OpenWrt image. Instead, we want **dnsmasq** to ignore the ISP DNS servers, keep resolving local addresses like it always did, and use **Unbound** for all of the upstream requests. Setting this up is actually rather simple, but it does have a couple of caveats, which are easy to avoid when you understand how OpenWrt's DNS setup works. ---- \\ ==== OpenWrt default DNS setup ==== The router itself uses DNS so it can resolve hosts like [[https://downloads.openwrt.org/|downloads.openwrt.org]] to install packages. On a stock OpenWrt setup, that goes like this: * Lookup from router --> ''/etc/resolv.conf'' --> **dnsmasq** --> ''/tmp/resolv.conf.d/resolv.conf.auto'' --> ISP resolvers Client devices on your network get the IP addresses of the router advertised as the sole DNS server, meaning all devices in your network will use your OpenWrt router as their resolver. On a stock OpenWrt setup, that goes like this: * Lookup from client --> advertised DNS server --> gateway IP port 53 --> **dnsmasq** --> ''/tmp/resolv.conf.d/resolv.conf.auto'' --> ISP resolvers In a default OpenWrt setup, ''/etc/resolv.conf'' (which is symlinked to ''/tmp/resolv.conf'') will look like this: search lan nameserver 127.0.0.1 nameserver ::1 This means that all local nameserver lookups will go to **dnsmasq** (listening on default DNS port 53) on the loopback interface. In turn, **dnsmasq** will then use ''/tmp/resolv.conf.d/resolv.conf.auto'' to determine the upstream resolvers. This file is automatically generated by **netifd** after WAN interfaces come up and go down. For example, with Freedom Internet, it looks like this: # Interface wan nameserver 185.93.175.43 nameserver 185.232.98.76 # Interface wan_6 nameserver 2a10:3780:2:52:185:93:175:43 nameserver 2a10:3780:2:53:185:232:98:76 ---- \\ ==== Unbound behind dnsmasq ==== If you want to put **Unbound** behind **dnsmasq** and have it handle all DNS lookups (with the exception of the local zone), both for the router itself and for clients on the network, you'll have to install some packages and change some configuration options. First, install **Unbound**: apk update apk add unbound-anchor unbound-daemon Then configure it. Only a few defaults have to be changed: option add_local_fqdn '0' option domain 'lan' option domain_type 'refuse' option listen_port '1053' option query_minimize '1' option ttl_min '0' option validator '1' Note that if you use another local domain instead of ''lan'', you have to change it accordingly. Basically, these options say: * Ignore all handling of the local zone (**dnsmasq** does that). * Listen on port 1053 instead of 53 (**dnsmasq** listens there). * Use [[https://datatracker.ietf.org/doc/html/rfc9156|query name minimisation]] (don't tell the root servers which subdomains you want to resolve). * Don't increase TTL values returned by upstream resolvers. * Enable DNSSEC validation. All other options can be left at their defaults. If you have a router with large amounts of computing power and RAM, such as a Turris Omnia, you may want to change these values too: option num_threads '2' option recursion 'aggressive' option resource 'large' Restart **Unbound**: service unbound restart \\ Then configure **dnsmasq**: option cachesize '0' option noresolv '1' list server '127.0.0.1#1053' list server '::1#1053' Basically, these options say: * Disable caching (**Unbound** does that, and two caches on the same device makes no sense). * Disable parsing of ''/tmp/resolv.conf.d/resolv.conf.auto'' (don't use ISP resolvers). * Only use port 1053 on the loopback interface as the upstream resolver (this is where **Unbound** listens). Restart **dnsmasq**: service dnsmasq restart \\ That's all there is to it. **dnsmasq** will now ignore your ISP DNS servers and use **Unbound** instead, while maintaining the normal behaviour for local DNS, DHCP hostnames as A/AAAA records, and so on. So the situation has now become: * Lookup from router --> ''/etc/resolv.conf'' --> **dnsmasq** --> ''::1#1053'' --> **Unbound** --> local cache or root DNS servers * Lookup from client --> advertised DNS server --> gateway IP port 53 --> **dnsmasq** --> ''::1#1053'' --> **Unbound** --> local cache or root DNS servers ---- \\ ==== Dealing with sysupgrades ==== While this setup is minimally invasive and very easy to revert if you should ever decide to do so, there is one scenario you have to keep in mind: **your DNS will be completely broken after a regular sysupgrade**. The reason for this is simple: - A regular sysupgrade means you download an image from [[https://firmware-selector.openwrt.org/|firmware-selector.openwrt.org]] and flash it. - While you keep the router settings, you **will lose** manually installed packages. - Stock OpenWrt images **do not contain Unbound**. - **dnsmasq** is set to ignore the ISP DNS servers and only query **Unbound**. - There is now **nothing** listening on port 1053 on the loopback interface. - All DNS lookups (other than to the local zone) will fail. Due to all lookups failing, this also means you can't run ''apk update'' and ''apk add'', because [[https://downloads.openwrt.org/|downloads.openwrt.org]] can't be resolved, meaning you can't install **Unbound** to get DNS working again. \\ However Kafkaesque as this may sound, the situation is actually not that dire. Because **dnsmasq**'s init script contains code that manipulates ''resolv.conf'' once it's stopped and started. The important part being in ''dnsmasq_stop()'': ln -sf "/tmp/resolv.conf.d/resolv.conf.auto" /tmp/resolv.conf ''/etc/resolv.conf'' is a symlink to ''/tmp/resolv.conf''. When **dnsmasq** is running, it contains only ''127.0.0.1'' and ''::1'' (read: **dnsmasq**) as the sole DNS server for the OpenWrt system. However, when **dnsmasq** is stopped, ''/tmp/resolv.conf'' will become a symlink to ''/tmp/resolv.conf.d/resolv.conf.auto'', which contains the ISP DNS servers set by **netifd**. In other words, once you stop **dnsmasq**, DNS resolving **on the router** will start working again. (DNS resolving **from clients** will still not work, due to **dnsmasq** not running at all now.) In that case, all you need is a script that: - Stops **dnsmasq** (ISP resolvers are used). - Synchronises the clock (to avoid TLS certificates "not yet being valid"). - Updates the package lists (so we can install **Unbound**). - Installs **Unbound** (so it will be back, listening on port 1053). - Starts **dnsmasq** again (which can then talk to **Unbound** and thus work). Such a script is, as you may expect, really simple to make: #!/bin/sh service dnsmasq stop service sysntpd restart apk update apk add unbound-anchor unbound-daemon service dnsmasq start After a sysupgrade, all you have to do is SSH to your router and run ''install.sh'' (or whatever you called it). ssh root@openwrt sh install.sh Once you've run this script, DNS resolving, both from the router and from clients on the network, will work normally again. \\ Of course, now that OpenWrt 25.12 has [[https://openwrt.org/docs/guide-user/installation/attended.sysupgrade|Attended Sysupgrade]], this is even less of an issue. With Attended Sysupgrade, a custom image is built, containing all the packages your current installation contains. So if you have ''unbound-anchor'' and ''unbound-daemon'' installed, so will the new image. In that case, DNS will simply work after a sysupgrade. **DNS not working is only an issue when you do a classic sysupgrade with a stock OpenWrt image.** ---- \\ ==== Why is this not the default? ==== You may wonder, //"if this is better, why is it not the default?"// The answer is pretty simple: **resources**. **Unbound** is relatively heavy, especially when you consider the fact that OpenWrt targets with a single CPU core running at less than 500 MHz, 4 **M**B of storage and 32 **M**B of RAM exist. Running **Unbound** on a system like that would be (nearly) impossible, if it would even fit on the storage at all. Because OpenWrt (understandably) does not want to maintain different default packages for different targets, the much lighter **dnsmasq** is used as a local caching and forwarding DNS server. But if you have the resources and any reason to not use your ISP's DNS servers (for which there are many), what you really want is validating, recursive, caching nameserver, like **Unbound**. ----