Setting up pfSense with NextDNS CLI
5 min read

Setting up pfSense with NextDNS CLI

Setting up pfSense with NextDNS CLI

NextDNS is piHole on the cloud and claims to protect networks and devices from security threats, malware, and adverts. I have been using their DNS servers on our home network for a while now, and although that provides basic functionality, their NextDNS CLI provides more advanced features that I was missing out on. Unfortunately, there's no official support on pfSense for it yet, and documentation is sketchy on how to get it up and running.

Hence, I thought of writing a 15-min step-by-step guide on setting up NextDNS CLI with pfSense.

The end goal

Before we get into the how, let's talk about what we want to get out of this. On both physical and virtual interfaces, we want our devices to use the NextDNS DNS53 to DoH translator.


Our current path using the inbuilt unbound DNS resolver looks something like below for both external websites and internal domains. A client, in any subnet, calls out to unbound with a DNS query. Unbound resolves it recursively and responds with an answer, either to an internal server or to an external website. The client then connects to that server, assuming all other routing rules exist.

Current path without NextDNS

From that, we want to serve queries to external websites via the NextDNS DNS53 to DoH translator but keep serving queries to internal domains via unbound.

New path with NextDNS for external domains

The 6-steps

Step 1: SSH into pfSense

SSH access is turned off by default on pfSense. To enable it, go to System -> Advanced -> Enable Secure Shell

Secure Shell configuration under System -> Advanced

Step 2: Log into pfSense using SSH

Use any terminal to log into pfSense using SSH. Use the same username and password you use to login to the pfSense GUI.

Once you login select 8) Shell to access the shell prompt.

ssh [email protected]
([email protected]) Password for [email protected]:
Netgate XG-7100 - Serial: 1943208830 - Netgate Device ID: 1518ea0accfcecea6d60


 0) Logout (SSH only)                  9) pfTop
 1) Assign Interfaces                 10) Filter Logs
 2) Set interface(s) IP address       11) Restart webConfigurator
 3) Reset webConfigurator password    12) PHP shell + Netgate pfSense Plus tools
 4) Reset to factory defaults         13) Update from console
 5) Reboot system                     14) Disable Secure Shell (sshd)
 6) Halt system                       15) Restore recent configuration
 7) Ping host                         16) Restart PHP-FPM
 8) Shell

Enter an option: 8

[21.05.1-RELEASE][[email protected]]/root:
Sequence of steps to log into the pfSense shell prompt

Step 3: Install NextDNS CLI

This wiki describes steps to install the NextDNS CLI. For our purposes, we just need one command.

sh -c 'sh -c "$(curl -sL https://nextdns.io/install)"'

Follow the wizard and enter your config id from the NextDNS setup page.

Step 4: Modify the config

The installation script will create a vanilla configuration. We need to modify it to (1) make it play nice with pfSense and (2) support all our subnets. For more advanced usecases, like supporting conditional configuration (e.g. different configuration for a kids subnet), follow their guide here.

To edit the configuration file, in the pfSense GUI, navigate to Diagonistics -> Edit File and open the configuration file located at /usr/local/etc/nextdns.conf.

Let's start with a template configuration.

control /var/run/nextdns.sock
discovery-dns 127.0.0.1:5555
bogus-priv true
use-hosts true
setup-router false
listen localhost:53
listen 192.168.1.1:53
listen 192.169.1.1:53
config xxxx
cache-max-age 0s
log-queries true
max-ttl 5s
report-client-info true
detect-captive-portals false
timeout 5s
cache-size 10MB
hardened-privacy false
auto-activate true 
forwarder internal.deydas.com=127.0.0.1:5555
NextDNS configuration template

There are a few things we need to modify to make it work:

  1. discovery-dns - unbound, by default, listens on port 53. In the next step, we will change it to something else (5555 in the template). Put in that port here.
  2. listen ip:53 - we need a listen entry for every subnet we want to route through NextDNS. In the template those are 192.168.1.1 and 192.169.1.1. We also need an entry for localhost so pfSense can resolve domains. Don't worry about routing to internal domains just yet, we will get to that later.
  3. config - the config ID from the NextDNS setup page.
  4. forwarder - to forward internal domain resolution to unbound, we need to tell NextDNS to route all queries for domain.com (internal.deydas.com in this case) to unbound.

Feel free to keep the rest same, or modify if necessary.

Step 5: Modify pfSense DNS Resolver settings

Go to Services -> DNS Resolver and change the following settings.

Change the port to something other than 53
Register leases so host names show up

Step 6: Bringing it home

That's about it. Restart unbound and make sure port 53 is released. Then restart NextDNS.

If everything goes well, you should see DNS queries for external websites routed through NextDNS. To verify, go back to the pfSense shell and run

[21.05.1-RELEASE][[email protected]]/root: nextdns log
Feb  5 12:08:00 router nextdns[57391]: Query 193.168.6.131 UDP AAAA 3.north-america.pool.ntp.org. (qry=46/res=101) cached HTTP/2.0
Feb  5 12:08:00 router nextdns[57391]: Query 193.168.6.131 UDP A 3.north-america.pool.ntp.org. (qry=46/res=110) cached HTTP/2.0
Feb  5 12:08:00 router nextdns[57391]: Query 193.168.6.121 UDP A echo-disp.ntp-fireos.com. (qry=42/res=122) cached HTTP/2.0
Feb  5 12:08:00 router nextdns[57391]: Query 193.168.8.231 UDP 65 api.dropbox.com. (qry=33/res=132) cached HTTP/2.0
nextdns log should show incoming queries

The NextDNS logs page should also show queries along with the caller's hostname.

NextDNS logs page

Conclusion

I hope NextDNS comes up with a pfSense package that supports all these natively. But till then, this could be a simple workaround to support more advanced features like hostname logging, conditional configuration, local caching and split horizon.

Till next time, happy hacking!