Unbound as unfiltered DNS resolver + alternative for pihole disable

I've got the idea to investigate this from this topic ([Exclude certain LAN addresses from filtering) The solution, proposed by @deathbybandaid uses external DNS resolvers and pihole DHCP to provide an alternate DNS server to devices.

Many of us are running unbound, so an unfiltered DNS solution is already available, no need to go to an outside source. I'm using the compiled version. My unbound configuration here.

edit
all files, referred in this topic, can be found here. The unbound files are the files before the configuration (see below) changes are applied
/edit

After several hours of duckduckgo / trial and error, here is a solution (this solution will NOT survive pihole -r or pihole -up. changes in /etc/dnsmasq.d/01-pihole.conf will be undone!):

environment:

  • Raspbian Stretch Lite (Minimal image based on Debian Stretch), Version: April 2019, Release date: 2019-04-08,Kernel version: 4.14
  • Unbound version 1.9.1
  • Pihole version 4.3
  • FTL version 4.3.1
  • Primary IP address: 192.168.2.57 (static after the pihole installation)

The first thing to do is add the secondary IP address (example 192.168.2.47). I have several subnets on my network, thus the /27 subnet mask, most users will have a single subnet and should use /24 as the subnet mask:

sudo ip -4 addr add 192.168.2.47/27 dev eth0

We want to ensure the secondary IP address is available after a reboot. Enter the command:
edit
see second entry of topic below, changed the commands to create /etc/dhcpcd.exit-hook
/edit

echo '#add secondary IP address' | sudo tee -a /etc/dhcpcd.exit-hook
echo 'if [ "$reason" = "PREINIT" ]; then' | sudo tee -a /etc/dhcpcd.exit-hook
echo '  ip -4 addr add 192.168.2.47/27 dev eth0' | sudo tee -a /etc/dhcpcd.exit-hook
echo '	exit 0' | sudo tee -a /etc/dhcpcd.exit-hook
echo 'fi' | sudo tee -a /etc/dhcpcd.exit-hook

Next, we have to modify the behavior of pihole-FTL. Most people will have pihole-FTL listening on interface eth0. We have to change that, in order to avoid responses from the secondary IP (192.168.2.47)

# comment out interface=eth0
sudo sed -i 's/interface=/#&/' $file
# make pihole-FTL listen on the primary IP address only (192.168.2.57)
echo 'listen-address=192.168.2.57' | sudo tee -a $file
# enforce listening on the loopback interface (see man dnsmasq (http://www.thekelleys.org.uk/dnsmasq/docs/dnsmasq-man.html))
echo 'listen-address=127.0.0.1' | sudo tee -a $file

and restart pihole-FTL

sudo service pihole-FTL stop
sudo service pihole-FTL start

Next we have to modify the behavior of unbound. we want to make unbound listen on the secondary IP address (192.168.2.47) but unbound can't listen on port 53, so we will use the same port, used in my unbound configuration example (see above - my unbound configuration file is /etc/unbound/unbound.conf.d/unbound.conf):

sudo sed -i '0,/\tinterface:/s//\tinterface: 192.168.2.47@5552\n&/' /etc/unbound/unbound.conf.d/unbound.conf

The result is an unbound configuration looking like this (partial), you don't need to add the IPv6 entries, if you don't want to, this example explains IPv4 redirection only:

    verbosity: 1
	interface: 192.168.2.47@5552
	interface: 127.10.10.2@5552
	interface: fdaa:bbcc:ddee:2::5552@5552
    do-ip4: yes
    do-ip6: yes
    do-udp: yes
    do-tcp: yes

In my unbound configuration, I have a section Access Control. As a result, I need to allow the clients, using unbound as their DNS server.
Again, my network has a number of subnets, the example will allow access to the clients on one subnet only, most users with only one subnet will probably use 192.168.2.0/24, but you can use this to limit access to unfiltered DNS by specifying a single IP address.

sudo sed -i '0,/\taccess-control:/s//\taccess-control: 192.168.2.192\/26 allow\n&/' $file

The result (partial):

	# Access Control
	access-control: 192.168.2.192/26 allow
	access-control: fdaa:bbcc:ddee:2::5552/128 allow

and restart unbound:

sudo service unbound stop
sudo service unbound start

Now he have to enforce redirection, using iptables, this to ensure when a client requests DNS records, using DNS server 192.168.2.47 (secondary IP address), port 53, the request is redirected to port 5552 (unbound).

sudo iptables -t nat -A PREROUTING -d 192.168.2.47 -p tcp --dport 53 -j REDIRECT --to-port 5552
sudo iptables -t nat -A PREROUTING -d 192.168.2.47 -p udp --dport 53 -j REDIRECT --to-port 5552

result (sudo iptables -t nat --line-numbers -L):

