peacey / split-vpn

A split tunnel VPN script for Unifi OS routers (UDM, UXG, UDR) with policy based routing.
GNU General Public License v3.0
802 stars 56 forks source link

Specify a different DNS server for a specific interface #163

Closed GrassfedMeatSticks closed 1 year ago

GrassfedMeatSticks commented 1 year ago

Hi, Great project. It works well.

I've come across a use-case that I'm not quite sure how to tackle.

I have several interfaces going over this connection using the DNS server I specified in the DNS_IPV4_INTERFACE property of the vpn.conf file, however, I want one of these interfaces to use a different DNS server. How difficult would that be to achieve?

GrassfedMeatSticks commented 1 year ago

I figured out a way around it on my DNS server, but I think it's still an interesting question to consider.

peacey commented 1 year ago

Hi @GrassfedMeatSticks,

I think there's a misunderstanding. The DNS_IPV4_INTERFACE option is the interface the DNS is on if you're using a local DNS server with DNS_IPV4_IP. It isn't the interface that you force to use the DNS. These DNS options forces all the clients you defined in FORCEDSOURCE* to use this DNS.

Do you want to force different clients to different DNS servers?

GrassfedMeatSticks commented 1 year ago

@peacey , I should have clarified. I do run a local DNS server and I want most of my VPN clients to use it. I just wanted to exclude one of the client from the local DNS server.

peacey commented 1 year ago

Hi @GrassfedMeatSticks,

Oh I see. In that case, there is no option as you said, but you can use a post_up snippet and custom iptables rules. To exclude a single client or subnet, you just need to add something like this to the end of your vpn.conf (assuming client IP you want to bypass DNS is 192.168.2.3/32 for example):

hooks_up() {
    for proto in udp tcp; do
        iptables -t nat -I ${PREFIX}PREROUTING 1 -s 192.168.2.3/32 -p $proto --dport 53 -m mark --mark ${MARK} -j ACCEPT
    done
}

That will make that client bypass the DNS NAT rules, so the client will use whatever DNS is assigned to it in its network settings instead of being forced. You can also force this client to use a different DNS server if you want, but it would require a bit more complicated rules (which I can help you with if you need to do that). You can also exempt by interface (-i brX) or subnet (-s 192.168.1.0/24), instead of client IP. You'll also need to add ip6tables rules if you are forcing/exempting IPv6 DNS.

GrassfedMeatSticks commented 1 year ago

This is fantastic! Thank you!

GrassfedMeatSticks commented 1 year ago

Just getting around to implementing this.

If I had two VLANs I'd like to bypass DNS NAT rules, would I do this?

hooks_up() {
    for proto in udp tcp; do
        iptables -t nat -I ${PREFIX}PREROUTING 1 -i br111 -p $proto --dport 53 -m mark --mark ${MARK} -j ACCEPT
        iptables -t nat -I ${PREFIX}PREROUTING 1 -i br112 -p $proto --dport 53 -m mark --mark ${MARK} -j ACCEPT
    done
}
peacey commented 1 year ago

Hey @GrassfedMeatSticks,

That's correct. That should work.

GrassfedMeatSticks commented 1 year ago

Thanks @peacey, So I ended up only needing to exclude one VLAN after all, however, https://dnsleaktest.com reflects the DNS provider associated with my VPN and not the DNS provider my device received from the DHCP server. I tried both the -i <interface> and the -s <CIDR subnet> and rebooted.

To be clear, I put this at the end of my vpn.conf file.

peacey commented 1 year ago

Hmm, it works for me when I tested it. What DNS do you currently have set up on that excluded VLAN in the Unifi settings and on your client that you're testing from (DHCP or manual)? The thing is some VPN providers route all DNS requests through their DNS servers (they have a DNAT rule on the VPN side). So if you are using a public DNS as your DNS on one of these excluded clients, the DNS will be re-routed to the VPN's DNS on the VPN side and you will see that in the dnsleaktest. But if you are using the UDM router itself as the DNS or a local DNS IP, then the DNS requests will stay local to your network and won't go through the tunnel so won't be re-routed.

