Setting Up a Recursive DNS Resolver Using Unbound on Docker
Tags: unbound, ubuntu, dns, beginners
Published: 2024-03-20 23:23:10

In this article, I'll walk through the steps to set up a recursive DNS resolver using Unbound on Docker. This resolver will handle DNS queries for a local domain `mich0w0h.house` by forwarding them to an authoritative nameserver while forwarding queries for other domains to external DNS servers like Google DNS.

This post is one part of building a local DNS service using docker-compose and the authoritative nameserver for `mich0w0wh.house` is already built by this article.
- [Setting up a CoreDNS Server for Local Custom Domain Management on Ubuntu Using Docker - DEV Community](https://dev.to/mich0w0h/setting-up-a-coredns-server-for-local-custom-domain-management-on-ubuntu-using-docker-meo)

I'll write the other remaining parts later.

## Prerequisites

- Docker installed and running
- A Docker network named `internal-dns` with subnet `192.168.1.0/24`
- An authoritative nameserver at `192.168.1.102:53` for the `mich0w0h.house` domain

## Directory Structure
Please ensure that you execute all the following commands within the `internal-dns` directory.
```
internal-dns
├── authoritative
│   └── ...
└── resolver
    ├── Dockerfile
    └── unbound.conf
```

## Create a Dockerfile

```resolver/dockerfile
FROM ubuntu:22.04

RUN apt-get update && apt-get install -y unbound && rm -rf /var/lib/apt/lists/*

COPY unbound.conf /etc/unbound/unbound.conf

CMD ["/usr/sbin/unbound", "-d", "-c", "/etc/unbound/unbound.conf"]
```

This Dockerfile installs Unbound on an Ubuntu 22.04 base image and copies the `unbound.conf` configuration file, then setting the default command to run unbound with the -d flag (to run in the foreground) and the -c flag to specify the configuration file.

## Create an unbound.conf

```resolver/unbound.conf
server:
    interface: 0.0.0.0
    access-control: 192.168.1.0/24 allow
    remote-control:
        control-enable: no
stub-zone:
    name: "mich0w0h.house"
    stub-addr: 192.168.1.102
forward-zone:
    name: "."
    forward-addr: 8.8.8.8 # google DNS
    # forward-addr: 192.168.10.1. # ISP provided DNS
```

This configuration file sets up the following:

- `server` section configures the Unbound server to listen on all interfaces (`0.0.0.0`) on port 53 and allows queries from the `192.168.1.0/24` subnet.
- `stub-zone` section configures a stub zone for the `mich0w0h.house` domain, forwarding queries to the authoritative nameserver at `192.168.1.102:53`.
- `forward-zone` section configures forwarding for all other domains to Google DNS (`8.8.8.8`). You can also use your ISP's DNS server if preferred.

## Building and Running

Build the Docker image:

```sh
sudo docker image build -t unbound-resolver resolver/
```

Run the container for testing the configuration:

```sh
sudo docker container run --rm --name resolver-test --network internal-dns --ip 192.168.1.101 unbound-resolver unbound-checkconf
```

If the output shows `unbound-checkconf: no errors in /etc/unbound/unbound.conf`, then the configuration is valid.

Stop the resolver-test container and run a Unbound resolver container:

```sh
sudo docker container run --rm -d --name resolver --network internal-dns --ip 192.168.1.101 unbound-resolver
```

## Testing

You can test the resolver by querying for a record in the `mich0w0h.house` domain:

```sh
dig @192.168.1.101 www.mich0w0h.house
```

This should return the IP address configured for `www.mich0w0h.house` in the authoritative nameserver.

```sh
; <<>> DiG 9.18.18-0ubuntu0.22.04.2-Ubuntu <<>> @192.168.1.101 www.mich0w0h.house
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 64714
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;www.mich0w0h.house.		IN	A

;; ANSWER SECTION:
www.mich0w0h.house.	86400	IN	A	192.168.1.103

;; Query time: 0 msec
;; SERVER: 192.168.1.101#53(192.168.1.101) (UDP)
;; WHEN: Thu Mar 21 07:52:43 JST 2024
;; MSG SIZE  rcvd: 63
```

With this setup, your devices on the `192.168.1.0/24` subnet can use the resolver at `192.168.1.101` to resolve DNS queries. The resolver will handle queries for `mich0w0h.house` by forwarding them to the authoritative nameserver while forwarding queries for other domains to external DNS servers.

## What's Next
next, I'll use docker compose to build the authoritative nameserver container I set up in the previous article and the recursive resolver container I set up in this article.

It is necessary to configure networking in order to access these containers from other devices on the home network. (Yes, indeed currently these containers can only be accessed from the Ubuntu server on the host machine.)

## References
- [DNSがよくわかる教科書 | SBクリエイティブ](https://www.sbcr.jp/product/4797394481/)
- [開発系エンジニアのためのDocker絵とき入門 - 秀和システム あなたの学びをサポート！](https://www.shuwasystem.co.jp/book/9784798071503.html)
- [Configuration — Unbound 1.19.3 documentation](https://unbound.docs.nlnetlabs.nl/en/latest/getting-started/configuration.html)
- [NLnet Labs Documentation - Unbound - unbound-checkconf.8](https://nlnetlabs.nl/documentation/unbound/unbound-checkconf/)
- [NLnet Labs Documentation - Unbound - unbound.conf.5](https://nlnetlabs.nl/documentation/unbound/unbound.conf/)
- [Resolver for Home Networks — Unbound 1.19.3 documentation](https://unbound.docs.nlnetlabs.nl/en/latest/use-cases/home-resolver.html)
- [Docker+DNS入門　その2：Unboundを用いたキャッシュDNSサーバ構築 #Docker - Qiita](https://qiita.com/miwamoto/items/7981c390ac876a7fac5e)