Skip to content

Example Configurations

Cuong Manh Le edited this page Sep 14, 2024 · 10 revisions

Example Configurations

The best way to learn something is to see an example. This page provides a few example ctrld configurations for different use cases. You can copy/paste these and use them as a starting point.

You can find your existing configuration file in the following locations, depending on your platform:

  • /etc/controld/ - on most platforms
  • /data/controld/ - on some Ubiqiti devices
  • /jffs/controld/ - on DD-WRT, Asus merlin, FreshTomato

Important: Enable Local Config Mode

If you used the default 1 liner installer to get ctrld on your device, or used the --cd RESOLVER_ID flag to start the service, you will see the following line at the top of the config file on disk:

# AUTO-GENERATED VIA CD FLAG - DO NOT MODIFY

In this mode, the config is automatically re-rendered every time the service starts, so your changes will be overwritten. In order to use ctrld in local config mode, execute the following commands:

  • ctrld stop - this will stop the service
  • ctrld start --config=/path/to/ctrld.toml - this will start the service in "local config mode" which enforces the config on disk

Now you can make changes to it, and execute ctrld restart command to enforce your changes.

Self Managed DNS Steering

If you wish to route DNS queries yourself, without any magic on the part of ctrld you can start it in pure service mode, where ctrld will register as a system service and start the necessary listeners, but make no changes to your DNS configuration (resolv.conf files, or interface with dnsmasq). You can then reference the listeners yourself part of your advanced setup.

  • ctrld service start --cd=RESOLVER_ID - starts ctrld service in cd mode but makes no DNS routing changes
  • ctrld service start --config=/path/to/ctrld.toml - starts ctrld service in local config mode but makes no DNS routing changes

Remember running service configuration

Since v1.3.8, if ctrld has been already installed, running ctrld start or ctrld service start will start the existing ctrld service instead of installing new one. That means ctrld service will be started with last service configuration.

Example:

$ ctrld start --cd=RESOLVER_ID -vv
$ ctrld stop
$ ctrld start # service will be run with --cd=RESOLVER_ID and -vv flags.

Passing any new arguments/flags to ctrld start will result in old behavior.

Simplest

The simplest possible configuration looks like this. The following config does the following:

  • Spawn a single DNS listener on 0.0.0.0 (all interfaces) and port 53
  • Send all DNS queries to the matching upstream (listener.0 -> upstream.0) which uses DNS-over-HTTPS
[listener]
  [listener.0]
    ip = '0.0.0.0'
    port = 53

[upstream]
  [upstream.0]
    type = 'doh'
    endpoint = 'https://freedns.controld.com/p2'
    timeout = 5000

Multiple Upstreams

Let's get a bit fancier, and specify multiple upstreams and route requests via network policy. The following config does the following:

  • Spawn a single DNS listener on 0.0.0.0 (all interfaces) and port 5354
  • Define 2 networks with 2 different subnets: 192.168.1.0/24 and 192.168.100.0/24
  • Define 2 upstreams with different levels of blocking
  • Using a network policy map 192.168.1.0/24 to upstream.0 and 192.168.100.0/24 to upstream.1. This means that requests originating from each subnet will be sent to a different upstream DNS resolver = 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).
  • If a request doesn't match the defined networks, it will use the default upstream.0. To override this behavior and REFUSE queries from undefined networks, use restricted resolver config param.
[listener]
  [listener.0]
    ip = '0.0.0.0'
    port = 5354

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

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

[network]
  [network.0]
    name = 'Adults'
    cidrs = ['192.168.1.0/24']

  [network.1]
    name = 'Children'
    cidrs = ['192.168.100.0/24']

[upstream]
  [upstream.0]
    name = 'Control D - No Ads'
    type = 'doh'
    endpoint = 'https://freedns.controld.com/p2'
    timeout = 5000

  [upstream.1]
    name = 'Control D - Family Friendly'
    type = 'doh'
    endpoint = 'https://freedns.controld.com/family'
    timeout = 5000

Custom Domains and Upstreams

If you wish to use your own local DNS resolver for your own custom domains, and send everything else to a remote upstream, you can do that too. The following config will do the following:

  • Spawn a single DNS listener on 10.0.0.1 and port 5354
  • Define 2 networks with 3 different subnets: 10.0.0.0/24 + 10.0.1.0/24 (network.0) and 10.0.99.0/24 (network.1)
  • Define 3 upstreams with 3 different endpoints, one of them being a local DNS resolver running on 10.0.0.1:1234
  • Using a network policy map network.0 (10.0.0.0/24, 10.0.1.0/24) to upstream.0, and network.1 (10.0.99.0/24) to upstream.1
  • Using a rules policy map DNS queries for *.domain.local (wildcard) and secret.cheese (specific) to upstream.2 which runs locally on 10.0.0.1:1234. This supersedes network policy rules.
[listener]
  [listener.0]
    ip = '10.0.0.1'
    port = 5354

    [listener.0.policy]
      name = 'My Policy'
      networks = [
          {'network.0' = ['upstream.0']},
          {'network.1' = ['upstream.1']}
      ]
      rules = [
        { '*.domain.local' = ['upstream.2']},
        { 'secret.cheese' = ['upstream.2']}
      ]

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

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

[upstream]
  [upstream.0]
    name = 'Unfiltered Resolver'
    type = 'doh'
    endpoint = 'https://freedns.controld.com/p0'
    timeout = 5000

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

Multiple Listeners

So far all examples used a single listener, which is likely what you want in 99% of cases. But you're the 1% that needs more. Well, we got you covered here as well. The following example config will do the following:

  • Spawn 2 different DNS listeners on 2 different IPs (127.0.0.1:5555 and 127.0.0.2:5555)
  • Define 2 upstreams with different DNS endpoints
  • DNS traffic received on listener.0 uses upstream.0, while DNS traffic on listener.1 uses upstream.1 (this is default behavior unless you have an overriding policy)
[listener]
  [listener.0]
    ip = '127.0.0.1'
    port = 5555

  [listener.1]
    ip = '127.0.0.2'
    port = 5555

[upstream]
  [upstream.0]
    type = 'doh'
    endpoint = 'https://freedns.controld.com/p2'
    timeout = 5000

  [upstream.1]
    type = 'legacy'
    endpoint = '1.1.1.1'
    timeout = 5000

You can now use network and rule policies in conjunction with the above to define highly complex behaviors using multiple listeners. Use the earlier example for a single listener, as it works the same. Just be sure to attach the policy to the correct listener.