[SOLVED] No DNS on Guest Network when preventing DNS leaks

Good evening,

I have two firewall zones: lan and guest. Clients in the lan zone are routed over a VPN (Wireguard) whilst clients in the guest zone are routed over the WAN; my ISP.

Under Network > Interfaces > LAN > DHCP Server > Advanced Settings > DHCP-options and Network I've set 6,10.64.0.1 to use my VPN provider's resolver.

I've also set 10.64.0.1 Under Network > DHCP and DNS > Server Settings > DNS forwardings

Under Under Network > Interfaces > {WAN, WAN6} > Common Configuration > Advanced Settings > Use custom DNS, I've set the DNS.watch resolvers.

However, I see DNS leaks from the lan clients; the pertinent configs are below:

resolv.conf.auto
# Interface wan
nameserver 84.200.69.80
nameserver 84.200.70.40
/etc/config/dhcp
config dnsmasq
	option domainneeded '1'
	option localise_queries '1'
	option rebind_protection '1'
	option rebind_localhost '1'
	option local '/lan/'
	option domain 'lan'
	option expandhosts '1'
	option authoritative '1'
	option readethers '1'
	option leasefile '/tmp/dhcp.leases'
	option nonwildcard '1'
	option localservice '1'
	option serversfile '/tmp/adb_list.overall'
	list server '10.64.0.1'

config dhcp 'lan'
	option interface 'lan'
	option leasetime '12h'
	option dhcpv6 'server'
	option ra 'server'
	option ra_management '1'
	option start '100'
	option limit '150'
	list dhcp_option '6,10.64.0.1'

config dhcp 'wan'
	option interface 'wan'
	option ignore '1'

config odhcpd 'odhcpd'
	option maindhcp '0'
	option leasefile '/tmp/hosts/odhcpd'
	option leasetrigger '/usr/sbin/odhcpd-update'
	option loglevel '4'

config dhcp 'Guest'
	option start '100'
	option limit '150'
	option interface 'Guest'
	option leasetime '1h'
/etc/config/network
config interface 'loopback'
	option ifname 'lo'
	option proto 'static'
	option ipaddr '127.0.0.1'
	option netmask '255.0.0.0'

config globals 'globals'
	option ula_prefix 'fd2c:dd09:d634::/48'

config interface 'lan'
	option type 'bridge'
	option ifname 'eth1.1'
	option proto 'static'
	option ipaddr '192.168.100.1'
	option ip6assign '60'
	option netmask '255.255.255.0'

config interface 'wan'
	option ifname 'eth0.2'
	option proto 'pppoe'
	option username '...'
	option password '...'
	option ipv6 'auto'
	option peerdns '0'
	option dns '84.200.69.80 84.200.70.40'

config interface 'wan6'
	option ifname 'eth0.2'
	option proto 'dhcpv6'
	option reqaddress 'try'
	option reqprefix 'auto'
	option peerdns '0'
	option dns '2001:1608:10:25::1c04:b12f 2001:1608:10:25::9249:d69b'

config switch
	option name 'switch0'
	option reset '1'
	option enable_vlan '1'

config switch_vlan
	option device 'switch0'
	option vlan '1'
	option ports '1 2 3 4 6t'

config switch_vlan
	option device 'switch0'
	option vlan '2'
	option ports '5 0t'

config interface 'Guest'
	option proto 'static'
	option netmask '255.255.255.0'
	option ipaddr '10.0.0.1'
	option type 'bridge'

config interface 'mullvad'
	option proto 'wireguard'
	option private_key '...'
	option force_link '1'
	list addresses '10.99.57.166/32'

config wireguard_mullvad
	list allowed_ips '0.0.0.0/0'
	option persistent_keepalive '25'
	option public_key '...'
	option description 'gb2'
	option endpoint_host '185.16.85.130'

config interface 'MODEM'
	option proto 'static'
	option ifname 'eth0.2'
	option ipaddr '192.168.2.2'
	option netmask '255.255.255.0'
/etc/config/firewall
config defaults
	option syn_flood '1'
	option input 'ACCEPT'
	option output 'ACCEPT'
	option forward 'REJECT'