GrassfedMeatSticks commented 1 year ago

So, I set a CloudFlare DNS server (1.1.1.1) for the excluded VLAN in the UniFi settings.

The client indicates that the DNS server it should be using is 1.1.1.1, however, the DNSleaktest reflects the same Quad9 DNS server my local DNS server uses (as configured in the vpn.conf's DNS_IPV4_INTERFACE and DNS_IPV4_IP properties).

I have a dirtynet VLAN that is altogether exempt from both the local DNS and the WireGuard VPN and it works as expected. It's just this in-between VLAN that I'm having trouble with. I want it to have VPN access but avoid the local DNS server the other VPN'd VLANs use.

I appreciate all of the time and effort you invest here.

peacey commented 1 year ago

So like I said, I just tested the above hook and it works to exempt a particular forced client from using the DNS set in DNS_IPV4_IP. So you shouldn't be seeing the DNS forced with that hook.

Can you please show me your vpn.conf, and the output of iptables -t nat -S | grep 53 after VPN is enabled?

Also with the VPN and the custom hook enabled, can you run the following tcpdump on your UDM:

tcpdump -ni any port 53 | grep cnn.com

Then from the forced client you are exempting from the DNS with the hook, please do a DNS lookup on cnn.com, for example with dig: dig @1.0.0.1 google.com A

This way we can see where the DNS packets are travelling from this client and whether they're being re-routed or not.

GrassfedMeatSticks commented 1 year ago

@peacey

vpn.conf (removed some of the comments to help with readability, but left all properties)

# Force these sources through the VPN.
FORCED_SOURCE_INTERFACE="br0 br1 br2 br3 br53 l2tp0"
FORCED_SOURCE_IPV4=""
FORCED_SOURCE_IPV6=""
FORCED_SOURCE_MAC=""

FORCED_SOURCE_IPV4_PORT=""
FORCED_SOURCE_IPV6_PORT=""
FORCED_SOURCE_MAC_PORT=""

# Force these destinations through the VPN. 
FORCED_DESTINATIONS_IPV4=""
FORCED_DESTINATIONS_IPV6=""

# Force local UDM traffic going out of these WAN interfaces to go through the
# VPN instead for both IPv4 and IPv6 traffic.
FORCED_LOCAL_INTERFACE=""

# Exempt these sources from the VPN. 
EXEMPT_SOURCE_IPV4=""
EXEMPT_SOURCE_IPV6=""
EXEMPT_SOURCE_MAC=""

EXEMPT_SOURCE_IPV4_PORT=""
EXEMPT_SOURCE_IPV6_PORT=""
EXEMPT_SOURCE_MAC_PORT=""

# Exempt these destinations from the VPN. 
EXEMPT_DESTINATIONS_IPV4=""
EXEMPT_DESTINATIONS_IPV6=""

# Force/exempt these IP sets
FORCED_IPSETS=""
EXEMPT_IPSETS="UBIOS_NETv4_br0:dst UBIOS_NETv4_br1:dst UBIOS_NETv4_br2:dst UBIOS_NETv4_br3:dst UBIOS_NETv4_br53:dst"

# VPN port forwards.
PORT_FORWARDS_IPV4=""
PORT_FORWARDS_IPV6=""

# Redirect IPv4 and IPv6 DNS to these addresses for VPN-destined traffic.
DNS_IPV4_IP="192.168.53.53"
DNS_IPV4_PORT=53

# Set this to the interface (brX) the DNS is on if it is a local IP. Leave blank for
# non-local IPs. Local DNS redirects will not work without specifying the interface.
DNS_IPV4_INTERFACE="br53.mac"

DNS_IPV6_IP=""
DNS_IPV6_PORT=53

# Bypass masquerade (SNAT) for these source IPs. This option should only be used if your 
# VPN server is setup to know how to route the subnet you do not want to masquerade 
BYPASS_MASQUERADE_IPV4=""
BYPASS_MASQUERADE_IPV6=""

# Enabling kill switch drops VPN-destined traffic that doesn't go through the VPN.
KILLSWITCH=1

# Enable this only if you are testing or you don't care about your real IP leaking
# when the vpn client restarts or exits.
REMOVE_KILLSWITCH_ON_EXIT=0

# Enable this if you added blackhole routes in the Unifi Settings to prevent Internet
# access at system startup before the VPN script runs. This option removes the blackhole 
# routes to restore Internet access after the killswitch has been enabled. 
REMOVE_STARTUP_BLACKHOLES=1

# Set the VPN provider.
VPN_PROVIDER="external"

VPN_ENDPOINT_IPV4="<REDACTED>"
VPN_ENDPOINT_IPV6="<REDACTED>"

# Set to "disabled" if you are using the nexthop option to connect to a VPN on your LAN.
GATEWAY_TABLE="auto"

# Set the MSS clamping on packets going out the VPN tunnel. Usually, it is not needed to
# set this manually, but some VPN connections stall if the MSS clamping is not set correctly.
MSS_CLAMPING_IPV4=""
MSS_CLAMPING_IPV6=""

# Set this to the timer to use for the rule watcher (in seconds).
WATCHER_TIMER=1

# Options for custom table and chains.
ROUTE_TABLE=101
MARK=0x171
PREFIX="VPN1_"
PREF=32601
DEV=wg0

hooks_up() {
    for proto in udp tcp; do
        iptables -t nat -I ${PREFIX}PREROUTING 1 -s 192.168.3.0/24 -p $proto --dport 53 -j ACCEPT
    done
}

Output of iptables -t nat -S | grep 53

# iptables -t nat -S | grep 53
-A PREROUTING ! -s 192.168.53.53/32 ! -d 192.168.53.53/32 -i br0 -p udp -m udp --dport 53 -j LOG --log-prefix "[DNAT-br0-udp]"
-A PREROUTING ! -s 192.168.53.53/32 ! -d 192.168.53.53/32 -i br0 -p udp -m udp --dport 53 -j DNAT --to-destination 192.168.53.53
-A PREROUTING ! -s 192.168.53.53/32 ! -d 192.168.53.53/32 -i br0 -p tcp -m tcp --dport 53 -j LOG --log-prefix "[DNAT-br0-tcp]"
-A PREROUTING ! -s 192.168.53.53/32 ! -d 192.168.53.53/32 -i br0 -p tcp -m tcp --dport 53 -j DNAT --to-destination 192.168.53.53
-A PREROUTING ! -s 192.168.53.53/32 ! -d 192.168.53.53/32 -i br1 -p udp -m udp --dport 53 -j LOG --log-prefix "[DNAT-br1-udp]"
-A PREROUTING ! -s 192.168.53.53/32 ! -d 192.168.53.53/32 -i br1 -p udp -m udp --dport 53 -j DNAT --to-destination 192.168.53.53
-A PREROUTING ! -s 192.168.53.53/32 ! -d 192.168.53.53/32 -i br1 -p tcp -m tcp --dport 53 -j LOG --log-prefix "[DNAT-br1-tcp]"
-A PREROUTING ! -s 192.168.53.53/32 ! -d 192.168.53.53/32 -i br1 -p tcp -m tcp --dport 53 -j DNAT --to-destination 192.168.53.53
-A PREROUTING ! -s 192.168.53.53/32 ! -d 192.168.53.53/32 -i br2 -p udp -m udp --dport 53 -j LOG --log-prefix "[DNAT-br2-udp]"
-A PREROUTING ! -s 192.168.53.53/32 ! -d 192.168.53.53/32 -i br2 -p udp -m udp --dport 53 -j DNAT --to-destination 192.168.53.53
-A PREROUTING ! -s 192.168.53.53/32 ! -d 192.168.53.53/32 -i br2 -p tcp -m tcp --dport 53 -j LOG --log-prefix "[DNAT-br2-tcp]"
-A PREROUTING ! -s 192.168.53.53/32 ! -d 192.168.53.53/32 -i br2 -p tcp -m tcp --dport 53 -j DNAT --to-destination 192.168.53.53
-A VPN1_PREROUTING ! -s 192.168.53.53/32 ! -d 192.168.53.53/32 -p udp -m mark --mark 0x171 -m udp --dport 53 -j DNAT --to-destination 192.168.53.53:53
-A VPN1_PREROUTING ! -s 192.168.53.53/32 ! -d 192.168.53.53/32 -p tcp -m mark --mark 0x171 -m tcp --dport 53 -j DNAT --to-destination 192.168.53.53:53

Output oftcpdump -ni any port 53 | grep cnn.com while running nslookup cnn.com 1.1.1.1 from a client on the subnet I am trying to get to bypass the VPN DNS

# tcpdump -ni any port 53 | grep cnn.com
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked v1), capture size 262144 bytes
06:05:53.316552 ethertype IPv4, IP 192.168.3.3.55747 > 1.1.1.1.53: 4+ A? cnn.com. (25)
06:05:53.316552 IP 192.168.3.3.55747 > 1.1.1.1.53: 4+ A? cnn.com. (25)
06:05:53.316552 IP 192.168.3.3.55747 > 1.1.1.1.53: 4+ A? cnn.com. (25)
06:05:53.316716 IP 192.168.3.3.55747 > 192.168.53.53.53: 4+ A? cnn.com. (25)
06:05:53.316721 IP 192.168.3.3.55747 > 192.168.53.53.53: 4+ A? cnn.com. (25)
06:05:53.364864 ethertype IPv4, IP 192.168.3.3.55748 > 1.1.1.1.53: 5+ AAAA? cnn.com. (25)
06:05:53.364864 IP 192.168.3.3.55748 > 1.1.1.1.53: 5+ AAAA? cnn.com. (25)
06:05:53.364864 IP 192.168.3.3.55748 > 1.1.1.1.53: 5+ AAAA? cnn.com. (25)
06:05:53.365009 IP 192.168.3.3.55748 > 192.168.53.53.53: 5+ AAAA? cnn.com. (25)
06:05:53.365014 IP 192.168.3.3.55748 > 192.168.53.53.53: 5+ AAAA? cnn.com. (25)
^C154 packets captured
188 packets received by filter
0 packets dropped by kernel
peacey commented 1 year ago

