Setting up DNS over HTTPS on macOS

Back in April, Cloudflare announced a privacy-focused DNS server running at 1.1.1.1 (and 1.0.0.1), and that it supported DNS over HTTPS. A lot of regular traffic goes over HTTPS these days, but DNS queries to look up the IP address of a domain are still unencrypted, so your ISP can still snoop on which servers you’re visiting even if they can’t see the actual content. We have a Mac mini that runs macOS Server and does DHCP and DNS for our home network, among other things, and with the impending removal of those functions and their suggested replacements with regular non-UI tools with a upcoming version of it, I figured now would be a good time to look into moving us over to use Cloudflare’s shiny new DNS server at the same time.

Turns out it wasn’t that difficult!

Overview

  1. Install Homebrew.
  2. Install cloudflared and dnsmasq: brew install cloudflare/cloudflare/cloudflared dnsmasq
  3. Configure dnsmasq to point to cloudflared as its own DNS resolver.
  4. Configure cloudflared to use DNS over HTTPS and run on port 54.
  5. Install both as services to run at system boot.

Configuring dnsmasq

Edit the configuration file located at /usr/local/etc/dnsmasq.conf and uncomment line 66 and change it from server=/localnet/192.168.0.1 to server=127.0.0.1#54 to tell it to pass DNS requests onto localhost on port 54, which is where cloudflared will be set up.

Configuring cloudflared

Create the directory /usr/local/etc/cloudflared and create a file inside that called config.yml with the following contents:

port: 54
no-autoupdate: true
proxy-dns: true
proxy-dns-upstream:
  - https://1.1.1.1/dns-query
  - https://1.0.0.1/dns-query

Auto-update is disabled because that seems to break things when the update occurs, and the service doesn’t start back up correctly.

Configuring dnsmasq and cloudflared to start on system boot

dnsmasq is easy, simply run: sudo brew services start dnsmasq which will both start it immediately and also set it to start at system boot.

Due to a bug that isn’t fixed as of writing, setting the port for cloudflared requires being set via a launchctl environment variable. Install it as a service with sudo cloudflared service install, then run sudo launchctl unload /Library/LaunchDaemons/com.cloudflare.cloudflared.plist  to temporarily turn off the service. Next, run sudo launchctl setenv TUNNEL_DNS_PORT 54 to set the environment variable such that the launch script will pick it up, and lastly run sudo launchctl load /Library/LaunchDaemons/com.cloudflare.cloudflared.plist to start the service up again.

This is the same thing as setting port: 54 in the configuration file above but works around the aforementioned bug where that setting is ignored (and so tries to start on the default port 53 which fails because dnsmasq is already running there).

And done!

Apart from a bunch of work to figure out how to work around that cloudflared bug, I was surprised at how straightforward this was. I also didn’t realise until I was doing all of this that dnsmasq also does DHCP, so with the assistance of this blog post I’ve also replaced the built-in DHCP server on the Mac mini and continue to have full local hostname resolution as well!

Leave a Reply

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