config zone
	option name 'lan'
	option input 'ACCEPT'
	option output 'ACCEPT'
	option forward 'ACCEPT'
	option network 'Streaming lan'

config zone
	option name 'wan'
	option input 'REJECT'
	option output 'ACCEPT'
	option forward 'REJECT'
	option masq '1'
	option mtu_fix '1'
	option network 'mullvad wan wan6 MODEM'

config rule
	option name 'Allow-DHCP-Renew'
	option src 'wan'
	option proto 'udp'
	option dest_port '68'
	option target 'ACCEPT'
	option family 'ipv4'

config rule
	option name 'Allow-Ping'
	option src 'wan'
	option proto 'icmp'
	option icmp_type 'echo-request'
	option family 'ipv4'
	option target 'ACCEPT'

config rule
	option name 'Allow-IGMP'
	option src 'wan'
	option proto 'igmp'
	option family 'ipv4'
	option target 'ACCEPT'

config rule
	option name 'Allow-DHCPv6'
	option src 'wan'
	option proto 'udp'
	option src_ip 'fc00::/6'
	option dest_ip 'fc00::/6'
	option dest_port '546'
	option family 'ipv6'
	option target 'ACCEPT'

config rule
	option name 'Allow-MLD'
	option src 'wan'
	option proto 'icmp'
	option src_ip 'fe80::/10'
	list icmp_type '130/0'
	list icmp_type '131/0'
	list icmp_type '132/0'
	list icmp_type '143/0'
	option family 'ipv6'
	option target 'ACCEPT'

config rule
	option name 'Allow-ICMPv6-Input'
	option src 'wan'
	option proto 'icmp'
	list icmp_type 'echo-request'
	list icmp_type 'echo-reply'
	list icmp_type 'destination-unreachable'
	list icmp_type 'packet-too-big'
	list icmp_type 'time-exceeded'
	list icmp_type 'bad-header'
	list icmp_type 'unknown-header-type'
	list icmp_type 'router-solicitation'
	list icmp_type 'neighbour-solicitation'
	list icmp_type 'router-advertisement'
	list icmp_type 'neighbour-advertisement'
	option limit '1000/sec'
	option family 'ipv6'
	option target 'ACCEPT'

config rule
	option name 'Allow-ICMPv6-Forward'
	option src 'wan'
	option dest '*'
	option proto 'icmp'
	list icmp_type 'echo-request'
	list icmp_type 'echo-reply'
	list icmp_type 'destination-unreachable'
	list icmp_type 'packet-too-big'
	list icmp_type 'time-exceeded'
	list icmp_type 'bad-header'
	list icmp_type 'unknown-header-type'
	option limit '1000/sec'
	option family 'ipv6'
	option target 'ACCEPT'

config rule
	option name 'Allow-IPSec-ESP'
	option src 'wan'
	option dest 'lan'
	option proto 'esp'
	option target 'ACCEPT'

config rule
	option name 'Allow-ISAKMP'
	option src 'wan'
	option dest 'lan'
	option dest_port '500'
	option proto 'udp'
	option target 'ACCEPT'

config include
	option path '/etc/firewall.user'

config zone
	option forward 'REJECT'
	option output 'ACCEPT'
	option name 'guest'
	option input 'REJECT'
	option network 'Guest'

config rule
	option target 'ACCEPT'
	option proto 'udp'
	option dest_port '67-68'
	option name 'guest_dhcp'
	option src 'guest'

config rule
	option target 'ACCEPT'
	option proto 'tcp udp'
	option dest_port '53'
	option name 'guest_dns'
	option src 'guest'

config forwarding
	option dest 'wan'
	option src 'guest'

config forwarding
	option dest 'wan'
	option src 'lan'

config rule
	option enabled '1'
	option src 'guest'
	option name 'Disable Modem Access'
	option dest 'wan'
	option dest_ip '192.168.2.1'
	option target 'DROP'

If I check "Ignore Resolve File" under Network > DHCP and DNS > Server Settings > Resolv and Hosts Files, then lan clients no longer leak DNS queries. However, as soon as I check that, clients on the Guest network cannot resolve hostnames.

I'm sure I'm being a complete peanut, but I can't, for the life of me, figure out what's going on. I fear I may have over-complicated my set-up, so any guidance would be most welcome.

All the best,

