unifios-utilities icon indicating copy to clipboard operation
unifios-utilities copied to clipboard

Force all DNS queries to external Adguard

Open joeblack2k opened this issue 2 years ago • 27 comments

Hello is it possible to reroute DNS queries to an external AdGuard I already have AdGuard set up on my Synology system so I don’t need it on the UDM is there a way to have a script on boot for that? For example my adguard is on 10.0.1.148

ive tried to look at the adguard script but I couldn’t find an entry to copy paste into a script 😅

many thanks!

joeblack2k avatar Sep 26 '21 07:09 joeblack2k

@joeblack2k

Did you find a way to do it?

Tntdruid avatar Oct 03 '21 17:10 Tntdruid

Hello is it possible to reroute DNS queries to an external AdGuard I already have AdGuard set up on my Synology system so I don’t need it on the UDM is there a way to have a script on boot for that? For example my adguard is on 10.0.1.148

ive tried to look at the adguard script but I couldn’t find an entry to copy paste into a script 😅

many thanks!

You can't point the clients to the external AdGuard as the DNS server?

lensherm avatar Oct 06 '21 05:10 lensherm

@joeblack2k

Did you find a way to do it?

no unfortunately not..

joeblack2k avatar Oct 09 '21 18:10 joeblack2k

Hello is it possible to reroute DNS queries to an external AdGuard I already have AdGuard set up on my Synology system so I don’t need it on the UDM is there a way to have a script on boot for that? For example my adguard is on 10.0.1.148 ive tried to look at the adguard script but I couldn’t find an entry to copy paste into a script 😅 many thanks!

You can't point the clients to the external AdGuard as the DNS server?

there are multiple devices like the google chromecast that forces an dns that you can't change so its better if the UDM can capture port 53 and forward all requests to the external Adguard dns ip adress

joeblack2k avatar Oct 09 '21 18:10 joeblack2k

@joeblack2k Add this to your boot scripts, example 99iptables.sh Edit the IPV4 and IPV6 address to the DNS server you want to send captured DNS to. I use this for the exact same reason you are trying to use it for, capturing devices that use hard coded DNS so they don't rout past my Pihole. It works great.

#!/bin/sh

IPV4_IP="192.168.7.4"
IPV6_IP="2XXXXXXXXXXXXXXXXXXXXX3"
FORCED_INTFC="br0"

for intfc in ${FORCED_INTFC}; do
  if [ -d "/sys/class/net/${intfc}" ]; then
    for proto in udp tcp; do
      prerouting_rule="PREROUTING -i ${intfc} -p ${proto} ! -s ${IPV4_IP} ! -d ${IPV4_IP} --dport 53 -j DNAT --to-destination ${IPV4_IP}"
      iptables -t nat -C ${prerouting_rule} || iptables -t nat -A ${prerouting_rule}
      
      if [ -n "${IPV6_IP}" ]; then
        prerouting_rule="PREROUTING -i ${intfc} -p ${proto} ! -s ${IPV6_IP} ! -d ${IPV6_IP} --dport 53 -j DNAT --to-destination ${IPV6_IP}"
        ip6tables -t nat -C ${prerouting_rule} || ip6tables -t nat -A ${prerouting_rule}
      fi
    done
  fi
done

GhostlyCrowd avatar Dec 06 '21 14:12 GhostlyCrowd

@GhostlyCrowd

Thanks for the script, works great!

Tntdruid avatar Dec 06 '21 16:12 Tntdruid

@GhostlyCrowd

Thanks for the script, works great!

Great, glad to help

GhostlyCrowd avatar Dec 06 '21 16:12 GhostlyCrowd

Modified it a bit with some post-routing so devices that reject dns responses from a ip that does not match the original query ip. Also grabbed the logging snipits from the 10-dns script This resolves issues if your DNS is on the same subnet as your devices.

#!/bin/sh

IPV4_IP="192.168.7.4"
IPV6_IP="2607:fXXXXXXXXXXXXX03"
FORCED_INTFC="br0"

