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.