ah, I see.

So, how do I force my guest-zone clients to use some other resolver? Is it:

list dhcp_option '6,84.200.69.80,84.200.70.40'

As it is now, you just send to LAN hosts the VPN nameservers. Guests and the router itself will use DNS.watch nameservers. But if you turn it off the guests won't be able to reach it, hence the malfunction.
If you don't hijack the DNS queries, LAN clients can use any NS. Usually some of them come with Google NS hardcoded (android phones and tablets), so I guess this is where the "leak" comes from.

Yes, that's right.

2 Likes

It comes from here:

Wouldn't that be the case if the LAN hosts queried the router instead of the VPN NS directly?
Or didn't I understand correctly what the code snippet does?

Thanks, both. That's all clear. I've now got the following, and it seems to work; no more DNS leaks from the lan-zone clients!

/etc/config/dhcp
config dnsmasq
	option domainneeded '1'
	option localise_queries '1'
	option rebind_protection '1'
	option rebind_localhost '1'
	option local '/lan/'
	option domain 'lan'
	option expandhosts '1'
	option authoritative '1'
	option readethers '1'
	option leasefile '/tmp/dhcp.leases'
	option nonwildcard '1'
	option localservice '1'
	option serversfile '/tmp/adb_list.overall'
	option noresolv '1'
	list server '10.64.0.1'

config dhcp 'lan'
	option interface 'lan'
	option leasetime '12h'
	option dhcpv6 'server'
	option ra 'server'
	option ra_management '1'
	option start '100'
	option limit '150'
	list dhcp_option '6,10.64.0.1'

config dhcp 'wan'
	option interface 'wan'
	option ignore '1'

config odhcpd 'odhcpd'
	option maindhcp '0'
	option leasefile '/tmp/hosts/odhcpd'
	option leasetrigger '/usr/sbin/odhcpd-update'
	option loglevel '4'

config dhcp 'Guest'
	option start '100'
	option limit '150'
	option interface 'Guest'
	option leasetime '1h'
	list dhcp_option '6,84.200.69.80,84.200.70.40'

My next hurdle is going to be stubby. If I set:

list server '127.0.0.1#5453'

then my lan-zone clients will use that, even if I have list dhcp_option '6,10.64.0.1' set.

can I, instead, set list dhcp_option '6,127.0.0.1:5453' on the guest-zone?

noresolv is not defined in the config, so it becomes 0.

resolvfile is not defined in the config, so it becomes /tmp/resolv.conf.auto.

Removing these options from the config changes nothing, because their default values are defined in the dnsmasq init-script.

No, you can't use ports numbers in the DHCP-option-6.


Actually, my previous statement about unreachable DNS via VPN for guest-zone is wrong.
Because guest-clients don't need to access DNS-server via VPN directly, they should do it via Dnsmasq which should be able to reach that DNS-server.

OK, thanks. So, either I stick with the setup I have now (which works), or I employ stubby and have to accept the DNS leaks from the lan-zone clients?

LAN hosts use directly 10.64.0.1 that is advertised from the DHCP server.
Why would there be a leak to a different resolver?
Or do you mean that they use the IPv6 NS?

That's how Dnsmasq works:

I understand that, but LAN hosts have a NS from VPN to query directly. Dnsmasq should not be involved in this, unless queried from the IPv6.

1 Like

Yep, option dhcp_option applies to Dnsmasq only and overrides DHCP-option-6 for DHCPv4.
So, @tectonic should also use option dns which applies to odhcpd and overrides DHCP-option-6 for DHCPv6.
In this case it may be worth replacing Dnsmasq with odhcpd for both DHCPv4 and DHCPv6.

Note that pushing 3-rd party DNS-servers via DHCP-option-6 will effectively disable Adblock.
If you need Adblock, then you should use another method.

2 Likes

Sorry for the slow reply.

Thanks very much, @vgaetera and @trendy for your assistance. I think I'm beginning to make sense of this. From what you've said, do I understand correctly that my DNS leaks are as a result of resolvfile which defaults to /tmp/resolv.conf.auto when not specified explicitly, and that this gets created by the WAN DHCP client.

So, setting 'Ignore Resolve File' and specifying list dhcp_option '6,84.200.69.80,84.200.70.40' for guest-zone clients fixes the leak.