That's odd, the custom hook rule didn't seem like it was added. When did you install split-vpn? Hooks were added back in July 2021, so if you installed it before then, can you reinstall it? Then make sure to bring the VPN down and back up again, and show me the iptables nat output again.

Also, I see you have other DNAT rules not related to split-vpn to force DNS traffic for br0, br1, and br2. The client you're exempting isn't part of these networks, right?

GrassfedMeatSticks commented 1 year ago

Judging by the prefix and IPs, I thought the last two entries from the iptables -t nat -S | grep 53 output were the custom hook rule?

-A VPN1_PREROUTING ! -s 192.168.53.53/32 ! -d 192.168.53.53/32 -p udp -m mark --mark 0x171 -m udp --dport 53 -j DNAT --to-destination 192.168.53.53:53
-A VPN1_PREROUTING ! -s 192.168.53.53/32 ! -d 192.168.53.53/32 -p tcp -m mark --mark 0x171 -m tcp --dport 53 -j DNAT --to-destination 192.168.53.53:53

I installed split-vpn well after July 2021.

My local DNS server does inject some DNAT rules and you are correct, the client subnet I am trying to exempt is also excluded from those DNAT rules.

For reference, my local DNS server is 192.168.53.53 on VLAN/br53 and my subnet/client I am trying to exclude from local DNS is 192.168.3.3/24

