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.
all files, referred in this topic, can be found here. The unbound files are the files before the configuration (see below) changes are applied
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!):
- 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:
see second entry of topic below, changed the commands to create
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
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
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: 18.104.22.168
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
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 22.214.171.124), the script will work, you will use an external resolver.
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.