Skip to content

NextDNS Mode

Yegor S edited this page Jan 6, 2024 · 5 revisions

Requirements

v1.3.2 or newer must be installed in order to use this guide.

Overview

ctrld is a customizable DNS forwarding proxy server that runs on any operating system, most routers, and can be used to send DNS queries to any upstream, using any DNS protocol. NextDNS mode allows for a 1 liner command setup of ctrld with NextDNS service, while relaying client specific metadata (MAC address, IP address, Hostname) to a NextDNS upstream, similar to the native app.

As far as I can tell, ctrld is feature complete with the NextDNS analog (all functionality of the NextDNS app is supported as of v1.3.2), but also includes other functionality such as support for all DNS protocols, multiple listeners, richer configuration syntax, support for more routers, and highly advanced modes of operation.

Supported Platforms

  • Asus Merlin
  • DD-WRT
  • Firewalla
  • FreshTomato
  • GL.iNet
  • OpenWRT
  • pfSense / OPNsense
  • Synology
  • Ubiquiti (UniFi, EdgeOS)
  • Windows
  • MacOS
  • Linux (any)

Why this exists

We want you to try our app with NextDNS, love all the things you can do with it, and then maybe check out this Control D thing.

How to Use

You can get the binaries here, but the simplest way to get ctrld on your device is using the installer command:

Linux/MacOS/FreeBSD Install

sh -c 'sh -c "$(curl -sL https://api.controld.com/dl)"'

Windows Install

Run this in cmd not Powershell.

powershell -Command "(Invoke-WebRequest -Uri 'https://api.controld.com/dl' -UseBasicParsing).Content | Set-Content 'ctrld_install.bat'" && ctrld_install.bat

Usage

The above commands will install the binary into the correct directory (installer will show you where), based on your platform. Here is an example from pfSense, but all platforms are the same.

[2.7.0-RELEASE][root@pfSense.home.arpa]/root: sh -c 'sh -c "$(curl -sSL https://api.controld.com/dl)"'

        __         .__       .___
  _____/  |________|  |    __| _/
_/ ___\   __\_  __ \  |   / __ |
\  \___|  |  |  | \/  |__/ /_/ |
 \___  >__|  |__|  |____/\____ |
     \/       installer       \/

---------------------
|    System Info    |
---------------------
OS Type      : freebsd
OS Vendor    : pfSense
Arch         : amd64
CPU          : 13th Gen Intel(R) Core(TM) i7-13700K
Free RAM     : 70 MB / 448 MB
---------------------
|  Install Details  |
---------------------
Binary URL   : https://assets.controld.com/ctrld/freebsd/amd64/ctrld
Install Path : /usr/local/bin
---------------------
Install binary and run it? (y/n): y
 - Starting download
 - Making binary executable
 - Launching /usr/local/bin/ctrld
---------------------
        __         .__       .___
  _____/  |________|  |    __| _/
_/ ___\   __\_  __ \  |   / __ |
\  \___|  |  |  | \/  |__/ /_/ |
 \___  >__|  |__|  |____/\____ |
     \/ dns forwarding proxy  \/

Usage:
  ctrld [command]

Available Commands:
  run         Run the DNS proxy server
  service     Manage ctrld service
  start       Quick start service and configure DNS on interface
  stop        Quick stop service and remove DNS from interface
  restart     Restart the ctrld service
  reload      Reload the ctrld service
  status      Show status of the ctrld service
  uninstall   Stop and uninstall the ctrld service
  clients     Manage clients

Flags:
  -h, --help            help for ctrld
  -s, --silent          do not write any log output
  -v, --verbose count   verbose log output, "-v" basic logging, "-vv" debug level logging
      --version         version for ctrld

Use "ctrld [command] --help" for more information about a command.
[2.7.0-RELEASE][root@pfSense.home.arpa]/root:

The binary is installed, but currently not doing anything. In order for it to do stuff, use the start command with the --nextdns flag, while supplying your ID.

[2.7.0-RELEASE][root@pfSense.home.arpa]/root: ctrld start --nextdns 8cec72
Nov 20 22:44:21.000 NTC Starting service
Nov 20 22:44:21.000 NTC Generating nextdns config: /etc/controld/ctrld.toml
Nov 20 22:44:26.000 NTC Service started

That's it, you're done here. You can check that it works using a dig or nslookup command, or simply view your Logs in NextDNS.

[2.7.0-RELEASE][root@pfSense.home.arpa]/root: dig test.com

; <<>> DiG 9.18.14 <<>> test.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 25793
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;test.com.                      IN      A

;; ANSWER SECTION:
test.com.               3600    IN      A       67.225.146.248

;; Query time: 42 msec
;; SERVER: 127.0.0.1#53(127.0.0.1) (UDP)
;; WHEN: Mon Nov 20 22:44:38 UTC 2023
;; MSG SIZE  rcvd: 53

[2.7.0-RELEASE][root@pfSense.home.arpa]/root:

image

By default, ctrld will send all traffic from your device or router to the chosen NextDNS profile, using DOH3 protocol, while appending LAN metadata just like the native app. If you wish to get fancy, read on.

Advanced Usages

Everything is config driven, and the start command will tell you where the config was written to, usually it's /etc/controld/ but the path can differ based on your router platform. You can edit this config to get ctrld to route your DNS queries in highly advanced ways. Here is some relevant material to get you started:

Once you crafted your perfect config, simply run: ctrld restart in order for changes to kick in. To see what LAN clients ctrld discovered using DHCP leases file, arp, mDNS, PTR probes or hosts file, you can run the ctrld clients list command. This client data will be appended to DNS queries. Be mindful, that this only works when DNS-over-HTTPS (any version) is used. DOT and DOQ don't support this.

[2.7.0-RELEASE][root@pfSense.home.arpa]/root: ctrld clients list
+-----------------------------------------+------------+-------------------+------------+
|                   IP                    |  Hostname  |        Mac        | Discovered |
+-----------------------------------------+------------+-------------------+------------+
| 10.0.10.1                               |            | 00:50:56:9f:0e:84 | arp        |
| 10.0.10.209                             | pfSense    | 00:0c:29:f5:a3:55 | arp,dhcp   |
| 10.0.10.238                             | Office-Box | 74:56:3c:44:eb:5e | arp,mdns   |
| 10.0.10.245                             | Test-W11   |                   | mdns       |
| 127.0.0.1                               | pfSense    | 00:0c:29:f5:a3:55 | dhcp       |
| ::1                                     | pfSense    | 00:0c:29:f5:a3:55 | dhcp       |
| 2607:f0c8:8000:8210:10a:e664:e855:1f61  | Office-Box |                   | mdns       |
| 2607:f0c8:8000:8210:20c:29ff:fef5:a355  | pfSense    | 00:0c:29:f5:a3:55 | dhcp       |
| 2607:f0c8:8000:8210:2d11:e044:9e90:a14c | Test-W11   |                   | mdns       |
| 2607:f0c8:8000:8210:4d66:459f:6b76:1c16 | Test-W11   |                   | mdns       |
| 2607:f0c8:8000:8210:dcf4:5b74:4f7e:bd7f | Office-Box |                   | mdns       |
| fe80::20c:29ff:fef5:a355                | pfSense    | 00:0c:29:f5:a3:55 | dhcp       |
| fe80::1554:c4ab:cfba:189                | Office-Box |                   | mdns       |
| fe80::ab3f:8a1c:df6b:91b9               | Test-W11   |                   | mdns       |
+-----------------------------------------+------------+-------------------+------------+
[2.7.0-RELEASE][root@pfSense.home.arpa]/root:

By the way, ctrld will resolve any LAN-local A or PTR record for you, using the data it has in the client list.

[2.7.0-RELEASE][root@pfSense.home.arpa]/var: dig +short Test-W11
10.0.10.245
[2.7.0-RELEASE][root@pfSense.home.arpa]/var: dig +short -x 10.0.10.245
Test-W11.

DNS Steering Rules

Much like in the native app, you can steer DNS queries to different NextDNS Profiles based on:

  • hostnames
  • MAC addresses
  • IP networks

This is accomplished using policies. Let's take a look at an example config file. Before you panic, it's simpler (and more powerful) than it looks.

[listener]
  [listener.0]
    ip = '0.0.0.0'
    port = 53

    [listener.0.policy]
      name = 'My Policy'
      networks = [
          {'network.0' = ['upstream.0']},
          {'network.1' = ['upstream.1']}
      ]

      rules = [
        { '*.cool.domain' = ['upstream.1']},
        { '*.in-addr.arpa' = ['upstream.1']}
      ]

      macs = [
       {"14:54:4a:8e:08:2d" = ["upstream.1"]}
      ]

[network]
  [network.0]
    name = 'Main Subnets'
    cidrs = ['10.0.0.0/24', '10.0.1.0/24']

  [network.1]
    name = 'Secret Subnet'
    cidrs = ['10.0.99.0/24']

[upstream]
  [upstream.0]
    name = 'My NextDNS Resolver'
    type = 'doh3'
    endpoint = 'https://dns.nextdns.io/qwerty'
    timeout = 5000

  [upstream.1]
    name = 'My Fancy Control D Resolver'
    type = 'doh3'
    endpoint = 'https://dns.controld.com/abcd1234'
    timeout = 5000

Not all params are needed, and shown for illustrative purposes only. Let's go over the config section by section.

  1. In the [listener] block we define our.... listener with an IP + port.
  2. In the [listener.0.policy] block we define the policy of how DNS traffic should be routed, let's skip that over for a second.
  3. In the [network] block we define our subnets if you want to leverage source IP based routing. If you do not, don't define any.
  4. In the [upstream] block we define our DNS upstreams where DNS traffic should be sent. You should have at least one of these, but here we have 2.
  5. Coming back to the [listener.0.policy] block. It strings together the defined networks and upstreams, as well as new concepts like rules and macs and defines which upstream should be used if there is a match.
  6. The matching order is: rules => macs => networks

So for example:

  • A DNS query from 10.0.0.5 would be sent to upstream.0, while a query from 10.0.99.123 would be sent to upstream.1
  • A DNS query for my-host.cool.domain from any subnet would be sent to upstream.1 (since host rules match first)
  • A DNS query from a device with MAC address 14:54:4a:8e:08:2d from any subnet would be sent to upstream.1 (since MAC rules match 2nd).
  • All PTR queries would be sent to upstream.1

You can find more example configs for different use cases in the Wiki.

That's all there is to it. If you spot any issues with this guide, let us know.