However, if I'm to use Stubby for DNS over TLS, then I have to accept leaks to (e.g.) Cloudflare for lan-zone clients. The only way to get around this is to have multiple dnsmasq instances whereby the guest-zone could use Stubby, but the lan-zone could use 10.64.0.1 (and ignore resolv.conf.auto).

I'll get the first version working (i.e. without Stubby) and then have a go at the separate dnsmasq instance...

If you want to use Adblock or DoT via Stubby, you should not push 3-rd party DNS-servers via DHCP-option-6.

Exactly.
https://openwrt.org/docs/guide-user/base-system/dhcp_configuration#multiple_dhcpdns_serverforwarder_instances

1 Like

I think, think it's working. I see no DNS leaks on lan-zone clients, but guest-zone clients are using DoT and DNSSEC. Adblock is working in both zones.

/etc/config/network
config interface 'loopback'
	option ifname 'lo'
	option proto 'static'
	option ipaddr '127.0.0.1'
	option netmask '255.0.0.0'

config globals 'globals'
	option ula_prefix 'fd2c:dd09:d634::/48'

config interface 'lan'
	option type 'bridge'
	option ifname 'eth1.1'
	option proto 'static'
	option ipaddr '192.168.100.1'
	option ip6assign '60'
	option netmask '255.255.255.0'

config interface 'wan'
	option ifname 'eth0.2'
	option proto 'pppoe'
	option username '...'
	option password '...'
	option ipv6 'auto'
	option peerdns '0'
	option dns '127.0.0.1 84.200.69.80 84.200.70.40'

config interface 'wan6'
	option ifname 'eth0.2'
	option proto 'dhcpv6'
	option reqaddress 'try'
	option reqprefix 'auto'
	option peerdns '0'
	option dns '0::1 2001:1608:10:25::1c04:b12f 2001:1608:10:25::9249:d69b'

config switch
	option name 'switch0'
	option reset '1'
	option enable_vlan '1'

config switch_vlan
	option device 'switch0'
	option vlan '1'
	option ports '1 2 3 4 6t'

config switch_vlan
	option device 'switch0'
	option vlan '2'
	option ports '5 0t'

config interface 'streaming'
	option proto 'static'
	option ipaddr '192.168.3.1'
	option netmask '255.255.255.0'
	option type 'bridge'

config interface 'guest'
	option proto 'static'
	option netmask '255.255.255.0'
	option ipaddr '10.0.0.1'
	option type 'bridge'

config interface 'mullvad'
	option proto 'wireguard'
	option private_key '...'
	option force_link '1'
	list addresses '10.99.57.166/32'

config wireguard_mullvad
	list allowed_ips '0.0.0.0/0'
	option persistent_keepalive '25'
	option public_key '...'
	option description 'mv'
	option endpoint_host '...'

config interface 'MODEM'
	option proto 'static'
	option ifname 'eth0.2'
	option ipaddr '192.168.2.2'
	option netmask '255.255.255.0'
/etc/config/firewall
config defaults
	option syn_flood '1'
	option input 'ACCEPT'
	option output 'ACCEPT'
	option forward 'REJECT'

config zone
	option name 'lan'
	option input 'ACCEPT'
	option output 'ACCEPT'
	option forward 'ACCEPT'
	option network 'streaming lan'

config zone
	option name 'wan'
	option input 'REJECT'
	option output 'ACCEPT'
	option forward 'REJECT'
	option masq '1'
	option mtu_fix '1'
	option network 'mullvad wan wan6 MODEM'

config rule
	option name 'Allow-DHCP-Renew'
	option src 'wan'
	option proto 'udp'
	option dest_port '68'
	option target 'ACCEPT'
	option family 'ipv4'

config rule
	option name 'Allow-Ping'
	option src 'wan'
	option proto 'icmp'
	option icmp_type 'echo-request'
	option family 'ipv4'
	option target 'ACCEPT'

config rule
	option name 'Allow-IGMP'
	option src 'wan'
	option proto 'igmp'
	option family 'ipv4'
	option target 'ACCEPT'

