Pihole is a network wide ad blocker. Using DHCP we can tell every device on your network to automatically and transparently use Pihole for DNS. But what if you have custom DNS entries in your firewall? I use OPNsense but this process largely transposes to PFsense as well.
When your laptop makes a DNS request, it is sent to Pihole. Pihole performs a lookup and if it can't find the requested address, forwards that request on to the next DNS server in the chain. If OPNsense has that DNS record in it's lookup caches or DNS configurations it will return it to the client. If not, it will go out to the upstream DNS provider (cloudflare or google or your ISP) and find it there instead. This process repeats until an authoritative DNS server is found for the requested lookup.
This allows you to use Pihole in conjunction with Unbound and perform network-wide ad-blocking but also retain complete custom local DNS control.
For the purposes of this post the following hostnames and IPs are used.
OPNsense DHCP configuration
First we need to tell every device on our network to use Pihole for DNS.
The next time a device requests an IP via DHCP it will now also receive instructions to use
192.168.1.97 for DNS.
Pihole DNS configuration
Next, we need to tell Pihole where to look when it doesn't know the answer. We want to send these requests to OPNsense, not the internet (yet).
One other thing you might wish to enable is
Without this it will look like all DNS requests came from your firewall and not each individual client.
OPNsense DNS configuration
System -> Settings -> General -> Networking set your public upstream DNS providers. I used Cloudflare
220.127.116.11 and Google
18.104.22.168 but you can use whatever you like.
To test everything works as you'd like, create a DNS entry in Unbound on OPNsense under
Services -> Unbound DNS -> Overrides. In my case I created
blogtest.ktz.lan to point to
dig to verify.
alex@stan ~ % dig blogtest.ktz.lan +short 22.214.171.124 alex@stan ~ % dig blogtest.ktz.lan ; <<>> DiG 9.10.6 <<>> blogtest.ktz.lan ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 22032 ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 4096 ;; QUESTION SECTION: ;blogtest.ktz.lan. IN A ;; ANSWER SECTION: blogtest.ktz.lan. 3596 IN A 126.96.36.199 ;; Query time: 4 msec ;; SERVER: 192.168.1.97#53(192.168.1.97) ;; WHEN: Wed May 20 13:34:20 EDT 2020 ;; MSG SIZE rcvd: 61
Success! Digs output can be a little cryptic but note the
SERVER output is
192.168.1.97, which is our Pihole. However the custom entry is in Unbound on OPNsense so by this logic Pihole must have sent our DNS request on to OPNsense and returned the value we set. Proof it works! Huzzah!