---
title: "DNS over TLS using stunnel "
slug: "aa-01386"
description: "This article explains how to provide a DNS over TLS service using BIND 9 and stunnel, as well as set up a privacy aggregator."
tags: ["DOT", "RFC 7858", "encryption"]
updated: 2023-12-20T11:22:09Z
published: 2023-12-20T11:22:09Z
---

> ## Documentation Index
> Fetch the complete documentation index at: https://kb.isc.org/llms.txt
> Use this file to discover all available pages before exploring further.

# DNS over TLS

NB: This document is retained for historical interest only as "named" and "dig" have supported TLS natively since BIND release 9.17.7.

RFC 7858 specifies DNS over TLS (Transport Layer Security). There are multiple ways to implement DoT. One implementation example, which uses nginx, is provided in the contrib directory of the BIND 9 distribution, entitled 'dnspriv'. This is documented at https://dnsprivacy.org/wiki/.

This article explains how to provide a DNS over TLS service using BIND 9 and [stunnel](https://www.stunnel.org). *The setup of a privacy aggregator is at the end.*

**BIND 9** configuration: nothing special, but if you want to limit external insecure access to the service you can play with `listen-on` clause address and port, `acl`, or even a system firewall as **BIND 9** provides no per-transport protocol access control.

**stunnel** setup for the opportunistic privacy profile:

- Create a X.509 public key certificate, for instance by:

```
openssl genrsa -out dns.key 1024

openssl req -new -key dns.key -out dns.crt -x509
```

This creates a self-signed certificate, enough for clients performing no authentication.

- Create a **stunnel** configuration `dnstls.conf`:

```
[dns]

accept = 853

connect = 127.0.0.1:53

cert = dns.crt

key = dns.key
```

The service_name should be `dns` according to documentation. The DNS over TLS well-known port is `853`; **stunnel** will accept any TLS connection on this port and forward content in TCP to `127.0.0.1` (localhost) on port `53`(dns).

- Launch **stunnel** in daemon mode using the configuration file:

```
stunnel dnstls.conf
```

**stunnel** setup for the the out-of-band key-pinned privacy profile:

- You should use a real X.509 CA but for experiments you can create a CA certificate by:

```
openssl genrsa -out ca.key 1024

openssl req -new -key ca.key -out ca.crt -x509 -extensions v3_ca
```

- Create a X.509 public key certificate in a X.509 Certificate Authority, for instance the homemade CA:

```
openssl genrsa -out dns.key 1024

openssl req -new -key dns.key -out dns.req

openssl x509 -req -in dns.req -out dns.crt -CA ca.crt -CAkey ca.key -CAcreateserial
```

- Add in **stunnel** configuration:

```
CAfile = ca.crt
```

This makes **stunnel** add the CA certificate to the chain during TLS handshake (as it is supposed to do).

- Launch **stunnel** in daemon mode

#### How to test the service using **getdns** (https://getdnsapi.net).

- Install (or configure and compile) **getdns** with the `getdns_query` tool you can find in `src/test` of the distribution.
- If you'd like to authenticate the server, the CA must be known. The simplest way to do it is:

```
$ export SSL_CERT_FILE=.../ca.crt

% setenv SSL_CERT_FILE .../ca.crt
```

If you have a shell or a c-shell filling the ... by the path where the **OpenSSL** library can find the CA certificate.

- For key-pinning you have to compute the sha256 pin, according to [https://timtaubert.de/blog/2014/10/http-public-key-pinning-explained/](https://timtaubert.de/blog/2014/10/http-public-key-pinning-explained/). You can use from the key:

```
openssl rsa -in dns.key -outform der -pubout | openssl dgst -sha256 -binary | openssl enc -base64
```

or from the certificate:

```
openssl x509 -in dns.crt -pubkey -noout | openssl rsa -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64
```

- A typical call could be:

```
getdns_query -s foo.example.org a @10.1.2.3 -l L
```

`-l L` ask for TLS transport (vs. `U` for UDP or `T` for TCP)

- or with key-pinning:

```
getdns_query -s foo.example.org a @10.1.2.3 -l L -K 'pin-sha256="KAGwR1fXzY4JJtBP1yYoAisc+4yNomT6VrFPwkMi5qE="' -m
```

`-K` specifies a public key pin, `-m` requires authentication (vs `-n` for no authentication).

**BIND 9** setup for a privacy aggregator uses, for instance:

```
options {

    ...

    forwarders { 127.0.0.1 port 1053; };

    forward only;

};

server 127.0.0.1 {

    tcp-only yes;

};

...
```

`forwarders` makes all queries to be forwarded to the designated service on another port, `forward only` disables fallback to standard resolution, and the `tcp-only` clause in the `server` entry enforces the use of TCP transport (note this feature was added in version 9.11).

**stunnel** setup for a privacy aggregator is in client mode with, for instance:

```
[dns]

client = yes

accept = localhost:1053

connect = <server>:853
```

To test the privacy aggregator setup: <client_hosts> --- *:53 <named> localhost:1053 <stunnel> ---- *:853 <unbound> an **unbound** configuration could be:

```
server:

    ...

    interface: 0.0.0.0@853

    ssl-port: 853

    ssl-service-key: dns.key

    ssl-service-pen: dns.crt

    do-udp: no
```
