DNS over TLS on OpenWrt with Unbound: Configuration and Leak Checks
TABLE OF CONTENTS (Nhấn để xem)
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 packages
opkg update
opkg install unbound-daemon
# Enable DNS encryption
uci set unbound.fwd_google.enabled="1"
uci set unbound.fwd_google.fallback="0"
uci commit unbound
service 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]
Troubleshooting and runtime audit
When DNS fails, restart logging/Unbound and collect runtime and persistent configuration:[27]
# Restart services
service log restart; service unbound restart
# Log and status
logread -e unbound; netstat -l -n -p | grep -e unbound
# Runtime configuration
pgrep -f -a unbound
head -v -n -0 /etc/resolv.* /tmp/resolv.* /tmp/resolv.*/*
# Persistent configuration
uci 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 packages
opkg update
opkg install luci-app-unbound
service rpcd restart
Then open LuCI → Services → Recursive DNS to configure Unbound.[27]
Switch the provider to Cloudflare
# Configure DoT provider
uci set unbound.fwd_google.enabled="0"
uci set unbound.fwd_cloudflare.enabled="1"
uci set unbound.fwd_cloudflare.fallback="0"
uci commit unbound
service 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 :; done
uci add unbound zone
uci 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 unbound
service 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 validation
uci set unbound.@unbound[0].validator="1"
uci commit unbound
service 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 53
sed -i "s/option listen_port '53'/option listen_port '5353'/g" /etc/config/unbound
sed -i "s/option add_local_fqdn '2'/option add_local_fqdn '0'/g" /etc/config/unbound
# configure dnsmasq to forward to localhost 5353
service dnsmasq stop
uci set dhcp.@dnsmasq[0].noresolv="1"
uci set dhcp.@dnsmasq[0].cachesize='0'
uci -q delete dhcp.@dnsmasq[0].server
uci add_list dhcp.@dnsmasq[0].server="127.0.0.1#5353"
uci add_list dhcp.@dnsmasq[0].server="::1#5353"
uci commit dhcp
service dnsmasq start
service 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 DNS
uci del system.ntp.server
uci add_list system.ntp.server='194.177.4.1' # 0.openwrt.pool.ntp.org
uci add_list system.ntp.server='213.222.217.11' # 1.openwrt.pool.ntp.org
uci add_list system.ntp.server='80.50.102.114' # 2.openwrt.pool.ntp.org
uci add_list system.ntp.server='193.219.28.60' # 3.openwrt.pool.ntp.org
uci commit system
Disable ISP-provided DNS:[27]
# Optional: Disable ISP's DNS server
uci 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 rule
uci 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 server
uci add firewall redirect
uci 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
https://openwrt.org/docs/guide-user/services/dns/dot_unbound — DoT with Unbound. ↩︎
OpenWRT
- Back Up and Restore OpenWrt Configuration with LuCI and SSH
- DNS over TLS on OpenWrt with Unbound: Configuration and Leak Checks Reading
- LuCI Essentials on OpenWrt: Installation, HTTPS, Alternate Ports, and Offline Setup
- Samba File Sharing on OpenWrt: Configuration, Firewall, and Troubleshooting
- Using a USB Storage Device with OpenWrt: Drivers, Partitions, and Automount
Discussion & Feedback 0
Share your questions, feedback or insights with the community