peacey commented 1 year ago

No those are not the custom hook rule, which is an ACCEPT rule and not a DNAT rule. Those are just the regular DNAT rules to force port 53 traffic that are added with DNS_IPV4_IP option.

This is really odd, the hook should be running, but it doesn't seem to be running. I added the exact same hook to my config and it runs fine and adds the exemption rule. This is really puzzling, how did you modify vpn.conf? Are you using vim or your OS editor and scp'ing the file back?

Can you try to use this hook instead and show me the output of wg-quick up when you bring up the VPN?

hooks_up() {
    echo "This is a test."
}

"This is a test" should show up in the output of wg-quick up.

Also, after you bring up the VPN, can you just run this iptables command manually so we can see if it bypasses the DNAT for you if it were added with the hook.

iptables -t nat -I VPN1_PREROUTING 1 -s 192.168.3.0/24 -p udp --dport 53 -j ACCEPT
iptables -t nat -I VPN1_PREROUTING 1 -s 192.168.3.0/24 -p tcp --dport 53 -j ACCEPT

See if that bypasses the DNS for now while we figure out the hook issue.

GrassfedMeatSticks commented 1 year ago

I really appreciate you @peacey.

I SSH into my UDMPRO and modify my /vpn.conf file via vi.

I replaced the previous hooks_up in my vpn.conf file with the one you just provided.