config rule
	option name 'Allow-DHCPv6'
	option src 'wan'
	option proto 'udp'
	option src_ip 'fc00::/6'
	option dest_ip 'fc00::/6'
	option dest_port '546'
	option family 'ipv6'
	option target 'ACCEPT'

config rule
	option name 'Allow-MLD'
	option src 'wan'
	option proto 'icmp'
	option src_ip 'fe80::/10'
	list icmp_type '130/0'
	list icmp_type '131/0'
	list icmp_type '132/0'
	list icmp_type '143/0'
	option family 'ipv6'
	option target 'ACCEPT'

config rule
	option name 'Allow-ICMPv6-Input'
	option src 'wan'
	option proto 'icmp'
	list icmp_type 'echo-request'
	list icmp_type 'echo-reply'
	list icmp_type 'destination-unreachable'
	list icmp_type 'packet-too-big'
	list icmp_type 'time-exceeded'
	list icmp_type 'bad-header'
	list icmp_type 'unknown-header-type'
	list icmp_type 'router-solicitation'
	list icmp_type 'neighbour-solicitation'
	list icmp_type 'router-advertisement'
	list icmp_type 'neighbour-advertisement'
	option limit '1000/sec'
	option family 'ipv6'
	option target 'ACCEPT'

config rule
	option name 'Allow-ICMPv6-Forward'
	option src 'wan'
	option dest '*'
	option proto 'icmp'
	list icmp_type 'echo-request'
	list icmp_type 'echo-reply'
	list icmp_type 'destination-unreachable'
	list icmp_type 'packet-too-big'
	list icmp_type 'time-exceeded'
	list icmp_type 'bad-header'
	list icmp_type 'unknown-header-type'
	option limit '1000/sec'
	option family 'ipv6'
	option target 'ACCEPT'

config rule
	option name 'Allow-IPSec-ESP'
	option src 'wan'
	option dest 'lan'
	option proto 'esp'
	option target 'ACCEPT'

config rule
	option name 'Allow-ISAKMP'
	option src 'wan'
	option dest 'lan'
	option dest_port '500'
	option proto 'udp'
	option target 'ACCEPT'

config include
	option path '/etc/firewall.user'

config zone
	option forward 'REJECT'
	option output 'ACCEPT'
	option name 'guest'
	option input 'REJECT'
	option network 'guest'

config rule
	option target 'ACCEPT'
	option proto 'udp'
	option dest_port '67-68'
	option name 'guest_dhcp'
	option src 'guest'

config rule
	option target 'ACCEPT'
	option proto 'tcp udp'
	option dest_port '53'
	option name 'guest_dns'
	option src 'guest'

config forwarding
	option dest 'wan'
	option src 'guest'

config forwarding
	option dest 'wan'
	option src 'lan'

config rule
	option src 'guest'
	option name 'Disable Modem Access'
	option dest 'wan'
	option dest_ip '192.168.2.1'
	option target 'DROP'

config redirect 'adblock_dns_53'
	option name 'Adblock DNS'
	option src 'lan'
	option proto 'tcp udp'
	option src_dport '53'
	option dest_port '53'
	option target 'DNAT'

config redirect 'adblock_dns_guest_53'
	option proto 'tcp udp'
	option target 'DNAT'
	option name 'Adblock DNS Guest'
	option src 'guest'
	option src_dport '53'
	option dest_port '53'