for intfc in ${FORCED_INTFC}; do
  if [ -d "/sys/class/net/${intfc}" ]; then
    for proto in udp tcp; do
      prerouting_rule="PREROUTING -i ${intfc} -p ${proto} ! -s ${IPV4_IP}  ! -d ${IPV4_IP} --dport 53 -j LOG --log-prefix [DNAT-${intfc}-${proto}]"
      iptables -t nat -C ${prerouting_rule} 2>/dev/null || iptables -t nat -A ${prerouting_rule}
      prerouting_rule="PREROUTING -i ${intfc} -p ${proto} ! -s ${IPV4_IP} ! -d ${IPV4_IP} --dport 53 -j DNAT --to ${IPV4_IP}"
      iptables -t nat -C ${prerouting_rule} 2>/dev/null || iptables -t nat -A ${prerouting_rule} 

      # (optional) IPv6 force DNS (TCP/UDP 53) through DNS container
      if [ -n "${IPV6_IP}" ]; then
        prerouting_rule="PREROUTING -i ${intfc} -p ${proto} ! -s ${IPV6_IP}  ! -d ${IPV6_IP} --dport 53 -j LOG --log-prefix [DNAT-${intfc}-${proto}]"
        ip6tables -t nat -C ${prerouting_rule} 2>/dev/null || ip6tables -t nat -A ${prerouting_rule}
        prerouting_rule="PREROUTING -i ${intfc} -p ${proto} ! -s ${IPV6_IP} ! -d ${IPV6_IP} --dport 53 -j DNAT --to ${IPV6_IP}"
        ip6tables -t nat -C ${prerouting_rule} 2>/dev/null || ip6tables -t nat -A ${prerouting_rule}

      #fix "reply from unexpected source: with dig, also fixes devices that reject a dns response from an ip that did not match their query target ip"
	#remove them if they already exist so they dont multiply, because we cannot use -C it throws "No chain/target/match by that name."
      iptables -t nat -D POSTROUTING -d ${IPV4_IP} -p tcp -m tcp --dport 53 -j MASQUERADE -m comment --comment "IPV4 DNS Pi-hole MASQUERADE"
      iptables -t nat -D POSTROUTING -d ${IPV4_IP} -p udp -m udp --dport 53 -j MASQUERADE -m comment --comment "IPV4 DNS Pi-hole MASQUERADE"
      ip6tables -t nat -D POSTROUTING -d ${IPV6_IP} -p tcp -m tcp --dport 53 -j MASQUERADE -m comment --comment "IPV6 DNS Pi-hole MASQUERADE"
      ip6tables -t nat -D POSTROUTING -d ${IPV6_IP} -p udp -m udp --dport 53 -j MASQUERADE -m comment --comment "IPV6 DNS Pi-hole MASQUERADE"	

	#add the postrouting fix sleeping because some of the entries dont make it into post routing for some reason
	sleep30 
      iptables -t nat -A POSTROUTING -d ${IPV4_IP} -p tcp -m tcp --dport 53 -j MASQUERADE -m comment --comment "IPV4 DNS Pi-hole MASQUERADE"
      iptables -t nat -A POSTROUTING -d ${IPV4_IP} -p udp -m udp --dport 53 -j MASQUERADE -m comment --comment "IPV4 DNS Pi-hole MASQUERADE"
      ip6tables -t nat -A POSTROUTING -d ${IPV6_IP} -p tcp -m tcp --dport 53 -j MASQUERADE -m comment --comment "IPV6 DNS Pi-hole MASQUERADE"
      ip6tables -t nat -A POSTROUTING -d ${IPV6_IP} -p udp -m udp --dport 53 -j MASQUERADE -m comment --comment "IPV6 DNS Pi-hole MASQUERADE"	

      fi
    done
  fi
done

GhostlyCrowd avatar Dec 06 '21 18:12 GhostlyCrowd

This should get added to the wiki.

Tntdruid avatar Dec 06 '21 20:12 Tntdruid

This should get added to the wiki.

I'll look at cleaning it up and making it more sane before i ask for it to be added, and also i have to figure out how to do a pr lol

GhostlyCrowd avatar Dec 07 '21 00:12 GhostlyCrowd

@GhostlyCrowd

