The config file allows for advanced configuration of the ctrld
utility to cover a vast array of use cases.
- Source IP based DNS routing policies
- Destination IP based DNS routing policies
- Split horizon DNS
ctrld
uses TOML format for its configuration file. Default configuration file is ctrld.toml
found in following order:
/etc/controld
on *nix.- User's home directory on Windows.
- Same directory with
ctrld
binary on these routers:ddwrt
merlin
freshtomato
- Current directory.
The user can choose to override default value using command line --config
or -c
:
ctrld run --config /path/to/myconfig.toml
If no configuration files found, a default ctrld.toml
file will be created in the current directory.
In pre v1.1.0, config.toml
file was used, so for compatibility, ctrld
will still read config.toml
if it's existed.
[service]
log_level = "info"
log_path = ""
cache_enable = true
cache_size = 4096
cache_ttl_override = 60
cache_serve_stale = true
[network.0]
cidrs = ["0.0.0.0/0"]
name = "Everyone"
[network.1]
cidrs = ["10.10.10.0/24"]
name = "Admins"
[upstream.0]
bootstrap_ip = "76.76.2.11"
endpoint = "https://freedns.controld.com/p1"
name = "Control D - Anti-Malware"
timeout = 5000
type = "doh"
ip_stack = "both"
[upstream.1]
bootstrap_ip = "76.76.2.11"
endpoint = "p2.freedns.controld.com"
name = "Control D - No Ads"
timeout = 5000
type = "doq"
ip_stack = "split"
[upstream.2]
bootstrap_ip = "76.76.2.22"
endpoint = "private.dns.controld.com"
name = "Control D - Private"
timeout = 5000
type = "dot"
ip_stack = "v4"
[listener.0]
ip = "127.0.0.1"
port = 53
[listener.0.policy]
name = "My Policy"
networks = [
{"network.0" = ["upstream.1"]},
]
rules = [
{"*.local" = ["upstream.1"]},
{"test.com" = ["upstream.2", "upstream.1"]},
]
[listener.1]
ip = "127.0.0.69"
port = 53
restricted = true
See below for details on each configuration block.
The [service]
section controls general behaviors.
[service]
log_level = "debug"
log_path = "log.txt"
Logging level you wish to enable.
- Type: string
- Required: no
- Valid values:
debug
,info
,warn
,notice
,error
,fatal
,panic
- Default:
notice
Relative or absolute path of the log file.
- Type: string
- Required: no
- Default: ""
When cache_enable = true
, all resolved DNS query responses will be cached for duration of the upstream record TTLs.
- Type: boolean
- Required: no
- Default: false
The number of cached records, must be a positive integer. Tweaking this value with care depends on your available RAM.
A minimum value 4096
should be enough for most use cases.
An invalid cache_size
value will disable the cache, regardless of cache_enable
value.
- Type: int
- Required: no
- Default: 4096
When cache_ttl_override
is set to a positive value (in seconds), TTLs are overridden to this value and cached for this long.
- Type: int
- Required: no
- Default: 0
When cache_serve_stale = true
, in cases of upstream failures (upstreams not reachable), ctrld
will keep serving
stale cached records (regardless of their TTLs) until upstream comes online.
- Type: boolean
- Required: no
- Default: false
When ctrld
receives query with domain name in cache_flush_domains
, the local cache will be discarded
before serving the query.
- Type: array of strings
- Required: no
The number of concurrent requests that will be handled, must be a non-negative integer. Tweaking this value depends on the capacity of your system.
- Type: number
- Required: no
- Default: 256
Perform LAN client discovery using mDNS. This will spawn a listener on port 5353.
- Type: boolean
- Required: no
- Default: true
Perform LAN client discovery using ARP.
- Type: boolean
- Required: no
- Default: true
Perform LAN client discovery using DHCP leases files. Common file locations are auto-discovered.
- Type: boolean
- Required: no
- Default: true
Perform LAN client discovery using PTR queries.
- Type: boolean
- Required: no
- Default: true
Perform LAN client discovery using hosts file.
- Type: boolean
- Required: no
- Default: true
Time in seconds between each discovery refresh loop to update new client information data. The default value is 120 seconds, lower this value to make the discovery process run more aggressively.
- Type: integer
- Required: no
- Default: 120
Relative or absolute path to a custom DHCP leases file location.
- Type: string
- Required: no
- Default: ""
DHCP leases file format.
- Type: string
- Required: no
- Valid values:
dnsmasq
,isc-dhcp
,kea-dhcp4
- Default: ""
Decide how the client ID is generated. By default client ID will use both MAC address and Hostname i.e. hash(mac + host)
. To override this behavior, select one of the 2 allowed values to scope client ID to just MAC address OR Hostname.
- Type: string
- Required: no
- Valid values:
mac
,host
- Default: ""
If set to true
, collect and export the query counters, and show them in clients list
command.
- Type: boolean
- Required: no
- Default: false
Specifying the ip
and port
of the Prometheus metrics server. The Prometheus metrics will be available on: http://ip:port/metrics
. You can also append /metrics/json
to get the same data in json format.
- Type: string
- Required: no
- Default: ""
Checking DNS changes to network interfaces and reverting to ctrld's own settings.
The DNS watchdog process only runs on Windows and MacOS.
- Type: boolean
- Required: no
- Default: true
Time duration between each DNS watchdog iteration.
A duration string is a possibly signed sequence of decimal numbers, each with optional fraction and a unit suffix, such as "300ms", "-1.5h" or "2h45m". Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
If the time duration is non-positive, default value will be used.
- Type: time duration string
- Required: no
- Default: 20s
Time in seconds between each iteration that reloads custom config if changed.
The value must be a positive number, any invalid value will be ignored and default value will be used.
- Type: number
- Required: no
- Default: 3600
Once ctrld is "offline", mean ctrld could not connect to any upstream, next queries will be leaked to OS resolver.
- Type: boolean
- Required: no
- Default: true on Windows, MacOS and non-router Linux.
The [upstream]
section specifies the DNS upstream servers that ctrld
will forward DNS requests to.
[upstream.0]
bootstrap_ip = ""
endpoint = "https://freedns.controld.com/p1"
name = "Control D - DOH"
timeout = 5000
type = "doh"
ip_stack = "split"
[upstream.1]
bootstrap_ip = ""
endpoint = "https://freedns.controld.com/p1"
name = "Control D - DOH3"
timeout = 5000
type = "doh3"
ip_stack = "both"
[upstream.2]
bootstrap_ip = ""
endpoint = "p1.freedns.controld.com"
name = "Controld D - DOT"
timeout = 5000
type = "dot"
ip_stack = "v4"
[upstream.3]
bootstrap_ip = ""
endpoint = "p1.freedns.controld.com"
name = "Controld D - DOT"
timeout = 5000
type = "doq"
ip_stack = "v6"
[upstream.4]
bootstrap_ip = ""
endpoint = "76.76.2.2"
name = "Control D - Ad Blocking"
timeout = 5000
type = "legacy"
ip_stack = "both"
IP address of upstream DNS server when hostname or URL is used. This exists to prevent the bootstrapping cycle problem.
For example, if the Endpoint
is set to https://freedns.controld.com/p1
, ctrld
needs to know the ip address of freedns.controld.com
to be able to do communication. To do that, ctrld
may need to use OS resolver, which may or may not be set.
If bootstrap_ip
is empty, ctrld
will resolve this itself using its own bootstrap DNS, normal users should not care about bootstrap_ip
and just leave it empty.
- type: ip address string
- required: no
- Default: ""
IP address, hostname or URL of upstream DNS. Used together with Type
of the endpoint.
- Type: string
- Required: yes
Default ports are implied for each protocol, but can be overriden. ie. p1.freedns.controld.com:1024
Human-readable name of the upstream.
- Type: string
- Required: no
- Default: ""
Timeout in milliseconds before request failsover to the next upstream (if defined).
Value 0
means no timeout.
- Type: number
- Required: no
- Default: 0
The protocol that ctrld
will use to send DNS requests to upstream.
- Type: string
- Required: yes
- Valid values:
doh
,doh3
,dot
,doq
,legacy
Specifying what kind of ip stack that ctrld
will use to connect to upstream.
- Type: string
- Required: no
- Valid values:
both
: using either ipv4 or ipv6.v4
: only dial upstream via IPv4, never dial IPv6.v6
: only dial upstream via IPv6, never dial IPv4.split
:- If
A
record is requested -> dial via ipv4. - If
AAAA
or any other record is requested -> dial ipv6 (if available, otherwise ipv4)
- If
If ip_stack
is empty, or undefined:
- Default value is
both
for non-Control D resolvers. - Default value is
split
for Control D resolvers.
Specifying whether to include client info when sending query to upstream. This will only work with doh
or doh3
type upstreams.
- Type: boolean
- Required: no
- Default:
true
for ControlD upstreams.false
for other upstreams.
Specifying whether the upstream can be used for PTR discovery.
- Type: boolean
- Required: no
- Default:
true
for loopback/RFC1918/CGNAT IP address.false
for public IP address.
The [network]
section defines networks from which DNS queries can originate from. These are used in policies. You can define multiple networks, and each one can have multiple cidrs.
[network.0]
cidrs = ["0.0.0.0/0"]
name = "Any Network"
[network.1]
cidrs = ["192.168.1.0/24"]
name = "Home Wifi "
Name of the network.
- Type: string
- Required: no
- Default: ""
Specifies the network addresses that the listener
will accept requests from. You will see more details in the listener policy section.
- Type: array of network CIDR string
- Required: no
- Default: []
The [listener]
section specifies the ip and port of the local DNS server. You can have multiple listeners, and attached policies.
[listener.0]
ip = "127.0.0.1"
port = 53
[listener.1]
ip = "10.10.10.1"
port = 53
restricted = true
IP address that serves the incoming requests. If ip
is empty, ctrld will listen on all available addresses.
- Type: ip address string
- Required: no
- Default: "0.0.0.0" or RFC1918 addess or "127.0.0.1" (depending on platform)
Port number that the listener will listen on for incoming requests. If port
is 0
, a random available port will be chosen.
- Type: number
- Required: no
- Default: 0 or 53 or 5354 (depending on platform)
If set to true
, makes the listener REFUSED
DNS queries from all source IP addresses that are not explicitly defined in the policy using a network
.
- Type: bool
- Required: no
- Default: false
The listener will refuse DNS queries from WAN IPs using REFUSED
RCODE by default. Set to true
to disable this behavior, but this is not recommended.
- Type: bool
- Required: no
- Default: false
Allows ctrld
to set policy rules to determine which upstreams the requests will be forwarded to.
If no policy
is defined or the requests do not match any policy rules, it will be forwarded to corresponding upstream of the listener. For example, the request to listener.0
will be forwarded to upstream.0
.
The policy rule
syntax is a simple toml
inline table with exactly one key/value pair per rule. key
is either:
- Network.
- Domain.
- Mac Address.
Value is the list of the upstreams.
For example:
[listener.0.policy]
name = "My Policy"
networks = [
{"network.0" = ["upstream.1"]},
]
rules = [
{"*.local" = ["upstream.1"]},
{"test.com" = ["upstream.2", "upstream.1"]},
]
macs = [
{"14:54:4a:8e:08:2d" = ["upstream.3"]},
]
Above policy will:
- Forward requests on
listener.0
for.local
suffixed domains toupstream.1
. - Forward requests on
listener.0
fortest.com
toupstream.2
. If timeout is reached, retry onupstream.1
. - Forward requests on
listener.0
from client with Mac14:54:4a:8e:08:2d
toupstream.3
. - Forward requests on
listener.0
fromnetwork.0
toupstream.1
. - All other requests on
listener.0
that do not match above conditions will be forwarded toupstream.0
.
An empty upstream would not route the request to any defined upstreams, and use the OS default resolver.
[listener.0.policy]
name = "OS Resolver"
rules = [
{"*.local" = []},
]
Note that the order of matching preference:
rules => macs => networks
And within each policy, the rules are processed from top to bottom.
name
is the name for the policy.
- Type: string
- Required: no
- Default: ""
networks
is the list of network rules of the policy.
- Type: array of networks
- Required: no
- Default: []
rules
is the list of domain rules within the policy. Domain can be either FQDN or wildcard domain.
- Type: array of rule
- Required: no
- Default: []
macs
is the list of mac rules within the policy. Mac address value is case-insensitive.
- Type: array of macs
- Required: no
- Default: []
For non success response, failover_rcodes
allows the request to be forwarded to next upstream, if the response RCODE
matches any value defined in failover_rcodes
.
- Type: array of strings
- Required: no
- Default: []
For example:
[listener.0.policy]
name = "My Policy"
failover_rcodes = ["NXDOMAIN", "SERVFAIL"]
networks = [
{"network.0" = ["upstream.0", "upstream.1"]},
]
If upstream.0
returns a NXDOMAIN response, the request will be forwarded to upstream.1
instead of returning immediately to the client.
See all available DNS Rcodes value here.