Wireguard with PBR DNS Leak

Hello,
I'm trying to use Wireguard with PBR to do some split tunneling with devices that I want to use the VPN on.
I followed this guide, and ended up with most of it on my final configuration.
The only problem now is that when trying to use the DNS server from mullvad so the DNS leak stops, it always makes my devices not connected to the VPN interface lose connection entirely, and then it only by activating the vpn on the device or enabling in on PBR.
When adding the 'DHCP and DNS settings' part, it fixes the DNS Leak but stops the network on non vpn devices.

/etc/config/network

config interface 'loopback'
        option device 'lo'
        option proto 'static'
        option ipaddr '127.0.0.1'
        option netmask '255.0.0.0'

config globals 'globals'
        option ula_prefix 'fd57:ea44:8dd8::/48'
        option packet_steering '1'

config device
        option name 'br-lan'
        option type 'bridge'
        list ports 'lan1'
        list ports 'lan2'
        list ports 'lan3'
        list ports 'lan4'
        list ports 'sfp2'

config interface 'lan'
        option device 'br-lan'
        option proto 'static'
        option ipaddr '192.168.1.1'
        option netmask '255.255.255.0'
        option ip6assign '60'

config device
        option name 'br-wan'
        option type 'bridge'
        list ports 'eth1'
        list ports 'wan'

config device
        option name 'eth1'
        option macaddr 'a2:a8:cc:95:fb:2d'

config device
        option name 'wan'
        option macaddr 'a2:a8:cc:95:fb:2d'

config interface 'wan'
        option device 'br-wan'
        option proto 'dhcp'

config interface 'wan6'
        option device 'br-wan'
        option proto 'dhcpv6'
        option reqaddress 'try'
        option reqprefix 'auto'

config interface 'WGINTERFACE'
        option proto 'wireguard'
        option private_key 'privkey'
        list addresses 'vpnip'
        list addresses 'vpnip6'
        option defaultroute '0'

config wireguard_WGINTERFACE
        option public_key 'pub'
        option endpoint_host 'pubvpnip'
        option endpoint_port 'port'
        option description 'WireGuard Peer'
        option persistent_keepalive '25'
        list allowed_ips '0.0.0.0/0'
        list allowed_ips '::/0'
        option route_allowed_ips '0'

etc/config/pbr

config pbr 'config'
        option enabled '1'
        option verbosity '2'
        option strict_enforcement '1'
        option resolver_set 'none'
        option ipv6_enabled '0'
        list ignored_interface 'vpnserver'
        list ignored_interface 'wgserver'
        option boot_timeout '30'
        option rule_create_option 'add'
        option procd_reload_delay '1'
        option webui_show_ignore_target '0'
        list webui_supported_protocol 'all'
        list webui_supported_protocol 'tcp'
        list webui_supported_protocol 'udp'
        list webui_supported_protocol 'tcp udp'
        list webui_supported_protocol 'icmp'

config include
        option path '/usr/share/pbr/pbr.user.aws'
        option enabled '0'

config include
        option path '/usr/share/pbr/pbr.user.netflix'
        option enabled '0'

config policy
        option name 'Plex/Emby Local Server'
        option interface 'wan'
        option src_port '8096 8920 32400'
        option enabled '0'

config policy
        option name 'Plex/Emby Remote Servers'
        option interface 'wan'
        option dest_addr 'plex.tv my.plexapp.com emby.media app.emby.media tv.emby.media'
        option enabled '0'

config policy
        option name 'Route to WG'
        option interface 'WGINTERFACE'
        option src_addr '192.168.1.247 192.168.1.107'

# This one is a test, you can ignore it
config policy
        option name 'Route to WAN'
        option src_addr '192.168.1.0/24'
        option interface 'wan'
        option enabled '0'

etc/config/firewall

config defaults
        option input 'REJECT'
        option output 'ACCEPT'
        option forward 'REJECT'
        option synflood_protect '1'
        option flow_offloading '1'

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

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

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 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 zone
        option name 'WGZONE'
        option input 'REJECT'
        option output 'ACCEPT'
        option forward 'REJECT'
        option masq '1'
        option mtu_fix '1'
        list network 'WGINTERFACE'

config include 'pbr'
        option fw4_compatible '1'
        option type 'script'
        option path '/usr/share/pbr/pbr.firewall.include'