Chain PREROUTING (policy ACCEPT)
num  target     prot opt source               destination
1    REDIRECT   tcp  --  anywhere             192.168.2.47         tcp dpt:domain redir ports 5552
2    REDIRECT   udp  --  anywhere             192.168.2.47         udp dpt:domain redir ports 5552

Chain INPUT (policy ACCEPT)
num  target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
num  target     prot opt source               destination

Chain POSTROUTING (policy ACCEPT)
num  target     prot opt source               destination

We need to ensure the redirection configuration survives a reboot:

sudo apt-get -y install iptables-persistent
sudo iptables-save | sudo tee /etc/iptables/rules.v4

and finally reboot the system

sudo reboot

edit
If you've done everything correct, you can use nmapweb to verify the results
scanning 192.168.2.57 should result in:

Nmap scan report for 192.168.2.57
Host is up (0.00067s latency).
Not shown: 996 closed ports
PORT      STATE SERVICE
22/tcp    open  ssh
53/tcp    open  domain
80/tcp    open  http

scanning 192.168.2.47 should result in

Nmap scan report for 192.168.2.47
Host is up (0.0012s latency).
Not shown: 996 closed ports
PORT      STATE SERVICE
22/tcp    open  ssh
53/tcp    open  domain
80/tcp    open  http

Other open ports may be listed as well (depends on what you have running on the pi).
As you can see, both 192.168.2.47 and 192.168.2.57 are listening on port 53 (result of the redirection), despite the changed configuration in /etc/dnsmasq.d/01-pihole.conf.
/edit

result of this configuration (I have ligatus.com configured as a blocked domain in pihole):

nslookup ligatus.com 192.168.2.57
Server:  raspberry57.localdomain
Address:  192.168.2.57

Name:    ligatus.com
Addresses:  ::
          0.0.0.0

nslookup ligatus.com 192.168.2.47
Server:  UnKnown
Address:  192.168.2.47

Non-authoritative answer:
Name:    ligatus.com
Address:  35.189.193.103

On my windows pc, I have a script (cmd) that temporary switches the DNS server settings. When I start my pc, I get a DNS server from DHCP (192.168.2.57 - primary address), the script temporary switches the DNS server to a fixed DNS server (192.168.2.47 - secondary address), allowing me just enough time to hit the refresh button. The script needs to be run as administrator (windows UAC):

@echo off
echo Admin permissions required. Detecting permissions...

NET SESSION >nul 2>&1
IF %ERRORLEVEL% == 0 (
	echo.
    echo Success: Admin permissions confirmed.
    goto admin
) ELSE (
	echo.
	echo ################ ERROR: ADMINISTRATOR PRIVILEGES REQUIRED ################
	echo Failure: Current permissions inadequate.
	echo This script needs to be run as admin. -- Right click - Run as Admin --
	echo ##########################################################################
	echo.
	pause
	goto end
)

:admin
if not "%1" == "min" start /MIN cmd /c %0 min & exit/b >nul 2>&1

netsh interface ipv4 show dnsserver "Ethernet" | %windir%\system32\find.exe "DHCP"
if errorlevel 1 goto setDHCP

setlocal enableextensions enabledelayedexpansion

SET /A INDEX=1
set DNSopen=192.168.2.47
netsh interface ipv4 add dnsserver "Ethernet" address=%DNSopen% index=%INDEX%

endlocal

netsh interface ipv4 show dnsserver "Ethernet"
ipconfig /flushdns

timeout /T 10 /NOBREAK

:setDHCP
netsh interface ipv4 set dnsserver "Ethernet" "dhcp"
netsh interface ipv4 show dnsserver "Ethernet"
ipconfig /flushdns
goto done

:done
exit
:end

edit
You can increase the timeout (unfiltered time) by changing the value in the timeout command.
You need to change the IP address in the script to reflect your own secondary IP address. If you don't (want to) use the entire solution you can change the IP address to a known DNS resolver (example 208.67.222.222), the script will work, you will use an external resolver.
/edit
Somebody will probably be smart enough to create an equivalent for Linux...
Of course, you can always use your DHCP configuration to permanently assign 192.168.47 (secondary IP address) as the DNS server for a specific client.

2 Likes

I found a problem, using /etc/dhcpcd.exit-hook the way I described above:

My /var/log/syslog was flooded with:

dhcpcd[1058]: script_runreason: /lib/dhcpcd/dhcpcd-run-hooks: WEXITSTATUS 2

If you already implemented this solution, chances are high you'll have the same problem!

I modified /etc/dhcpcd.exit-hook, content:

#add secondary IP address
if [ "$reason" = "PREINIT" ]; then
	ip -4 addr add 192.168.2.47/27 dev eth0
	exit 0
fi

This eliminated the syslog entries.

Apologies for the inconvenience...