[Script] Automatically change DNS if Pi-hole is no longer working

Hi,

well I see it working every day since exactly one week ago.

Yes I have other nat rules active but none of them handles DNS traffic.

I split my rule with some explanation, maybe someone can correct me, where I’m wrong (or right).

Do
chain=dstnat (declare outgoing nat)
action=dst-nat (redirect for outgoing NAT)
to-addresses=192.168.0.250 (redirect to my pi)
to-ports=53 (to DNS port)

But only if:
protocol=udp (maybe TCP but mostly DNS is a udp thing)
And 
src-address=!192.168.0.250 (not from pi, so requests from pi could passthrough)
And
dst-address=!192.168.0.250 (no requests already sent to my pi)
And
in-interface=bridge (request is from local network)
And
dst-port=53 (if DNS port is used)

So every matching request is redirected to the pi. I can use ‘ping’ and ‘dig’ commands from my pi (via SSH) as much as I want, everything works as expected.

Maybe I’m wrong, but it’s working.

Have a nice Sunday

Would you mind posting the output of:

/ip firewall export

You can ofcourse remove all port forwarding rules of NAT.

In my case, I only use the default Mikrotik/RouterOS firewall rules:

/ip firewall filter
add action=accept chain=input comment="defconf: accept established,related,untracked" connection-state=established,related,untracked
add action=drop chain=input comment="defconf: drop invalid" connection-state=invalid
add action=accept chain=input comment="defconf: accept ICMP" protocol=icmp
add action=accept chain=input comment="defconf: accept to local loopback (for CAPsMAN)" dst-address=127.0.0.1
add action=drop chain=input comment="defconf: drop all not coming from LAN" in-interface-list=!LAN
add action=accept chain=forward comment="defconf: accept in ipsec policy" ipsec-policy=in,ipsec
add action=accept chain=forward comment="defconf: accept out ipsec policy" ipsec-policy=out,ipsec
add action=fasttrack-connection chain=forward comment="defconf: fasttrack" connection-state=established,related
add action=accept chain=forward comment="defconf: accept established,related, untracked" connection-state=established,related,untracked
add action=drop chain=forward comment="defconf: drop invalid" connection-state=invalid
add action=drop chain=forward comment="defconf: drop all from WAN not DSTNATed" connection-nat-state=!dstnat connection-state=new in-interface-list=WAN
/ip firewall nat
add action=dst-nat chain=dstnat comment=DNS-to-server-rule1 dst-address=!192.168.88.2 dst-port=53 in-interface=bridge protocol=udp src-address=!192.168.88.2 to-addresses=192.168.88.2 to-ports=53
add action=masquerade chain=srcnat comment=DNS-to-server-rule2 dst-address=192.168.88.2 dst-port=53 protocol=udp src-address=192.168.88.0/24
add action=masquerade chain=srcnat out-interface=pppoe-client

NAT only contains the 2 rules to force DNS to pihole. The very last rule is required for internet access as my proivider uses PPOE.

Hi,

Here is my Firewall export, without lists and mangle rules.

/ip firewall filter
add action=drop chain=input comment="Winbox on WAN" dst-port=\
    8291 in-interface=pppoe-out1 log-prefix="winbox on wan " \
    protocol=tcp
add action=drop chain=forward comment=\
    "Drop incoming packets that are not NATted" \
    connection-nat-state=!dstnat connection-state=new \
    in-interface=pppoe-out1 log=yes log-prefix=!NAT
add action=drop chain=forward comment=" Drop  Invalid" \
    connection-state=invalid log-prefix=invalid
add action=drop chain=forward comment=\
    "Drop incoming from internet which is not public IP" \
    in-interface=pppoe-out1 log=yes log-prefix=!public \
    src-address-list=not_in_internet
add action=drop chain=input comment="Drop Wan2Lan DNS (TCP)" \
    dst-port=53 in-interface=pppoe-out1 protocol=tcp
