Forwarding DNS queries locally using unbound

Table of Contents

Often when I’m working in the office, I want to be able to resolve private DNS for my own infrastructure with my Wireguard VPN, but also resolve private DNS for hosts in the office at the same time. Software like OpenVPN and wg-quick will change the contents of /etc/resolv.conf using a utility like resolvconf, cleaning away any nameserver information you received from DHCP.

I solved this problem by running a forwarding DNS server locally with unbound. By running a local DNS server, you can change the settings in your resolver to use localhost as your nameserver (or just disable your resolver entirely) and have unbound forward requests to different private DNS servers based on domain. There are many articles which configure unbound as a local forwarding DNS server, this one will focus on solving this problem above with minimal effort and a minimal configuration that was good enough for my use case.

Installing unbound

I am configuring unbound here on FreeBSD, which you can have pre-configured as a caching DNS resolver as part of the FreeBSD installation. Here I will be installing it using pkg:

# pkg install unbound

Configuration

Unbound

This is a very minimal unbound configuration, in /usr/local/etc/unbound/unbound.conf (if you are on Linux, you configure unbound in /etc/unbound/unbound.conf):

server:
    interface: 127.0.0.1
forward-zone:
	name: "eyegog.co.uk"
	forward-addr: 172.16.14.254

forward-zone:
	name: "myprivatedomain.com"
	forward-addr: 192.168.1.254

forward-zone:
	name: "."
	forward-addr: 8.8.8.8
    forward-addr: 8.8.4.4
  • server: The server clause configures options such as access control and networking for this unbound instance. The only thing we configure here is the interface to listen on, which is the loopback address.
  • forward-zone: There may be multiple forward-zone clauses in the configuration. Each clause must have a name and one or more hostnames or IP addresses. Each forward-zone clause defines a zone and a list of nameservers to forward queries to that match the full domain name in name.
    • Here, forward-addr is the address of private nameserver we want to send queries for eyegog.co.uk to. I defined another forward-zone for myprivatedomain.com as a similar example.
    • The last forward-zone’s name is . - which will forward all other queries to the nameservers listed in this zone, in this case, Google public DNS servers.

Finally we can enable and start unbound:

# service unbound enable
# service unbound start

See the unbound.conf(5) man page for more specific configuration.

Resolver

Now that we’ve configured unbound, we need to configure our resolver to use 127.0.0.1 as the first nameserver in /etc/resolv.conf. For me this is resolvconf(8) and is configured in /etc/resolvconf.conf… which is rather confusing :)

In /etc/resolvconf.conf:

name_servers="127.0.0.1"

This will prepend 127.0.0.1 to the list of nameservers in /etc/resolv.conf when resolvconf is run.

And update the DNS information:

# resolvconf -u
# cat /etc/resolv.conf
# Generated by resolvconf
nameserver 127.0.0.1

Alternatively, you can also just stop resolvconf from running any subscribers at all and edit resolv.conf yourself:

In /etc/resolvconf.conf:

resolvconf="NO"

Summary

Finally, let’s see if we can resolve DNS for our private domain, and something public:

$ drill eyegog.co.uk
;; ->>HEADER<<- opcode: QUERY, rcode: NOERROR, id: 29929
;; flags: qr rd ra ; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;; eyegog.co.uk.	IN	A

;; ANSWER SECTION:
eyegog.co.uk.	60	IN	A	172.16.10.2

;; AUTHORITY SECTION:

;; ADDITIONAL SECTION:

;; Query time: 0 msec
;; SERVER: 127.0.0.1
;; WHEN: Mon Jun 12 18:01:23 2023
;; MSG SIZE  rcvd: 46


$ drill google.com
;; ->>HEADER<<- opcode: QUERY, rcode: NOERROR, id: 63766
;; flags: qr rd ra ; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;; google.com.	IN	A

;; ANSWER SECTION:
google.com.	34	IN	A	172.217.16.238

;; AUTHORITY SECTION:

;; ADDITIONAL SECTION:

;; Query time: 10 msec
;; SERVER: 127.0.0.1
;; WHEN: Mon Jun 12 18:02:54 2023
;; MSG SIZE  rcvd: 44

Fantastic! The response came from 127.0.0.1 and we were able to forward our queries for eyegog.co.uk to our private nameserver and our queries for google.com to Google’s public nameserver. We can add as many forward-zones for as many private nameservers as we like and be able to resolve them all on our workstations. Now you can watch TV on your media server while you fix your pipelines :D