/etc/config/dhcp
`config dnsmasq 'main'
	option domainneeded '1'
	option localise_queries '1'
	option rebind_protection '1'
	option rebind_localhost '1'
	option local '/lan/'
	option domain 'lan'
	option expandhosts '1'
	option authoritative '1'
	option readethers '1'
	option leasefile '/tmp/dhcp.leases'
	option nonwildcard '1'
	option localservice '1'
	option serversfile '/tmp/adb_list.overall'
	option noresolv '1'
	list server '193.138.218.74'
	list server '10.64.0.1'
	list notinterface 'guest'
	
config dnsmasq 'guest'
	option domainneeded '1'
	option localise_queries '1'
	option rebind_protection '1'
	option rebind_localhost '1'
	option local '/guest/'
	option domain 'guest'
	option expandhosts '1'
	option authoritative '1'
	option readethers '1'
	option leasefile '/tmp/dhcp.leases.guest'
	option nonwildcard '1'
	option localservice '1'
	option serversfile '/tmp/adb_list.overall'
	option noresolv '1'
	list server '127.0.0.1#5453'
	list interface 'guest'
	list notinterface 'lo'
	option dnssec '1'
	option dnsseccheckunsigned '1'

config dhcp 'lan'
	option instance 'main'
	option interface 'lan'
	option leasetime '12h'
	option dhcpv6 'server'
	option ra 'server'
	option ra_management '1'
	option start '100'
	option limit '150'

config dhcp 'wan'
	option interface 'wan'
	option ignore '1'

config odhcpd 'odhcpd'
	option maindhcp '0'
	option leasefile '/tmp/hosts/odhcpd'
	option leasetrigger '/usr/sbin/odhcpd-update'
	option loglevel '4'

config dhcp 'streaming'
	option instance 'main'
	option interface 'streaming'
	option leasetime '12h'
	option start '100'
	option limit '150'

config dhcp 'guests'
	option instance 'guest'
	option interface 'guest'
	option start '100'
	option limit '150'
	option leasetime '1h'

Critical review appreciated: if there's anything that jumps out as 'not right', or there is scope to improve/refine, I'd gladly welcome your feedback. Otherwise, I'll mark this as 'solved'.

Possible future extension: add a 'kidsafe' network with safe-search and filtering (e.g. with OpenDNS Family Shield and the safe-search package. Note to self: list addnhosts '/etc/safe-search/enabled')

2 Likes

something's not right.

Wired clients are seeing dns failures. For example, my HDHomerun Connect logs show "webclient error (dns failed)". Similarly, my Philips Hue Bridge (also wired) cannot connect to meethue. Curiously, whilst the Hue Bridge has an IP address of 192.168.1.119, https://www.meethue.com/api/nupnp reports an IP address of 10.10.10.249.

Somehow, the multiple dnsmasq instances has messed things up. I suspect list interface and list nointerface settings are incorrect, but I can't determine what it might be.

Any suggestions or troubleshooting steps?

Without destination it works as a REDIRECT target forcing the traffic to the loopback interface:
http://ipset.netfilter.org/iptables-extensions.man.html#lbDM

But you need different destinations:

uci set firewall.adblock_dns_53.dest_ip="$(uci get network.lan.ipaddr)"
uci set firewall.adblock_dns_guest_53.dest_ip="$(uci get network.guest.ipaddr)"
uci commit firewall
service firewall restart

Test name resolution from client PC:

nslookup openwrt.org

Verify iptables rules.
Check that PIDs on interfaces match runtime configurations for those interfaces.

iptables-save -t nat
netstat -l -n -p | grep -e dnsmasq
pgrep -f -a dnsmasq

My testing shows that dynamic interface-binding for DNS works.
But I did not validate interface-based DHCP options.

I'm not sure it supports multiple instances or interface-based configurations.
I suggest to disable DHCPv6, at least until you fix DHCPv4+DNS4.

1 Like

Thanks, that's been applied and seems to have sorted my HDHomerun Connect; logs there are clean now.

The Philips Hue is also connecting to the Philips cloud service, but in order to resolve it, I had to remove option noresolv '1' from config dnsmasq 'main'. This means that devices are using the list of resolvers in resolv.conf.auto. I'm back to 'square one' with the DNS leaks. Here's what I'm seeing:

If I set option noresolv '0', then the Philips Hue loses connection to the Philips cloud service, and nslookup openwrt.org shows:

;; Truncated, retrying in TCP mode.
Server:   192.168.100.1
Address: 192.168.100.1#53

Non-authoritative answer:
Name:  openwrt.og
Address: 139.59.209.225

If I set set option noresolv '', then the Philips Hue connects fine to the Philips cloud service, andnslookup openwrt.org` shows:

Server:   192.168.100.1
Address: 192.168.100.1#53

Non-authoritative answer:
Name:  openwrt.og
Address: 139.59.209.225

So, it seems that the Hue Bridge uses UDP exclusively, and, for whatever reason, using my VPN provider's public resolver (193.138.218.74) doesn't accept UDP connections.

Is there a way by which I can enforce the order such that 193.138.218.74 is always preferred, but, if that fails, a second resolver is queried?