I’ve been using OPNsense as my home firewall for almost a year, and so far it has proven to be mostly user-friendly, with only a few gripes that annoy me. I will write more posts about some of the other issues I have later, but this post is reserved for documenting my process to get OPNsense to use Unbound to get DNS-over-TLS working, so that my DNS queries are encrypted. I will assume you, kind reader, already understand why encrypting your DNS lookups is a good thing, and if you don’t, I leave it as an exercise for you to discover.

The OPNsense developers may make a DNS-over-TLS option explicit in the settings at some point in the future, but I wanted it now so I set out to figure out how to do it, and I believe I have a solution. I will document it here mostly for myself, just in case I blow something up in my configuration and need to rebuild it . . .

First things first: if you’re not using Unbound as your resolver in OPNsense, that is, you’re using Dnsmasq, the same process will probably work but you’ll have to dig around for the correct files to edit, and figure out what changes to implement. My process here is strictly for Unbound on OPNsense.

To get started, let’s identify the basic settings in Unbound (Services:Unbound DNS in the OPNsense UI), that you’ll need to set in order for this to all work.

Note: I also have my OPNsense firewall set up to do DNS interception so that all DNS queries not intended for the firewall itself get NATed to the firewall’s LAN IP. More on that in another article.

Note: The changes listed in this post are not persistent across upgrades, so if you upgrade to a new version of OPNsense, you will need to make the changes again. Hopefully the OPNsense developers will make DNS-over-TLS a configurable option in the future.

Services: Unbound DNS: General

Following settings are in the General configuration section of Unbound.

  1. Enable DNS Resolver, checked
  2. Network interfaces: all
  3. Enable DNSSEC support, checked (not absolutely necessary, but OPNsense supports DNSSEC, so use it)
  4. Enable Forwarding Mode, unchecked (this is important)

No other options are mandatory in the General section. The defaults are fine, but you may need to tweak them for your environment.

You must uncheck Enable Forwarding Mode because you’re going to be specifying the remote DNS servers later, and you don’t want Unbound to use any DNS servers that you may have plugged into the System: Settings: General previously.

Services: Unbound DNS: Advanced

Following settings are in the Advanced configuration section of Unbound.

  1. Prefetch support, checked
  2. Prefetch DNS Key Support, checked
  3. Harden DNSSEC data, checked

No other options are mandatory in the Advanced section. The defaults are fine, but you may need to tweak them for your environment.

Other Sections

There are no mandatory settings to be made in the Overrides or Access Lists sections. Just make sure that your local LAN, or any other networks you want to have access to DNS on your OPNsense appliance, are listed in Access Lists.

Now, to the crunchy stuff . . .

Command-line Changes

The following section details what changes to make to actual configuration files in OPNsense. I access it as a non-admin user via SSH, and then use sudo to switch to root or modify files as appropriate. There are several Really Important cautions advised here.

First: Always, always, always make backup copies of any files you intend to change, before you change them. Or take a backup of the configuration. Or take a snapshot if the OPNsense appliance is virtual. Whatever, just make backups.

Second: Don’t do this on a production system where you have legit users attempting to perform their regular duties. You can effectively kill DNS for users if you mess something up (Unbound may refuse to start, etc). If you’re doing this on your local home LAN, then by all means, destroy stuff. See the first cautionary item.

With that out of the way, here is a list of the files we’ll be changing:

  1. /usr/local/etc/inc/plugins.inc.d/unbound.inc

That’s it. This is the only file we need to change. We add and/or make alterations to a few lines, then restart Unbound using the web UI and it should be good to go. When you start or restart Unbound, its configuration is dynamically generated from this file and a few others, but I haven’t had to alter the others to get DNS-over-TLS working.

Edit that file using whichever command line editor you prefer. I’m most comfortable in vi, so that’s what I’m using.

Go to line 350 (it contains only  a variable declaration named forward_conf) and insert these lines after line 350; don’t delete, rename, or alter the forward_conf variable:

rrset-roundrobin: yes
minimal-responses: yes
forward-zone:
forward-ssl-upstream: yes
name: "."
forward-addr: 9.9.9.9@853
forward-addr: 9.9.9.10@853
forward-addr: 1.1.1.1@853
forward-addr: 1.0.0.1@853

Port 853 is the default port that Quad9 and Cloudflare use for DNS-over-TLS. Your ISP may block it, if they don’t want you doing encrypted DNS lookups. If that’s the case you may need to look for a proxy service that will provide encrypted DNS. Otherwise you might be out of luck.