add action=drop chain=input comment="Drop Wan2Lan DNS (UDP)" \
    dst-port=53 in-interface=pppoe-out1 protocol=udp
add action=drop chain=forward comment=\
    "Drop packets from LAN that do not have LAN IP" \
    in-interface=bridge log=yes log-prefix=LAN_!LAN \
    src-address=!192.168.0.0/24
add action=drop chain=forward comment="Firehol list" \
    connection-state=new disabled=yes dst-address-list=firehol
add action=accept chain=input comment=\
    "Allow established, related" \
    connection-state=established,related
add action=accept chain=input comment="Allow internal" \
    src-address-list=allowed_to_router
add action=fasttrack-connection chain=forward comment=\
    "FastTrack" connection-state=\
    established,related
add action=accept chain=forward comment="Established,  Related" \
    connection-state=established,related
add action=accept chain=forward comment=\
    "Forward Portmapping (Plex)" connection-nat-state=dstnat \
    dst-port=32400 in-interface=pppoe-out1 log=yes log-prefix=\
    "Forward Plex" protocol=tcp
add action=accept chain=forward comment="SIP forward" \
    connection-nat-state=dstnat log=yes log-prefix=SIP \
    src-address-list=SIP
add action=accept chain=forward comment="Zyxel Forward Rule" \
    dst-address=192.168.1.1 in-interface=bridge log=yes \
    log-prefix="Forward to Zyxel" out-interface=ether1 \
    src-address-list=allowed_to_router
add action=drop chain=forward comment=\
    " Drop  tries  to  reach  not public  addresses  from  LAN" \
    dst-address-list=not_in_internet in-interface=bridge \
    log-prefix=!publich_from_LAN out-interface=!bridge
add action=drop chain=input comment="Drop everything" \
    in-interface=pppoe-out1 log-prefix=dropped

and the NAT part:

/ip firewall nat
add action=dst-nat chain=dstnat comment=\
    "SIP Connection forward 3478 to FB7590 (UDP)" \
    connection-type=sip dst-port=3478 in-interface=pppoe-out1 \
    log=yes log-prefix="UDP(5060)" protocol=udp \
    src-address-list=SIP to-addresses=192.168.0.254 to-ports=\
    3478
add action=dst-nat chain=dstnat comment=\
    "SIP Connection forward 5060 to FB7590 (UDP)" \
    connection-type=sip dst-port=5060 in-interface=pppoe-out1 \
    log=yes log-prefix="UDP(5060)" protocol=udp \
    src-address-list=SIP to-addresses=192.168.0.254 to-ports=\
    5060
add action=dst-nat chain=dstnat comment=\
    "SIP Connection forward 5060 to FB7590 (TCP)" dst-port=5060 \
    in-interface=pppoe-out1 log=yes log-prefix="TCP(5060)" \
    protocol=tcp src-address-list=SIP to-addresses=\
    192.168.0.254 to-ports=5060
add action=dst-nat chain=dstnat comment=\
    "SIP forward to FB7590 only 1and1-IPs" in-interface=\
    pppoe-out1 log=yes log-prefix="UDP(CALL)" protocol=udp \
    src-address-list=SIP to-addresses=192.168.0.254 to-ports=\
    7077-7110
add action=dst-nat chain=dstnat comment="Plex forward to 32400" \
    dst-port=24179 in-interface=pppoe-out1 log=yes log-prefix=\
    Plex protocol=tcp to-addresses=192.168.0.31 to-ports=32400
add action=dst-nat chain=dstnat comment="PS4 Forward" disabled=\
    yes dst-port=3074 in-interface=pppoe-out1 protocol=udp \
    to-addresses=192.168.0.18 to-ports=3074
add action=dst-nat chain=dstnat comment="Mikrotik forced" \
    disabled=yes dst-address=!192.168.0.1 dst-port=53 \
    in-interface=bridge protocol=udp src-address=!192.168.0.1 \
    to-addresses=192.168.0.1
add action=dst-nat chain=dstnat comment="pihole forced" \
    dst-address=!192.168.0.250 dst-port=53 in-interface=bridge \
    protocol=udp src-address=!192.168.0.250 to-addresses=\
    192.168.0.250 to-ports=53
add action=masquerade chain=srcnat comment=\
    "Outbound to Zyxel for Configuration" dst-address=\
    192.168.1.1 out-interface=ether1
add action=masquerade chain=srcnat comment=\
    "Outgoing NAT for WAN" dst-address-list=!not_in_internet \
    out-interface=pppoe-out1

I hope there’s something that could help, but i don’t see anything related. Maybe there’s some difference in your other dns related configuration (pihole or router)?

Here is mine from the router :

/ip dns
set allow-remote-requests=yes cache-max-ttl=12h servers=1.1.1.1,1.0.0.1

How is your pihole configuration?

Greetings

Hello again,

I did some testing and reconfiguration on my setup, because I was interested, what’s wrong on your site.

Guess what happened? Well it doesn’t work anymore…

Never touch a running system, I guess…

So I think I was wrong all the time. Maybe someone with more networking experience finds a way.

Until then I will use the solution from the thread I’ve posted, because it’s not DHCP TTL dependent.

Have a nice evening.

These are the command that are added:

/ip dhcp-server network

add address=192.168.88.0/24 dns-server=192.168.88.5 gateway=192.168.88.1 netmask=24

add address=192.168.88.5/32 dns-server=192.168.88.1 gateway=192.168.88.1 netmask=24

For fallback I modified the script and need some help, whether its written in right order or wrong. I am confused about this line

 [/ip dhcp-server network address=192.168.88.0/24 comment=defconf get dns-server]

, and the script formatting. This line doesn’t worked for me

:local currentDHCPDNS [/ip dhcp-server network print count-only where dns-server=$piholeDNS

]. How do I get dns server status from dhcp server network?

where dns-server

probably cause issue because I have another entry which has 192.168.88.5 as address in dhcp networks.

:local currentDNS [/ip dhcp-server network address=192.168.88.0/24  comment=defconf get dns-server]
:local piholeDNS "192.168.88.5"
:local testDomain "www.google.com"

:if ($currentDNS = $piholeDNS) do={
    :do {
        :resolve $testDomain server $piholeDNS
    } on-error={
        /ip dhcp-server network set 0 address=192.168.88.0/24 dns-server=$piholeDNS comment=defconf dns-none=yes gateway=192.168.88.1 netmask=24
    }
} else={
    :do {
        :resolve $testDomain server $piholeDNS
        /ip dhcp-server network set 0 address=192.168.88.0/24 dns-server=$piholeDNS comment=defconf gateway=192.168.88.1 netmask=24
    } on-error={}
}

Any help will be very much helpful.

The definitive version, in case anyone is searching for it. Note most scripts above lack a space or two, making them incorrect.

With the below, you will get the following:

  1. ALWAYS force dns requests to go either via your server (AdGuard Home or PiHole or Unbound etc) OR the public fallback DNS servers configured in your router (blocking Amazon devices, Google devices, smartTVs using their own dns server for tracking & ads).

HOW-TO:

  1. In IP > DHCP Server > Networks, set DNS to the same address as the router: 192.168.88.1
  2. In IP > DNS set your preferred fallback DNS servers, for example AdGuard DNS: 94.140.14.14 and 94.140.14.15.
  3. Now go to TERMINAL and type (without arrows) < /ip firewall nat >, hit ENTER then enter the folowing 3 rules (no typos!) and hit enter after each one:

add action=dst-nat chain=dstnat comment=force-own-DNS1 dst-address=!192.168.88.2 dst-port=53 in-interface=bridge protocol=udp src-address=!192.168.88.2 to-addresses=192.168.88.2 to-ports=53


  2. ```text
add action=masquerade chain=srcnat comment=force-own-DNS2 dst-address=192.168.88.2 dst-port=53 protocol=udp src-address=192.168.88.0/24

add chain=dstnat action=dst-nat comment=force-router-DNS to-addresses=192.168.88.1 protocol=udp src-address=!192.168.88.1 dst-address=!192.168.88.1 in-interface=bridge dst-port=53


The first 2 NAT rules are required to force your server DNS, the 3rd NAT rule is required to force the DNS servers that are configured in IP / DNS:

Finally, in System > Scripts, create the following script with name: DNS-fallback:

```text
:local serverIP "192.168.88.2"
:local testDomain "www.google.com"
:do {
     :resolve $testDomain server $serverIP
      /ip firewall nat;
          enable [find comment="force-own-DNS1"];
          enable [find comment="force-own-DNS2"];
          disable [find comment="force-router-DNS"];
    } on-error={
                /ip firewall nat;
                    enable [find comment="force-router-DNS"];
                    disable [find comment="Force-own-DNS1"];
                    disable [find comment="Force-own-DNS2"];
               }
    }

In System > Scheduler: add a new one, use the name of your script (DNS-fallback) and set interval to 10 seconds (to test).
Go to IP > Firewall > NAT, disable the 2 ‘force-own-DNS’ rules, enable the ‘force-router-DNS’ rule, wait max 10 seconds. If your server is up and running, the 2 rules should be enabled and the third rule disabled.

Go back to scheduler and change the interval to 1:00 (every minute, should be enough).


Remaining issue: since we need a masquerade rule, we cannot see clients in AdGuard Home or PiHole. Which means you cannot disable blocking per client or set rules per client :frowning:
To solve this, I understand your server should be in a different subnet (IP > DHCP Server > Networks). I tried creating a seperate subnet, changed the static lease of the server IP to reflect the change, refreshed my server network connection etc. But I didn’t have internet access, couldn’t access the router from the server. Or the clients couldn’t access anything on the server.
If anyone has NOOB instructions how to solve this, please share!

Hi zilexa,

I’ve found another Thread, started by shafiqrahman at http://forum.mikrotik.com/t/mikrotik-pihole-unbound/148772/18 .

It took me about 2 hours today, to get a second pihole running, with this configuration, for testing purpose. i used information from post #5 and #20, except the part with upnp.

I keep it running until the weekend, and if everything works well, i will move my normal setup (pihole with huge filterlists and unbound) into the second subnet. Until now everything seems to works as we want it to, especially that part with clientnames.

If you have any problems i will try to help you ( maybe this time with some success ;))

greetings

@Ddram,
Will you please share the unbound functionality or usage here? To my understanding unbound kinda unnecessary because mikrotik already act as a dns cache. If not I am interested too. Also, please let us know your test result with the new setup. Any update/issue will be highly appreciated. :smiley:

@shafiqrahman,

thanks for your work in the other thread, and all the contributors there. You’ve achived what many people are searching for ( noobs like me of course :slight_smile: ).

One day i found pihole for ad/tracker/phishing/malware blocking and there was a tutorial for using unbound as local dns-resolver and caching the root-dns-zone ( it’s called “hyperlocal” ), so i gave it a try. Basically i liked the idea to query on a direct way and not be dependend from ISP, Google or Quad9 etc. - dns servers. Another point was that unbound does some cache refresh for often requested domains, so there is a little speedup.

Just for clarification, i’m a noob with about 20 years of experience and it’s mostly a thing of “I do it, because i can and maybe i’m learning something new”. My actual plan is to keep pihole/unbound running and have the mikrotik dns as failover.

Until now (about 28hours) everything works very well. The alternativ subnet was the solution (i guess it was mentioned in other posts, but i couldn’t figure out how to do).

Greetz :slight_smile:

http://forum.mikrotik.com/t/how-do-i-connect-two-subnet-in-a-single-router/149158/21
This link might help you. Basically, all ethernet ports are attached to a bridge. You detach your desired ethernet port from that bridge. Make a DHCP server for that port,add ip addresses and define ip pool. Also there is a video on YouTube by Tksja , https://youtu.be/6G6dWF1nipY
Here is the break down of subnet creation:
Remove the desired port from the default bridge(for my case its ether5):

/interface bridge port remove [find interface="ether5"]

This portion is firewall dependent, my firewall requires that it should be in “LAN”

/interface list member add interface=ether5 list=LAN

Now, Give Ip address to that ethernet port.

/ip address 
add address=192.168.188.1/24 comment=pinet interface=ether5 network=192.168.188.0

Assign an IP range for the subnet.

/ip pool
add name=pinet ranges=192.168.188.20-192.168.188.40

Add a dhcp server for that newly created subnet and dhcp server network.

/ip dhcp-server
add address-pool=pinet disabled=no interface=ether5 name=pinetDHCP
/ip dhcp-server network
add address=192.168.188.0/24 comment=pinet gateway=192.168.188.1 netmask=24

If you need more clarification or help, please post on the above mentioned thread, there are other more advance/experienced members who might help :smiley:

Hi,

everything is fine. It’s running since wednesday and i switched my original pihole setup to the second subnet yesterday.

There was a little problem, because you have to run “pihole -r” to reconfigure the pi with the new ip and gateway. The problem is, that something is messed up with this routine. The gateway doesn’t change. I had to open “/etc /dhcpcd.conf” and there where both configurations. I deleted the old one, did a reboot and now everything works as expected.

In the pihole adminpage i had to keep my original subnet and gateway to have nameresolution for my clients.

In short:
Pihole is in 192.168.3.0/24 with 192.168.3.1 as gateway
My clients are in 192.168.0.0/24 and get recognized from the pi

Thanks again :slight_smile:

I use pihole inside a container. But, glad it worked for you.

When you put pithole server on different subnet what changes you make on firewall rules?

Full post http://forum.mikrotik.com/t/mikrotik-pihole-unbound/148772/20

:resolve $testDomain server $piholeDNS

This seems to resolve against the mikrotik cache, therefore introducing a delay in detection of a pihole outage.
How do we skip the cache for this particular request?

Most probably not. Not sure about that, but it worked perfectly for me. Try disabling your wan port and see what happens.

what do you mean

most probably not

?

Actually, when you want to do things like this you should look at the possibilities of “IP anycast routing”.
You can add another IP address to the Pi (it would be possible without that but it would have some disadvantages) and then you can add a route to that address with “ping” check enabled. So the route will disappear as soon as the ping fails.
Then, you also add another route to the same address with a greater distance to the alternative you want to use for the service. In this case it would be a route via the default gateway.
And, you in this case also need to setup a dst-nat to map that address to the DNS service you want to use instead (e.g. 1.1.1.1)

Now, your DNS settings are set to the anycast address on all systems (e.g. via DHCP).
Normally the DNS requests will go to the configured address and are routed to the Pi, but as soon as the Pi does not respond to ping anymore that route will get disabled, the next one takes over, and it will process the traffic.
You can also have multiple servers. E.g. when you have a larger network, where you use BGP or OSPF to route the traffic, you can have multiple places in the network where traffic for that anycast address is routed to a local server. As soon as the server fails, the route disappears, BGP/OSPF will no longer distribute it, and another route to another place in the network where you have this address configured will take over.

Very powerful concept for such failovers… and of course the big players use this all the time, e.g. to provide the service for 1.1.1.1 or 8.8.8.8.

ok thank you this is definitely very informative. I was wondering how 1.1.1.1 and 8.8.8.8 could work with the static IP.
Any chance you can give me a bit more detail to set it up though? I am a beginner with both mikrotik and pihole.
Thanks!

To me, the only thing I needed is to determine whether pihole is up or down. That , what the script does. It only checks whether pihole can resolve or not, then change accordingly. For instances , when the internet is down it also change the route through the router.