Interestingly, when I changed to the directory where vpn.conf is stored to run wg-quick up ./vpn.conf it returned the following messages:

# wg-quick up ./vpn.conf 
[#] ip link add vpn type wireguard
[#] wg setconf vpn /dev/fd/63
Line unrecognized: `FORCED_SOURCE_INTERFACE="br0br1br2br3br53l2tp0"'
Configuration parsing error
[#] ip link delete dev vpn

I even deleted and re-entered the double-quotes on that line to rule out weird copy/pasting issues to confirm.

I tried to use the wg-quick down command to take the interface down to reset my attempts here

# wg-quick down ./vpn.conf 
wg-quick: `vpn' is not a WireGuard interface

I then ran the wg-quick commands against the wg0.conf file that the run-vpn.sh script calls.

# wg-quick up ./wg0.conf 
wg-quick: `wg0' already exists

I then tried to turn wg0 off and on again

# wg-quick down ./wg0.conf 
[#] ip link delete dev wg0

# wg-quick up ./wg0.conf 
[#] ip link add wg0 type wireguard
[#] wg setconf wg0 /dev/fd/63
[#] ip -4 address add 172.16.0.2/32 dev wg0
[#] ip -6 address add 2606:4700:110:8d68:4e6b:8dc8:b72a:a06f/128 dev wg0
[#] ip link set mtu 1280 up dev wg0
[#] ip -6 route add ::/1 dev wg0 table 101
[#] ip -6 route add 8000::/1 dev wg0 table 101
[#] ip -4 route add 128.0.0.0/1 dev wg0 table 101
[#] ip -4 route add 0.0.0.0/1 dev wg0 table 101

Did not see the hook message.

For reference, here's my run-vpn.sh script that calls both the vpn.conf and wg0.conf files:

#!/bin/sh

# Set up the WireGuard kernel module and tools
/mnt/data/wireguard/setup_wireguard.sh

# Load configuration and run wireguard
cd /etc/split-vpn/wireguard/cf_warp
. ./vpn.conf

/etc/split-vpn/vpn/updown.sh ${DEV} pre-up >pre-up.log 2>&1
wg-quick up ./${DEV}.conf >wireguard.log 2>&1
cat wireguard.log
peacey commented 1 year ago

Sorry, you have to run the wg-quick command on wg0.conf not vpn.conf.

Can I see your wg0.conf? Did you add the PostUp/PreDown to wg0.conf? Because I don't see split-vpn even started there. There's supposed to be an entry at the bottom of wg-quick up saying split-vpn started.

GrassfedMeatSticks commented 1 year ago

No worries. I figured that out. I think :)

I added the hook below to the ./vpn.conf

hooks_up() {
    echo "This is a test."
}

When I added it to ./wg0.conf and bounced the connection the output was

# wg-quick down ./wg0.conf 
[#] ip link delete dev wg0

# wg-quick up ./wg0.conf 
[#] ip link add wg0 type wireguard
[#] wg setconf wg0 /dev/fd/63
Line unrecognized: `hooks_up(){'
Configuration parsing error
[#] ip link delete dev wg0

Here's a redacted version of my ./wg0.conf file:

[Interface]
PrivateKey = <private key>
Address = 172.16.0.2/32
Address = 2606:4700:110:8d68:4e6b:8dc8:b72a:a06f/128
#DNS = 1.1.1.1 - commented out so the DNS settings in vpn.conf would be observed
MTU = 1280
Table = 101
[Peer]
PublicKey = <public key>
#AllowedIPs = 0.0.0.0/0
#AllowedIPs = ::/0
AllowedIPs = 0.0.0.0/1
AllowedIPs = 128.0.0.0/1
AllowedIPs = ::/1
AllowedIPs = 8000::/1

Endpoint = <fqdn:port>
peacey commented 1 year ago

That hook goes in the vpn.conf, not the wg0.conf. But wg-quick up is done on wg0.conf. Sorry for the confusion.

In any case, it seems you didn't add the PostUp/PreDown lines to your wg0.conf (as shown in Step 2 of the wireguard steps). This means split-vpn was not even starting when the tunnel was up.

Please add these lines to your wg0.conf under the Interface section,

PostUp = sh /etc/split-vpn/vpn/updown.sh %i up
PreDown = sh /etc/split-vpn/vpn/updown.sh %i down

Then bring the wg0 tunnel down and back up again and check the output to see if split-vpn started.

Also, you can put the original iptables hook back into vpn.conf.

GrassfedMeatSticks commented 1 year ago

Yup. That was it. How embarrassing.

Somehow I forgot to add those lines to my wg0.conf file.

I re-added the hooks_up() function to the end of my ./vpn.conf file

hooks_up() {
    for proto in udp tcp; do
        iptables -t nat -I ${PREFIX}PREROUTING 1 -i br3 -p $proto --dport 53 -m mark --mark ${MARK} -j ACCEPT
    done
}

And bounced my wg0 interface

# wg-quick down ./wg0.conf 
[#] sh /etc/split-vpn/vpn/updown.sh wg0 down
[Wed Mar  1 19:30:55 CST 2023] split-vpn: wg0 down: Loading configuration from /mnt/data/split-vpn/wireguard/cf_warp/vpn.conf.
[#] ip link delete dev wg0

# wg-quick up ./wg0.conf 
[#] ip link add wg0 type wireguard
[#] wg setconf wg0 /dev/fd/63
[#] ip -4 address add 172.16.0.2/32 dev wg0
[#] ip -6 address add 2606:4700:110:8d68:4e6b:8dc8:b72a:a06f/128 dev wg0
[#] ip link set mtu 1280 up dev wg0
[#] ip -6 route add ::/1 dev wg0 table 101
[#] ip -6 route add 8000::/1 dev wg0 table 101
[#] ip -4 route add 128.0.0.0/1 dev wg0 table 101
[#] ip -4 route add 0.0.0.0/1 dev wg0 table 101
[#] sh /etc/split-vpn/vpn/updown.sh wg0 up
[Wed Mar  1 19:31:00 CST 2023] split-vpn: wg0 up: Loading configuration from /mnt/data/split-vpn/wireguard/cf_warp/vpn.conf.
[Wed Mar  1 19:31:00 CST 2023] split-vpn: Using IPv4 gateway from table 201: via <public IP> dev ethX.

I also checked the iptables via iptables -t nat -S | grep 53

# iptables -t nat -S | grep 53
-A PREROUTING ! -s 192.168.53.53/32 ! -d 192.168.53.53/32 -i br0 -p udp -m udp --dport 53 -j LOG --log-prefix "[DNAT-br0-udp]"
-A PREROUTING ! -s 192.168.53.53/32 ! -d 192.168.53.53/32 -i br0 -p udp -m udp --dport 53 -j DNAT --to-destination 192.168.53.53
-A PREROUTING ! -s 192.168.53.53/32 ! -d 192.168.53.53/32 -i br0 -p tcp -m tcp --dport 53 -j LOG --log-prefix "[DNAT-br0-tcp]"
-A PREROUTING ! -s 192.168.53.53/32 ! -d 192.168.53.53/32 -i br0 -p tcp -m tcp --dport 53 -j DNAT --to-destination 192.168.53.53
-A PREROUTING ! -s 192.168.53.53/32 ! -d 192.168.53.53/32 -i br1 -p udp -m udp --dport 53 -j LOG --log-prefix "[DNAT-br1-udp]"
-A PREROUTING ! -s 192.168.53.53/32 ! -d 192.168.53.53/32 -i br1 -p udp -m udp --dport 53 -j DNAT --to-destination 192.168.53.53
-A PREROUTING ! -s 192.168.53.53/32 ! -d 192.168.53.53/32 -i br1 -p tcp -m tcp --dport 53 -j LOG --log-prefix "[DNAT-br1-tcp]"
-A PREROUTING ! -s 192.168.53.53/32 ! -d 192.168.53.53/32 -i br1 -p tcp -m tcp --dport 53 -j DNAT --to-destination 192.168.53.53
-A PREROUTING ! -s 192.168.53.53/32 ! -d 192.168.53.53/32 -i br2 -p udp -m udp --dport 53 -j LOG --log-prefix "[DNAT-br2-udp]"
-A PREROUTING ! -s 192.168.53.53/32 ! -d 192.168.53.53/32 -i br2 -p udp -m udp --dport 53 -j DNAT --to-destination 192.168.53.53
-A PREROUTING ! -s 192.168.53.53/32 ! -d 192.168.53.53/32 -i br2 -p tcp -m tcp --dport 53 -j LOG --log-prefix "[DNAT-br2-tcp]"
-A PREROUTING ! -s 192.168.53.53/32 ! -d 192.168.53.53/32 -i br2 -p tcp -m tcp --dport 53 -j DNAT --to-destination 192.168.53.53
-A VPN1_PREROUTING -i br3 -p tcp -m tcp --dport 53 -j ACCEPT
-A VPN1_PREROUTING -i br3 -p udp -m udp --dport 53 -j ACCEPT
-A VPN1_PREROUTING ! -s 192.168.53.53/32 ! -d 192.168.53.53/32 -p udp -m mark --mark 0x171 -m udp --dport 53 -j DNAT --to-destination 192.168.53.53:53
-A VPN1_PREROUTING ! -s 192.168.53.53/32 ! -d 192.168.53.53/32 -p tcp -m mark --mark 0x171 -m tcp --dport 53 -j DNAT --to-destination 192.168.53.53:53

Lastly, I captured the output of tcpdump -ni any port 53 | grep cnn.com while running nslookup cnn.com 1.1.1.1 from a client on the subnet I am trying to get to bypass the VPN DNS.

# tcpdump -ni any port 53 | grep cnn.com
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked v1), capture size 262144 bytes
21:37:57.730180 ethertype IPv4, IP 192.168.3.3.51961 > 1.1.1.1.53: 4+ A? cnn.com. (25)
21:37:57.730180 IP 192.168.3.3.51961 > 1.1.1.1.53: 4+ A? cnn.com. (25)
21:37:57.730180 IP 192.168.3.3.51961 > 1.1.1.1.53: 4+ A? cnn.com. (25)
21:37:57.730323 IP 172.16.0.2.51961 > 1.1.1.1.53: 4+ A? cnn.com. (25)
21:37:57.768583 ethertype IPv4, IP 192.168.3.3.51962 > 1.1.1.1.53: 5+ AAAA? cnn.com. (25)
21:37:57.768583 IP 192.168.3.3.51962 > 1.1.1.1.53: 5+ AAAA? cnn.com. (25)
21:37:57.768583 IP 192.168.3.3.51962 > 1.1.1.1.53: 5+ AAAA? cnn.com. (25)
21:37:57.768729 IP 172.16.0.2.51962 > 1.1.1.1.53: 5+ AAAA? cnn.com. (25)
992 packets captured
1152 packets received by filter
0 packets dropped by kernel

Thank you very much @peacey!!

peacey commented 1 year ago

@GrassfedMeatSticks, very glad to see the problem is fixed! I'm happy it wasn't anything more complicated than that!

Feel free to re-open if you have any questions or bugs. Have a nice day!