Bookmark

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

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]

1
2
3
4
5
6
7
8
9
# 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]

1
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]

1
2
3
4
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]

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# 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]

1
2
3
4
# 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

1
2
3
4
5
6
# 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]

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# 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

1
2
3
4
# 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]

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# 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]

1
2
3
4
5
6
7
# 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]

1
2
3
4
# 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]

1
2
3
4
5
6
7
8
# 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]

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
## 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


0 Bình luận

Góp Ý / Bình Luận / Đánh giá