config forwarding
        option src 'lan'
        option dest 'WGZONE'

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

/etc/config/dhcp

config dnsmasq
        option domainneeded '1'
        option localise_queries '1'
        option rebind_protection '0'
        option local '/lan/'
        option domain 'lan'
        option expandhosts '1'
        option cachesize '1000'
        option authoritative '1'
        option readethers '1'
        option leasefile '/tmp/dhcp.leases'
        option resolvfile '/tmp/resolv.conf.d/resolv.conf.auto'
        option localservice '1'
        option ednspacket_max '1232'
        option port '54'
        list server '192.168.1.1'

config dhcp 'lan'
        option interface 'lan'
        option start '100'
        option limit '150'
        option leasetime '12h'
        option dhcpv4 'server'
        option dhcpv6 'server'
        list dns 'ip6 for adguard'
        option ra 'server'

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'

...
#My hosts

Not sure if there's anything else I can paste in here.
Thanks.

Ah I think I see your problem :slight_smile:

So according the rfc1918 specification the dns server from mullvad which they show in the documentation as 6,10.64.0.1 in the dhcp options, this is the local ip for your wireguard tunnel.

the reason why it fails or you get connection refused with this setup is because this dns option is only accessible to clients in this local tunnel, therefor the local lan subnet whos not routed to the vpn is not aware of this dns ip as it is not a public ip and cannot be resolved.

Also if you use option 6, it means you use it for the full dhcp server on that network interface, that might be not so playing nice either for lan clients who dont route on the vpn and have no access to the local subnet of the tunnel, you might need extra pbr rules to forward dst port 53 to dst 10.64.0.1 to wg interface for those two src lan ips, and then you can leave out the dhcp options as pbr takes care for it.

And for last in case of trouble of access to clients to or from the wireguard routed clients to normal lan ips.

If it doesn't work you might need to add one policy above the wg routed one with target ignore, you can enable the ignore rules in the last settings tab in luci I believe or add it in the config, but I think it might be not needed.

Note I edited it again because i made it to confusing now it makes sense :yum:

Hey, thanks for your response!

I understood your explanation on why this is not working.
But when it came to the PBR rule part, it became a little bit confusing to me.
You said:

What I understood from that is for me to forward to destination address 10.64.0.1/port 53, and my source IP being what I already have on the other PBR rule.
But doing so like this:
image
Doesn't work as expected, I'm not sure on what I did wrong.

I believe you need to change the chain to forward.

That way you basicly hijack the dns traffic for the src ip and sent it to the mullvads subnet if that doesn't work in PBR, you may also be able to use the firewalls portforward option to do the same aslong the src ip is the client which uses the wireguard routing.

Unfortunately, it's still leaking, changed chain to forward and added port forward in firewall port forward (not 100% sure if that's what you meant), but no success.

I only know how to do it with iptables but maybe that will give you an idea

pbrip=192.168.1.118
dns4=10.64.0.1

iptables -t nat -I PREROUTING -p udp -s $pbrip --dport 53 -j DNAT --to $dns4
iptables -t nat -I PREROUTING -p tcp -s $pbrip --dport 53 -j DNAT --to $dns4

Thanks, but honestly I don't know anything about iptables, so I don't think I can translate it to whatever I need haha.
I did come across an OpenWrt Wiki page with DNS Hijacking instructions, it did exactly what @xize told me to.
Not sure if this is an edge case, but I can't really find any post about this.
The only stuff that I found was in old DD-WRT post, that someone had a similar problem (you were there helping them too @egc).
Anyway, thanks for the help :smiley:.

you need to change source zone to lan and destination to wgzone :slight_smile:

Sorry, I forgot that the screenshot above was wrong, I did change it after reading the wiki page, source lan/dest unspecified.
But even after changing dest to WG, same result :confused:

Yeah I know my way around DDWRT (it is actually now incorporated in the DDWRT GUI) but still learning in OpenWRT :slight_smile:

I think I got a better solution, if you go into the wireguards interface and click advanced what happens if you add the dns in the custom dns field?

then this setup becomes less complicated because once it uses wireguards route it vice versa uses the correct dns because its part of the interface.

Still leaking (fyi I did restart my router), no success on changing the custom dns field.

click here to expand PBR image

So 10.34.79.0/24 is a smilliar subnet this subnet belongs to pcnet, the rule to wireguard is the last one from the bottom :slight_smile:

for me this works but you also have to specify the source ip in advanced (I did not do that but it makes sense).

the only thing you need to assure is that you use ipconfig /flushdns on windows, and in your browser you also flush your dns cache in brave it is: brave://net-internals/#dns and for chrome its chrome://net-internals/#dns.

for me it got routed correctly once I checked dnsleaktest :slight_smile:

It did not fix my issue yet, but in my firewall there was something different (I set only x.x.x.118 on advanced settings), now it's like this:
image

And in my PBR configuration there are no changes, only the two policies that were said in the beginning (I tried with the 'Forward to 53' policy enabled too, did not work):

If you don't see anything wrong here, would mind showing your Wireguard Interface? General, advanced and peers settings if possible, I feel like I may be missing something there, but I'm not sure at all.

I'm using my computer to configure all this, but my laptop (Ubuntu) through Wi-Fi to test the leakage, even when clearing the DNS Cache on both chrome and Ubuntu results on the same output.

here is my configuration please be aware mine would be different to some extend:

network.wan=interface
network.wan.proto='pppoe'
network.wan.username='<private>'
network.wan.password='<private>'
network.wan.device='eth2.6'
network.wan.delegate='0'
network.wan.ipv6='0'
network.wan.metric='20' <- this is so if I shutsdown wgclient wan fallsback.

network.pcnet=interface
network.pcnet.proto='static'
network.pcnet.ipaddr='10.34.79.1'
network.pcnet.netmask='255.255.255.0'
network.pcnet.device='br-pcnet'
network.pcnet.defaultroute='0' <-- no default route given because pbr handles this, in your case you may want pbr also to do the wan part for the clients using wan.
network.pcnet.delegate='0'

-- this is the main wireguard interface with peers inside --
network.wgclient=interface
network.wgclient.proto='wireguard'
network.wgclient.addresses='10.67.41.39/32'
network.wgclient.private_key='<private>'
network.wgclient.defaultroute='0' <- no default route

-- this is the first peer in wgclient --
network.cfg1e130b=wireguard_wgclient
network.cfg1e130b.description='mullvad_nl'
network.cfg1e130b.public_key='<private>'
network.cfg1e130b.allowed_ips='0.0.0.0/0'
network.cfg1e130b.endpoint_host='<private>'
network.cfg1e130b.endpoint_port='51820'
network.cfg1e130b.persistent_keepalive='0'

Note I have disabled ipv6 perhaps that could be a issue or: you have enabled checkbox Use default gateway in the interfaces advanced tab when PBR should take over the routing.

edit

I forgot to mentoin also that my firewall zone is different designed.

the input should be just set to reject or drop I forgotten this when I was testing something else accepting this should be a little unsafe in cases if the vpn had a local area network.

@zhyph I'm not sure what devices you're using (the affected client devices), but it's worth knowing that Android devices flatly ignore DNS server settings and use 8.8.8.8 regardless. I use DNAT rules to force client DNS queries to be resolved by my own servers.

Honestly I don't see any differences, besides the fact that I did not change anything about the IPV6 settings, we have pretty much the same config.

  • My WireGuard Interface has defaultroute=0, since I need wan to be the main one for PBR.
  • You have allowed_ips='0.0.0.0/0', just like I do, but I'm not sure if you are using route_allowed_ips='1', since the PBR doc says to disable it.

Other than that, my firewall is like this, but I don't have this can be the problem, if I take the forwarding off wgzone or wan, they don't work (as expected)

I've been testing everything through my Ubuntu laptop.

2 Likes

mine is set to route_allowed_ips='0'

but when I look to your zone wgzone need to be forwarded to wan, otherwise it would likely not use internet.

the only other possibility what I think is that you need to create a PBR rule before the wireguard one like:

src ip: 192.168.1.0/24
interface: wan
target: prerouting

that way this gets prioritized above wireguard, and then it routes the specific clients to the wireguard route with the second rule.

I'm not sure about this one either, but from what I got the wgzone is working a wan zone (not sure at all), but the problem is not the connection itself, since it's already working, but the leaking dns, unless moving wgzone under wan can be beneficial to this, I don't see why.

Since the wan interface is the default gateway, ins't this basically the default behavior? Anything that doesn't go through one of the policies goes to the default gateway, right?