Looks like new 1.11.0 broke it :(

sh 99iptables.sh

: not foundsh: line 2: : not foundsh: line 6: 99iptables.sh: line 7: syntax error: unexpected word (expecting "do")

Tntdruid avatar Dec 22 '21 16:12 Tntdruid

@GhostlyCrowd

Looks like new 1.11.0 broke it :(

sh 99iptables.sh

: not foundsh: line 2: : not foundsh: line 6: 99iptables.sh: line 7: syntax error: unexpected word (expecting "do")

It hasn't broke it for me, post your script.

# iptables -t nat -L -n -v | grep 53
  125  8571 LOG        udp  --  br0    *      !192.168.8.10        !192.168.8.10         udp dpt:53 LOG flags 0 level 4 prefix "[DNAT-br0-udp]"
    0     0 LOG        tcp  --  br0    *      !192.168.8.10        !192.168.8.10         tcp dpt:53 LOG flags 0 level 4 prefix "[DNAT-br0-tcp]"
    0     0 DNAT       tcp  --  br0    *      !192.168.8.10        !192.168.8.10         tcp dpt:53 to:192.168.8.10
    0     0 LOG        udp  --  br100  *      !192.168.8.10        !192.168.8.10         udp dpt:53 LOG flags 0 level 4 prefix "[DNAT-br100-udp]"
    0     0 DNAT       udp  --  br100  *      !192.168.8.10        !192.168.8.10         udp dpt:53 to:192.168.8.10
    0     0 LOG        tcp  --  br100  *      !192.168.8.10        !192.168.8.10         tcp dpt:53 LOG flags 0 level 4 prefix "[DNAT-br100-tcp]"
    0     0 DNAT       tcp  --  br100  *      !192.168.8.10        !192.168.8.10         tcp dpt:53 to:192.168.8.10
    0     0 LOG        tcp  --  *      *       0.0.0.0/0            192.168.8.10         match-set UBIOS_NETv4_br100 src tcp dpt:853 LOG flags 0 level 4
    0     0 MASQUERADE  tcp  --  *      *       0.0.0.0/0            192.168.8.10         match-set UBIOS_NETv4_br100 src tcp dpt:853 /* 00000000004294967310 */
  153 31025 MINIUPNPD  all  --  eth8   *       0.0.0.0/0            0.0.0.0/0
    0     0 LOG        tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            match-set UBIOS_ADDRv4_eth8 dst tcp dpt:853 LOG flags 0 level 4
    0     0 DNAT       tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            match-set UBIOS_ADDRv4_eth8 dst tcp dpt:853 /* 00000000004294967309 */ to:192.168.8.10:853

GhostlyCrowd avatar Dec 22 '21 16:12 GhostlyCrowd

Works now, had to run the sh 2 times. Silly udm

Tntdruid avatar Dec 22 '21 17:12 Tntdruid

still get error:

#!/bin/sh
IPV4_IP="10.10.1.2"
IPV6_IP="fe80::4d2f:8164:xxxx:xxxx"
FORCED_INTFC="br0"
for intfc in ${FORCED_INTFC}; do
  if [ -d "/sys/class/net/${intfc}" ]; then
    for proto in udp tcp; do
      prerouting_rule="PREROUTING -i ${intfc} -p ${proto} ! -s ${IPV4_IP}  ! -d ${IPV4_IP} --dport 53 -j LOG --log-prefix [DNAT-${intfc}-${proto}]"
      iptables -t nat -C ${prerouting_rule} 2>/dev/null || iptables -t nat -A ${prerouting_rule}
      prerouting_rule="PREROUTING -i ${intfc} -p ${proto} ! -s ${IPV4_IP} ! -d ${IPV4_IP} --dport 53 -j DNAT --to ${IPV4_IP}"
      iptables -t nat -C ${prerouting_rule} 2>/dev/null || iptables -t nat -A ${prerouting_rule} 

      # (optional) IPv6 force DNS (TCP/UDP 53) through DNS container
      if [ -n "${IPV6_IP}" ]; then
        prerouting_rule="PREROUTING -i ${intfc} -p ${proto} ! -s ${IPV6_IP}  ! -d ${IPV6_IP} --dport 53 -j LOG --log-prefix [DNAT-${intfc}-${proto}]"
        ip6tables -t nat -C ${prerouting_rule} 2>/dev/null || ip6tables -t nat -A ${prerouting_rule}
        prerouting_rule="PREROUTING -i ${intfc} -p ${proto} ! -s ${IPV6_IP} ! -d ${IPV6_IP} --dport 53 -j DNAT --to ${IPV6_IP}"
        ip6tables -t nat -C ${prerouting_rule} 2>/dev/null || ip6tables -t nat -A ${prerouting_rule}

      #fix "reply from unexpected source: with dig, also fixes devices that reject a dns response from an ip that did not match their query target ip"
	#remove them if they already exist so they dont multiply, because we cannot use -C it throws "No chain/target/match by that name."
      iptables -t nat -D POSTROUTING -d ${IPV4_IP} -p tcp -m tcp --dport 53 -j MASQUERADE -m comment --comment "IPV4 DNS Pi-hole MASQUERADE"
      iptables -t nat -D POSTROUTING -d ${IPV4_IP} -p udp -m udp --dport 53 -j MASQUERADE -m comment --comment "IPV4 DNS Pi-hole MASQUERADE"
      ip6tables -t nat -D POSTROUTING -d ${IPV6_IP} -p tcp -m tcp --dport 53 -j MASQUERADE -m comment --comment "IPV6 DNS Pi-hole MASQUERADE"
      ip6tables -t nat -D POSTROUTING -d ${IPV6_IP} -p udp -m udp --dport 53 -j MASQUERADE -m comment --comment "IPV6 DNS Pi-hole MASQUERADE"	

	#add the postrouting fix sleeping because some of the entries dont make it into post routing for some reason
	sleep30 
      iptables -t nat -A POSTROUTING -d ${IPV4_IP} -p tcp -m tcp --dport 53 -j MASQUERADE -m comment --comment "IPV4 DNS Pi-hole MASQUERADE"
      iptables -t nat -A POSTROUTING -d ${IPV4_IP} -p udp -m udp --dport 53 -j MASQUERADE -m comment --comment "IPV4 DNS Pi-hole MASQUERADE"
      ip6tables -t nat -A POSTROUTING -d ${IPV6_IP} -p tcp -m tcp --dport 53 -j MASQUERADE -m comment --comment "IPV6 DNS Pi-hole MASQUERADE"
      ip6tables -t nat -A POSTROUTING -d ${IPV6_IP} -p udp -m udp --dport 53 -j MASQUERADE -m comment --comment "IPV6 DNS Pi-hole MASQUERADE"	

      fi
    done
  fi
done

Tntdruid avatar Dec 22 '21 17:12 Tntdruid

Try

#!/bin/sh
IPV4_IP="10.10.1.2"
IPV6_IP="fe80::4d2f:8164:xxxx:xxxx"
FORCED_INTFC="br0"

for intfc in ${FORCED_INTFC}; do
  if [ -d "/sys/class/net/${intfc}" ]; then
    for proto in udp tcp; do
      prerouting_rule="PREROUTING -i ${intfc} -p ${proto} ! -s ${IPV4_IP} ! -d ${IPV4_IP} --dport 53 -j LOG --log-prefix [DNAT-${intfc}-${proto}]"
      iptables -t nat -C ${prerouting_rule} 2>/dev/null || iptables -t nat -A ${prerouting_rule}
      prerouting_rule="PREROUTING -i ${intfc} -p ${proto} ! -s ${IPV4_IP} ! -d ${IPV4_IP} --dport 53 -j DNAT --to ${IPV4_IP}"
      iptables -t nat -C ${prerouting_rule} 2>/dev/null || iptables -t nat -A ${prerouting_rule}

      # (optional) IPv6 force DNS (TCP/UDP 53) through DNS container
      if [ -n "${IPV6_IP}" ]; then
        prerouting_rule="PREROUTING -i ${intfc} -p ${proto} ! -s ${IPV6_IP} ! -d ${IPV6_IP} --dport 53 -j LOG --log-prefix [DNAT-${intfc}-${proto}]"
        ip6tables -t nat -C ${prerouting_rule} 2>/dev/null || ip6tables -t nat -A ${prerouting_rule}
        prerouting_rule="PREROUTING -i ${intfc} -p ${proto} ! -s ${IPV6_IP} ! -d ${IPV6_IP} --dport 53 -j DNAT --to ${IPV6_IP}"
        ip6tables -t nat -C ${prerouting_rule} 2>/dev/null || ip6tables -t nat -A ${prerouting_rule}
      fi
    done
  fi
done

#fix "reply from unexpected source: with dig, also fixes devices that reject a dns response from an ip that did not match their query target ip"
#remove them if they already exist so they don't multiply, because we cannot use -C it throws "No chain/target/match by that name."
      iptables -t nat -D POSTROUTING -d ${IPV4_IP} -p tcp -m tcp --dport 53 -j MASQUERADE -m comment --comment "IPV4 DNS Pi-hole MASQUERADE"
      iptables -t nat -D POSTROUTING -d ${IPV4_IP} -p udp -m udp --dport 53 -j MASQUERADE -m comment --comment "IPV4 DNS Pi-hole MASQUERADE"
      ip6tables -t nat -D POSTROUTING -d ${IPV6_IP} -p tcp -m tcp --dport 53 -j MASQUERADE -m comment --comment "IPV6 DNS Pi-hole MASQUERADE"
      ip6tables -t nat -D POSTROUTING -d ${IPV6_IP} -p udp -m udp --dport 53 -j MASQUERADE -m comment --comment "IPV6 DNS Pi-hole MASQUERADE"	

#add the postrouting sleeping fix because some of the entries don't make it into post routing for some reason
	sleep30 
      iptables -t nat -A POSTROUTING -d ${IPV4_IP} -p tcp -m tcp --dport 53 -j MASQUERADE -m comment --comment "IPV4 DNS Pi-hole MASQUERADE"
      iptables -t nat -A POSTROUTING -d ${IPV4_IP} -p udp -m udp --dport 53 -j MASQUERADE -m comment --comment "IPV4 DNS Pi-hole MASQUERADE"
      ip6tables -t nat -A POSTROUTING -d ${IPV6_IP} -p tcp -m tcp --dport 53 -j MASQUERADE -m comment --comment "IPV6 DNS Pi-hole MASQUERADE"
      ip6tables -t nat -A POSTROUTING -d ${IPV6_IP} -p udp -m udp --dport 53 -j MASQUERADE -m comment --comment "IPV6 DNS Pi-hole MASQUERADE"	

GhostlyCrowd avatar Dec 22 '21 18:12 GhostlyCrowd

# sh /mnt/data/on_boot.d/99iptables.sh
: not foundn_boot.d/99iptables.sh: line 5:
/mnt/data/on_boot.d/99iptables.sh: line 6: syntax error: unexpected word (expecting "do")
#

Tntdruid avatar Dec 22 '21 18:12 Tntdruid

I am confused as to why you are having issues on 1.11.0 and I am not me.

Does interface br0 still exist on your machine?

# ls /sys/class/net/
br0          dummy0       eth1         eth10.100    eth4         eth7         gre0         ifb0         lo           switch0.1
br10         erspan0      eth10        eth2         eth5         eth8         gretap0      ifb1         sit0         switch0.10
br100        eth0         eth10.10     eth3         eth6         eth9         honeypot10   ip_vti0      switch0      switch0.100
```

GhostlyCrowd avatar Dec 22 '21 20:12 GhostlyCrowd

I have changed nothing, only did the upgrade.

# ls /sys/class/net/
br0         dummy0      eth0        eth10       eth2        eth4        eth6        eth8        gre0        ifb0        ip_vti0     sit0        switch0.1
br10        erspan0     eth1        eth10.10    eth3        eth5        eth7        eth9        gretap0     ifb1        lo          switch0     switch0.10
#

Tntdruid avatar Dec 22 '21 20:12 Tntdruid

@Tntdruid - Make sure your end of line sequence is set to UNIX (if editing these scripts within a Windows environment).

gatesry avatar Dec 28 '21 23:12 gatesry

@mazak-ui I use notepad++ in Windows.

Tntdruid avatar Dec 29 '21 08:12 Tntdruid

@Tntdruid solved this already?

#!/bin/sh

set -ex

IPV4_IP="10.10.1.2"
IPV6_IP="fe80::4d2f:8164:xxxx:xxxx"
FORCED_INTFC="br0"

for intfc in ${FORCED_INTFC}; do
  if [ -d "/sys/class/net/${intfc}" ]; then
    for proto in udp tcp; do
#      prerouting_rule="PREROUTING -i ${intfc} -p ${proto} ! -s ${IPV4_IP}  ! -d ${IPV4_IP} --dport 53 -j LOG --log-prefix [DNAT-${intfc}-${proto}]"
#      iptables -t nat -C ${prerouting_rule} 2>/dev/null || iptables -t nat -A ${prerouting_rule}
      prerouting_rule="PREROUTING -i ${intfc} -p ${proto} ! -s ${IPV4_IP} ! -d ${IPV4_IP} --dport 53 -j DNAT --to ${IPV4_IP}"
      iptables -t nat -C ${prerouting_rule} 2>/dev/null || iptables -t nat -A ${prerouting_rule}

      iptables -t nat -D POSTROUTING -d ${IPV4_IP} -p ${proto} -m ${proto} --dport 53 -j MASQUERADE -m comment --comment "IPV4 DNS Pi-hole MASQUERADE" || echo -n ""
      iptables -t nat -A POSTROUTING -d ${IPV4_IP} -p ${proto} -m ${proto} --dport 53 -j MASQUERADE -m comment --comment "IPV4 DNS Pi-hole MASQUERADE"

      # (optional) IPv6 force DNS (TCP/UDP 53) through DNS container
      if [ -n "${IPV6_IP}" ]; then
#        prerouting_rule="PREROUTING -i ${intfc} -p ${proto} ! -s ${IPV6_IP}  ! -d ${IPV6_IP} --dport 53 -j LOG --log-prefix [DNAT-${intfc}-${proto}]"
#        ip6tables -t nat -C ${prerouting_rule} 2>/dev/null || ip6tables -t nat -A ${prerouting_rule}
        prerouting_rule="PREROUTING -i ${intfc} -p ${proto} ! -s ${IPV6_IP} ! -d ${IPV6_IP} --dport 53 -j DNAT --to ${IPV6_IP}"
        ip6tables -t nat -C ${prerouting_rule} 2>/dev/null || ip6tables -t nat -A ${prerouting_rule}

        ip6tables -t nat -D POSTROUTING -d ${IPV6_IP} -p ${proto} -m ${proto} --dport 53 -j MASQUERADE -m comment --comment "IPV6 DNS Pi-hole MASQUERADE" || echo -n ""
        ip6tables -t nat -A POSTROUTING -d ${IPV6_IP} -p ${proto} -m ${proto} --dport 53 -j MASQUERADE -m comment --comment "IPV6 DNS Pi-hole MASQUERADE"

      fi
    done
  fi
done

mabunixda avatar Jan 10 '22 11:01 mabunixda

@mabunixda Yes. Sometime when i run the script it get that error. other time it's works.

Tntdruid avatar Jan 10 '22 11:01 Tntdruid

try dos2unix on the script, if you copied it from a windows machine

CodeAnthem avatar Jan 11 '22 01:01 CodeAnthem

Yes, it’s end of line sequence from Windows to Unix. Visual Studio Code does it automatically (upon setting it correctly first time. Notepad++ requires tweaking.

gatesry avatar Jan 11 '22 01:01 gatesry

Thank you guys for posting these scripts, I was able to get DNS redirection working on my home :)

However, I'm wondering if anyone knows how to change these scripts to support 2 DNS servers? [EDIT] answer bellow

I have 2 Adguard Home DNS servers on a VLAN and I'm wondering how those iptable rules could be tweaked to support both?

This is the script that worked for me for a single DNS server. I copied those lines from 10-dns.sh

#!/bin/sh

set -e
set -x

IPV4_IP="10.10.X.X"
IPV6_IP="2601:XXXX:XXXX:5f80::608"
FORCED_INTFC="br0 br10 br20 br30" # redirect from management LAN, VLAN10, VLAN20 and VLAN30

for intfc in ${FORCED_INTFC}; do
  if [ -d "/sys/class/net/${intfc}" ]; then
    for proto in udp tcp; do
      prerouting_rule="PREROUTING -i ${intfc} -p ${proto} ! -s ${IPV4_IP} ! -d ${IPV4_IP} --dport 53 -j LOG --log-prefix [DNAT-${intfc}-${proto}]"
      iptables -t nat -C ${prerouting_rule} 2>/dev/null || iptables -t nat -A ${prerouting_rule}
      prerouting_rule="PREROUTING -i ${intfc} -p ${proto} ! -s ${IPV4_IP} ! -d ${IPV4_IP} --dport 53 -j DNAT --to ${IPV4_IP}"
      iptables -t nat -C ${prerouting_rule} 2>/dev/null || iptables -t nat -A ${prerouting_rule}

      if [ -n "${IPV6_IP}" ]; then
        prerouting_rule="PREROUTING -i ${intfc} -p ${proto} ! -s ${IPV6_IP} ! -d ${IPV6_IP} --dport 53 -j LOG --log-prefix [DNAT-${intfc}-${proto}]"
        ip6tables -t nat -C ${prerouting_rule} 2>/dev/null || ip6tables -t nat -A ${prerouting_rule}
        prerouting_rule="PREROUTING -i ${intfc} -p ${proto} ! -s ${IPV6_IP} ! -d ${IPV6_IP} --dport 53 -j DNAT --to ${IPV6_IP}"
        ip6tables -t nat -C ${prerouting_rule} 2>/dev/null || ip6tables -t nat -A ${prerouting_rule}
      fi
    done
  fi
done

EDIT:

I found out that there's a way to specify a range of addresses for the NAT rules. In my case, my DNS1 and DNS2 are on consecutive IPv4 addresses, so that works (example: 10.10.1.3 and 10.10.1.4).

I modified the script to work for either a single IPv4 address or a range of IPv4 addresses and that should work for anyone else that wants this. As far as IPv6 goes, that's a more complicated subject. From my understanding is that I'd have to setup a ULA and assign static IPs to both my DNS servers. I don't know how to do that, but I'm happy ipv6 DNS requests redirecting to at least DNS1

Below is the modified script that I've used

#!/bin/sh

set -e # TODO: remove this -- I've used this for debugging
set -x # TODO: remove this -- I've used this for debugging

IPV4_DNS_SRV_IP="10.10.1.3-10.10.1.4" # can be a single address or a range of addresses separated by a dash (no spaces)
IPV6_DNS_SRV_IP="2601:XXXX:XXXX:5f80::608"
FORCED_INTFC="br0 br10 br20 br30" # redirect from management LAN, VLAN10, VLAN20 and VLAN30

case "$IPV4_DNS_SRV_IP" in
  *-*) # if IPV4_DNS_SRV_IP contains a dash, then it is a range of addresses
    src_param="-m iprange ! --src-range"
    dst_param="--dst-range"
    ;;
  *)
    src_param="! -s"
    dst_param="-d"
    ;;
esac

for intfc in ${FORCED_INTFC}; do
  if [ -d "/sys/class/net/${intfc}" ]; then
    for proto in udp tcp; do
      # add rule to log ipv4 DNS packets
      prerouting_rule="PREROUTING -i ${intfc} -p ${proto} ${src_param} ${IPV4_DNS_SRV_IP} ! ${dst_param} ${IPV4_DNS_SRV_IP} --dport 53 -j LOG --log-prefix [DNAT-${intfc}-${proto}]"
      iptables -t nat -C ${prerouting_rule} 2>/dev/null || iptables -t nat -A ${prerouting_rule}
      # add rule to redirect ipv4 DNS packets
      prerouting_rule="PREROUTING -i ${intfc} -p ${proto} ${src_param} ${IPV4_DNS_SRV_IP} ! ${dst_param} ${IPV4_DNS_SRV_IP} --dport 53 -j DNAT --to ${IPV4_DNS_SRV_IP}"
      iptables -t nat -C ${prerouting_rule} 2>/dev/null || iptables -t nat -A ${prerouting_rule}

      if [ -n "${IPV6_DNS_SRV_IP}" ]; then
        # add rule to log ipv6 DNS packets
        prerouting_rule="PREROUTING -i ${intfc} -p ${proto} ! -s ${IPV6_DNS_SRV_IP} ! -d ${IPV6_DNS_SRV_IP} --dport 53 -j LOG --log-prefix [DNAT-${intfc}-${proto}]"
        ip6tables -t nat -C ${prerouting_rule} 2>/dev/null || ip6tables -t nat -A ${prerouting_rule}
        # add rule to redirect ipv6 DNS packets
        prerouting_rule="PREROUTING -i ${intfc} -p ${proto} ! -s ${IPV6_DNS_SRV_IP} ! -d ${IPV6_DNS_SRV_IP} --dport 53 -j DNAT --to ${IPV6_DNS_SRV_IP}"
        ip6tables -t nat -C ${prerouting_rule} 2>/dev/null || ip6tables -t nat -A ${prerouting_rule}
      fi
    done
  fi
done

paulo-erichsen avatar Apr 29 '22 07:04 paulo-erichsen

Hi,

FYI, I'm using this to capture the dns requests to my 2 PiHole's (running in HA, so I only need 1 IP):

#!/bin/sh
 
### Add iptables rules to redirect port 53 traffic (tcp+udp)
### IPv4 DNS traffic is redirected to $IPV4_IP on port $IPV4_PORT
### IPv6 DNS traffic is redirected to $IPV6_IP on port $IPV6_PORT
 
## DNS redirect configuration variables:
IPV4_IP="10.10.100.50"
IPV4_PORT=53
# Leave this blank if you don't use IPv6
IPV6_IP=
IPV6_PORT=53
 
# Set this to the interfaces you want to force through the DNS IP.
# Separate interfaces with spaces.
# e.g. "br0" or "br0 br1" etc.
FORCED_INTFC="br0 br10 br20 br30 br40 br50 br60 br70"
 
# Enable this if your pihole is on the same subnet as the devices/interfaces
# you are forwarding. This will allow the return traffic to work on the same
# subnet, but will make the pihole think the requests are coming from the router.
# You will lose client information in the pihole dashboard if you enable this.
# It is preferable to put the pihole on a different subnet and disable this.
ENABLE_MASQUERADE=0
 
# IPv4 force DNS (TCP/UDP 53) through DNS container
for intfc in ${FORCED_INTFC}; do
  if [ -d "/sys/class/net/${intfc}" ]; then
    for proto in udp tcp; do
      prerouting_rule="PREROUTING -i ${intfc} -p ${proto} ! -s ${IPV4_IP} ! -d ${IPV4_IP} --dport 53 -j DNAT --to ${IPV4_IP}:${IPV4_PORT}"
      iptables -t nat -C ${prerouting_rule} || iptables -t nat -A ${prerouting_rule}
 
      # IPv6 force DNS (TCP/UDP 53) through DNS container
      if [ -n "${IPV6_IP}" ]; then
        prerouting_rule="PREROUTING -i ${intfc} -p ${proto} ! -s ${IPV6_IP} ! -d ${IPV6_IP} --dport 53 -j DNAT --to ${IPV6_IP}:${IPV6_PORT}"
        ip6tables -t nat -C ${prerouting_rule} || ip6tables -t nat -A ${prerouting_rule}
      fi
    done
  fi
done
 
if [ "$ENABLE_MASQUERADE" = "1" ]; then
  for proto in udp tcp; do
    postrouting_rule="POSTROUTING ! -s ${IPV4_IP} -d ${IPV4_IP} -p ${proto} --dport 53 -j MASQUERADE"
    iptables -t nat -C ${postrouting_rule} || iptables -t nat -A ${postrouting_rule}
  done
fi

Kopernikus1979 avatar May 01 '22 16:05 Kopernikus1979

Hey guys! I've tried both scripts — This one looks like it gives me syntax errors. I've tried copy and pasting it from Visual Code Studio and 2 different terminal apps on macOS (the default terminal and iTerm).

With this script I'm getting no errors but also if I change my DNS on my Mac it uses the one I set, say for example '1.1.1.1' where it should my PiHole one '10.0.1.10'.

I have installed boostchicken onboot script but doesn't seem I can't get the DNS redirect working. Is this still a thing in UDM Pro version 1.12.33?

Thanks!

jcastro avatar Dec 05 '22 17:12 jcastro