Routing DNS over HTTPS Using Raspberry Pi

Posted October 6th, 2020, last updated October 6th, 2020 in cloud-software

DNS is a protocol from the late 1980s, and today at its core DNS is still exactly the same. When it was conceived, there wasn't the same privacy focus as there is today, and one of the main drawbacks with the protocol is that queries and responses are not encrypted nor tamper proof when sent over the internet.

DNS over HTTPS is a newer take on the original DNS protocol, which routes queries over secure HTTP connections. While this is seeing some support (namely in Firefox and Windows 10), many devices on your network will continue to send DNS queries over UDP for years to come.

Contents

  1. UDP Queries via HTTPS
  2. Setting Up The Server
  3. Blocking Adverts and Trackers

UDP Queries via HTTPS

I have been interested in how DNS works for a while now, and a few weeks ago started writing a DNS client/server in .NET Core. There are clients for HTTPS, TCP and UDP, any of which can be used by the server (see README for more information).

The library allows UDP DNS queries to be routed directly to a DNS over HTTPS (DoH) endpoint. If the server is set up on an IoT device like a Raspberry Pi and set as the default DNS server for the network, all UDP queries from devices on that network will be sent to your DNS provider via DoH.

In a typical home network setup your DNS requests would follow this path:

  • Local Device (UDP) → Remote DNS (UDP)

The path as described above with a Raspberry Pi is:

  • Local Device (UDP) → Local Pi (HTTPS) → Remote DNS (HTTPS)

This setup eliminates the ability of an entity in the middle to log and tamper with your DNS requests.

Setting Up The Server

The below guide assumes you want to use the default server implementation of Ae.Dns, which accepts UDP queries and routes them over HTTPS to the Google and CloudFlare public DNS services.

We will be using Ubuntu Server 20.04 LTS as the OS for the Raspberry Pi, but the executable itself runs on any Linux ARM x64 device (and can easily be compiled for any other target that .NET supports). These instructions assume Ubuntu for the service handling.

Getting the Server

A pre-built implementation of the server is available for linux-arm64.zip for Ubuntu on a Raspberry Pi, or linux-x64.zip for a regular x64 linux server. Here's the GitHub release.

This post assumes you will unzip the server to /home/ubuntu/dns/deploy, but if you want to put it somewhere better such as /opt/ adjust the paths accordingly.

Ensure that the server executable is permitted to bind to port 53 using the following command:

$ setcap 'cap_net_bind_service=+ep' /home/ubuntu/dns/deploy/Ae.Dns.Console

Stop Ubuntu's DNS Server

To allow the server to listen on port 53, you need to disable the DNS server on Ubuntu:

$ sudo nano /etc/systemd/resolved.conf
[Resolve]
#DNS=
#FallbackDNS=
#Domains=
#LLMNR=no
#MulticastDNS=no
#DNSSEC=no
#DNSOverTLS=no
#Cache=yes
DNSStubListener=no # uncomment, set to no
#ReadEtcHosts=yes

Once the changes are made, restart the DNS service:

$ sudo service systemd-resolved restart

Set a DNS Server for the Pi

In order to keep your Pi able to resolve domains even when the DNS server isn't working, you'll want to tell it to use a DNS server other than your router. To do that, you should install resolvconf:

$ sudo apt install resolvconf

Once installed, edit the following file:

$ sudo nano /etc/resolvconf/resolv.conf.d/head

And add your chosen DNS server:

nameserver 8.8.8.8

When your system next reboots, /etc/resolv.conf will now start with your specified DNS server.

Creating a Ubuntu Service

To start the server at boot and in case of a crash, we'll create a systemd service.

I used this excellent tutorial on systemd to craft the below service definition:

$ sudo nano /etc/systemd/system/aedns.service
[Unit]
Description=aedns
After=network.target
StartLimitIntervalSec=0

[Service]
Type=simple
Restart=always
RestartSec=1
User=ubuntu # Set this to the user you want the server to run as
WorkingDirectory=/home/ubuntu/dns/deploy
ExecStart=/home/ubuntu/dns/deploy/Ae.Dns.Console

[Install]
WantedBy=multi-user.target

Then to start the service and enable it at startup:

$ sudo service enable aedns
$ sudo service start aedns
$ sudo service status aedns

You can now set the Pi's IP address as your default DNS server on your router, and all your network's DNS queries will be sent encrypted over HTTPS.

Troubleshooting

The following command can retrieve the status of the service:

$ sudo service aedns status

And this command can retrieve logs:

$ journalctl --unit aedns.service --pager-end

Blocking Adverts and Trackers

The server supports blocking adverts and trackers too using a remote hosts file.

Here are a few good ones at the time of writing:

To configure the server to use these block lists, modify config.json included with the server to add a remoteBlocklists property:

{
    ...
    "remoteBlocklists": [
        "https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts",
        "https://mirror1.malwaredomains.com/files/justdomains",
        "https://s3.amazonaws.com/lists.disconnect.me/simple_ad.txt"
    ]
    ...
}

The server will download the specified blocklists at startup. Once config.json is updated, restart the service:

sudo service aedns restart

Manually Permitting Domains

If you would like to permit certain domains, you can do so with the allowlistedDomains property:

{
    ...
    "allowlistedDomains": [
        "device-metrics-us-2.amazon.com",
        "mobileanalytics.us-east-1.amazonaws.com"
    ]
    ...
}

Once config.json is updated, restart the service:

sudo service aedns restart

The README in the Ae.Dns repository has code examples, and the core clients and servers are also available as packages on NuGet.

Comments