🌐 VI | EN

DNS over TLS on OpenWrt with Unbound: Configuration and Leak Checks

Nghia Phan
Nghia Phan
🌐 Bản tiếng Việt Technical Guide Views

The goal here is to send DNS from the router to upstream resolvers through DNS over TLS (DoT) with Unbound. I decide first whether dnsmasq will give up its DNS role or keep port 53 and forward to Unbound; the two models use different configuration blocks.[27]

Goals and prerequisites

The OpenWrt page describes DoT for encrypted DNS, reduced DNS leak/hijacking exposure, and optional public providers. I need LuCI or SSH access, package/service/log administration, and a console fallback in case a DNS change breaks resolution.[27]

Option A: Unbound as the primary resolver

Disable dnsmasq’s DNS role (its DHCP role can optionally remain with odhcpd), then install Unbound:[27]

# Install packagesopkg updateopkg install unbound-daemon# Enable DNS encryptionuci set unbound.fwd_google.enabled="1"uci set unbound.fwd_google.fallback="0"uci commit unboundservice unbound restart

In this model, LAN clients and the local system use Unbound as their primary resolver, assuming dnsmasq no longer owns port 53.[27]

Test the resolver and protocol

Check resolution through localhost:[27]

nslookup openwrt.org localhost

The source lists provider tests for Cloudflare, AdGuard, NextDNS, Mullvad, Quad9, and OpenDNS, together with DNS Leak and DNSSEC tests. CLI checks include:[27]

dig +short txt proto.on.quad9.net.# should print: doh. or dot. or doq.curl -SL https://test.nextdns.io/

A useful NextDNS result can contain "status": "ok" and "protocol": "DOT". I do not treat one successful nslookup as sufficient; I also check the provider result and the real path.[27]

{{< ads >}}

Troubleshooting and runtime audit

When DNS fails, restart logging/Unbound and collect runtime and persistent configuration:[27]

# Restart servicesservice log restart; service unbound restart# Log and statuslogread -e unbound; netstat -l -n -p | grep -e unbound# Runtime configurationpgrep -f -a unboundhead -v -n -0 /etc/resolv.* /tmp/resolv.* /tmp/resolv.*/*# Persistent configurationuci show unbound

I check the listening port, resolver files, and uci show unbound before adding firewall rules; blindly changing a service that already owns the expected port makes recovery harder.[27]

Manage Unbound through LuCI

Install the integration:[27]

# Install packagesopkg updateopkg install luci-app-unboundservice rpcd restart

Then open LuCI → Services → Recursive DNS to configure Unbound.[27]

Switch the provider to Cloudflare

# Configure DoT provideruci set unbound.fwd_google.enabled="0"uci set unbound.fwd_cloudflare.enabled="1"uci set unbound.fwd_cloudflare.fallback="0"uci commit unboundservice unbound restart

Use another provider or Cloudflare Family

Disable both presets and create a root forward_zone:[27]

# Configure DoT provider (example: "Cloudflare Family Protection")uci set unbound.fwd_google.enabled="0"uci set unbound.fwd_cloudflare.enabled="0"while uci -q del unbound.@zone[4]; do :; doneuci add unbound zoneuci set unbound.@zone[-1].enabled="1"uci set unbound.@zone[-1].fallback="0"uci set unbound.@zone[-1].zone_type="forward_zone"uci add_list unbound.@zone[-1].zone_name="."uci add_list unbound.@zone[-1].server="1.1.1.3"uci add_list unbound.@zone[-1].server="1.0.0.3"uci add_list unbound.@zone[-1].server="2606:4700:4700::1113"uci add_list unbound.@zone[-1].server="2606:4700:4700::1003"uci set unbound.@zone[-1].tls_upstream="1"uci set unbound.@zone[-1].tls_index="family.cloudflare-dns.com"uci commit unboundservice unbound restart

Multiple resolvers improve fault tolerance, but the provider addresses and TLS hostname must match the actual service; do not change tls_index arbitrarily.[27]

Enable DNSSEC

# Enforce DNSSEC validationuci set unbound.@unbound[0].validator="1"uci commit unboundservice unbound restart

The source warns that DNSSEC has fault-tolerance and performance trade-offs. I enable it after basic DoT works so I can identify which layer caused a failure.[27]

Option B: keep dnsmasq on port 53 and forward to Unbound:5353

If dnsmasq should remain the LAN resolver, move Unbound and configure dnsmasq:[27]

# Change unbound port to 5353, because dnsmasq is running already on port 53sed -i "s/option listen_port '53'/option listen_port '5353'/g" /etc/config/unboundsed -i "s/option add_local_fqdn '2'/option add_local_fqdn '0'/g" /etc/config/unbound# configure dnsmasq to forward to localhost 5353service dnsmasq stopuci set dhcp.@dnsmasq[0].noresolv="1"uci set dhcp.@dnsmasq[0].cachesize='0'uci -q delete dhcp.@dnsmasq[0].serveruci add_list dhcp.@dnsmasq[0].server="127.0.0.1#5353"uci add_list dhcp.@dnsmasq[0].server="::1#5353"uci commit dhcpservice dnsmasq startservice unbound restart

Optional steps for the forwarding model

If NTP must work before DNS is ready:[27]

# Optional - ensure, that the NTP server can work without DNSuci del system.ntp.serveruci add_list system.ntp.server='194.177.4.1'    # 0.openwrt.pool.ntp.orguci add_list system.ntp.server='213.222.217.11' # 1.openwrt.pool.ntp.orguci add_list system.ntp.server='80.50.102.114'  # 2.openwrt.pool.ntp.orguci add_list system.ntp.server='193.219.28.60'  # 3.openwrt.pool.ntp.orguci commit system

Disable ISP-provided DNS:[27]

# Optional: Disable ISP's DNS serveruci set network.wan.peerdns='0'uci set network.wan6.peerdns='0'uci commit network

Force LAN clients away from public DNS:[27]

# Optional: Force LAN clients to send DNS queries to dnsmasq (that later will be going to unbound):uci add firewall ruleuci set firewall.@rule[-1].name='Block-Public-DNS'uci set firewall.@rule[-1].src='lan'uci set firewall.@rule[-1].dest='wan'uci set firewall.@rule[-1].dest_port='53 853 5353'uci set firewall.@rule[-1].target='REJECT'uci commit firewall

When an internal DNS server listens on another port, the source also includes a redirect example. Do not use it with an mDNS server:[27]

## 2. Optional: Redirect queries for DNS servers running on non-standard ports. For example: 5353## Warning: don't use this one if you run an mDNS serveruci add firewall redirectuci set firewall.@redirect[-1].dest='lan'uci set firewall.@redirect[-1].target='DNAT'uci set firewall.@redirect[-1].name='Divert-DNS, port 5353'uci set firewall.@redirect[-1].src='lan'uci set firewall.@redirect[-1].src_dport='5353'uci set firewall.@redirect[-1].dest_port='53'uci commit firewall# On the end/etc/init.d/firewall reload

Practical conclusion

I choose one of the two designs: Unbound owns the resolver role, or dnsmasq keeps port 53 and forwards to Unbound on 5353. After each change I check nslookup, the provider protocol, Unbound logs, listening ports, uci show unbound, and the firewall. If a redirect, public-DNS block, or DNSSEC setting breaks access, I revert one logical group through the console instead of resetting the whole router.[27]

Source

Original source: [DoT with Unbound]1, published/updated 2025-02-28.[27]

Sources

[27] https://openwrt.org/docs/guide-user/services/dns/dot_unbound — OpenWrt Wiki: DoT with Unbound

Footnotes

  1. https://openwrt.org/docs/guide-user/services/dns/dot_unbound — DoT with Unbound.

Comments & Discussion

Share your thoughts, ask questions and feedback

Markdown & QQ Emoji
Loading comments...