Save and close the file. You did remember to make a backup before you changed it, right?

Switch back to the OPNsense web UI, go to Services:Unbound DNS:General and restart the service by clicking the middle button on the upper right corner of the page. If you didn’t mess things up, this should come back and show a green icon. Now, you can view the dynamically generated configuration file:

less /var/unbound/unbound.conf

Look for the text you added near the bottom of the file (SHIFT+G to jump to the bottom of the file when using less, press ‘q’ when you’re ready to quit viewing the file). You should see the rrset-roundrobin, minimal-responses, forward-ssl-upstream, and forward-addr’s complete with all the DNS servers you specified in System:General Setup. Here’s what you should see:

rrset-roundrobin: yes
minimal-responses: yes
forward-zone:
forward-ssl-upstream: yes
name: "."
forward-addr: 9.9.9.9@853
forward-addr: 9.9.9.10@853
forward-addr: 1.1.1.1@853
forward-addr: 1.0.0.1@853

If Unbound doesn’t start cleanly (check the logs under Services: Unbound DNS: Log File), you’ll need to go back and troubleshoot why. Perhaps you copied and pasted something partially, or misspelled one of the parameters or values.

If Unbound appears to have started cleanly (check the logs under Services: Unbound DNS: Log File), open up another SSH session and run a tcpdump or two to watch traffic to verify communication with the remote DNS servers (9.9.9.9, 9.9.9.10, 1.1.1.1, and 1.0.0.1).

tcpdump -nni em1 '(port 853 or port 53)'

The interface em1 is the WAN interface on my OPNsense firewall. Yours might be different. Do an ifconfig to see which interface has the public IP address assigned. Try opening multiple SSH sessions with a tcpdump running in each one, so you can watch the LAN and WAN interfaces at the same time. You’ll be able to see the plain text DNS queries from your local clients come in on the LAN, and then see the TLS encrypted traffic going out to and coming back from the remote DNS servers.

Once you’ve got your tcpdump running, open up a command prompt in Windows, or a terminal in your favorite Linux or UNIX OS, and do some lookups to random sites. After a minute or so CTRL+C the tcpdump, transfer the packet capture to your local system, and check it in Wireshark. You should see TLS encrypted traffic — if so, your DNS lookups are secure . . . except for that whole SNI thing, of course.

For example, here are some sanitized versions of what I saw in my tests.

Watching the LAN interface:

root@opnsense:~ # tcpdump -nni em0 '(port 853 or port 53)' -s 0
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on em0, link-type EN10MB (Ethernet), capture size 262144 bytes
20:53:40.594255 IP x.x.x.x.57822 > x.x.x.x.53: 1+ PTR? x.x.x.x.in-addr.arpa. (42)
20:53:40.599544 IP x.x.x.x.57823 > x.x.x.x.53: 2+ A? www.espn.com.domain.local. (50)
20:53:40.601954 IP x.x.x.x.57824 > x.x.x.x.53: 3+ AAAA? www.espn.com.domain.local. (50)
20:53:40.604546 IP x.x.x.x.57825 > x.x.x.x.53: 4+ A? www.espn.com. (38)
20:53:40.854494 IP x.x.x.x.57826 > x.x.x.x.53: 5+ AAAA? www.espn.com. (38)

Watching the WAN interface:

root@opnsense:~ # tcpdump -nni em1 '(port 853 or port 53)' -s 0
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on em1, link-type EN10MB (Ethernet), capture size 262144 bytes
20:53:40.615165 IP 1.1.1.1.853 > x.x.x.x.51110: Flags [S.], seq 3974703940, ack 159654831, win 29200, options [mss 1460,nop,nop,sackOK,nop,wscale 10], length 0
20:53:40.631732 IP 1.1.1.1.853 > x.x.x.x.51110: Flags [.], ack 308, win 30, length 0
20:53:40.632904 IP 1.1.1.1.853 > x.x.x.x.51110: Flags [.], seq 1:1461, ack 308, win 30, length 1460
20:53:40.633898 IP 1.1.1.1.853 > x.x.x.x.51110: Flags [P.], seq 1461:2237, ack 308, win 30, length 776
20:53:40.657856 IP 1.1.1.1.853 > x.x.x.x.51110: Flags [P.], seq 2237:2661, ack 434, win 30, length 424
20:53:40.709170 IP 1.1.1.1.853 > x.x.x.x.51110: Flags [.], ack 541, win 30, length 0
20:53:40.732209 IP 1.1.1.1.853 > x.x.x.x.51110: Flags [P.], seq 2661:3160, ack 541, win 30, length 499
20:53:40.737955 IP 1.1.1.1.853 > x.x.x.x.61737: Flags [S.], seq 4608684, ack 2840902953, win 29200, options [mss 1460,nop,nop,sackOK,nop,wscale 10], length 0
20:53:40.749920 IP 1.1.1.1.853 > x.x.x.x.61737: Flags [.], ack 308, win 30, length 0
20:53:40.751478 IP 1.1.1.1.853 > x.x.x.x.61737: Flags [.], seq 1:1461, ack 308, win 30, length 1460
20:53:40.752439 IP 1.1.1.1.853 > x.x.x.x.61737: Flags [P.], seq 1461:2237, ack 308, win 30, length 776
20:53:40.769057 IP 1.1.1.1.853 > x.x.x.x.61737: Flags [P.], seq 2237:2661, ack 434, win 30, length 424
20:53:40.789016 IP 1.1.1.1.853 > x.x.x.x.61737: Flags [P.], seq 2661:3160, ack 541, win 30, length 499
20:53:40.800066 IP 1.0.0.1.853 > x.x.x.x.65212: Flags [S.], seq 1359219767, ack 4263821791, win 29200, options [mss 1460,nop,nop,sackOK,nop,wscale 10], length 0
20:53:40.800344 IP 1.1.1.1.853 > x.x.x.x.61737: Flags [F.], seq 3160, ack 573, win 30, length 0
20:53:40.815892 IP 1.0.0.1.853 > x.x.x.x.65212: Flags [.], ack 308, win 30, length 0
20:53:40.817303 IP 1.0.0.1.853 > x.x.x.x.65212: Flags [.], seq 1:1461, ack 308, win 30, length 1460
20:53:40.818349 IP 1.0.0.1.853 > x.x.x.x.65212: Flags [P.], seq 1461:2237, ack 308, win 30, length 776
20:53:40.834234 IP 1.0.0.1.853 > x.x.x.x.65212: Flags [P.], seq 2237:2661, ack 434, win 30, length 424
20:53:40.846633 IP 1.0.0.1.853 > x.x.x.x.65212: Flags [P.], seq 2661:3160, ack 524, win 30, length 499
20:53:40.857987 IP 1.0.0.1.853 > x.x.x.x.65212: Flags [F.], seq 3160, ack 556, win 30, length 0
20:53:40.872950 IP 1.1.1.1.853 > x.x.x.x.22372: Flags [S.], seq 1568429877, ack 3037278477, win 29200, options [mss 1460,nop,nop,sackOK,nop,wscale 10], length 0
20:53:40.890353 IP 1.1.1.1.853 > x.x.x.x.22372: Flags [.], ack 308, win 30, length 0
20:53:40.891313 IP 1.1.1.1.853 > x.x.x.x.22372: Flags [.], seq 1:1461, ack 308, win 30, length 1460
20:53:40.891596 IP 1.1.1.1.853 > x.x.x.x.22372: Flags [P.], seq 1461:2237, ack 308, win 30, length 776
20:53:40.908334 IP 1.1.1.1.853 > x.x.x.x.22372: Flags [P.], seq 2237:2661, ack 434, win 30, length 424
20:53:40.946853 IP 1.1.1.1.853 > x.x.x.x.22372: Flags [P.], seq 2661:3160, ack 524, win 30, length 499
20:53:40.963792 IP 1.1.1.1.853 > x.x.x.x.22372: Flags [F.], seq 3160, ack 556, win 30, length 0

Excellent. Go forth and conquer.

Update: How do you know DNS-over-TLS is working? Use tcpdump as noted before, but also make sure to access Firewall: Diagnostics: States Dump, and type “:853” in the Filter Expression field, then press the “Filter” button. Do this immediately after testing and it should show you the connections from your OPNsense system to the DNS servers we specified before:

all tcp 1.2.3.4:56043 -> 1.1.1.1:853 FIN_WAIT_2:FIN_WAIT_2

The snippet shows the connection from the OPNsense system, with a fake IP of 1.2.3.4, to the Cloudflare IP 1.1.1.1.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: