Block online ads by intercepting DNS queries
The github.com/shoenig/donutdns
module provides a CoreDNS plugin
as well as a standalone executable DNS server that can be used to block DNS queries to
domains used by online advertisers, trackers, scammers, and crypto miners. The project
is meant to be a simpler alternative to the venerable Pi-Hole. In
particular, donutdns
is easy to run as a non-root Docker container
with little to no configuration.
[INFO] plugin/donutdns: BLOCK query (A) for www.google-analytics.com.
[INFO] plugin/donutdns: BLOCK query (A) for www-google-analytics.l.google.com.
[INFO] plugin/donutdns: BLOCK query (AAAA) for stats.wp.com.
[INFO] plugin/donutdns: BLOCK query (A) for www.googletagservices.com.
[INFO] plugin/donutdns: BLOCK query (A) for tpc.googlesyndication.com.
[INFO] plugin/donutdns: BLOCK query (A) for c.amazon-adsystem.com.
[INFO] plugin/donutdns: BLOCK query (AAAA) for static.ads-twitter.com.
The default set of blocked domains are retrieved from the source lists in sources.json. These lists are compiled and maintained by volunteers; see their respective headers for more information about terms of use and other metadata. Thank you to those who contribute to these domain block lists.
The blocking of the default set of domains can be disabled by setting DONUT_DNS_NO_DEFAULT=1
.
Additional domains can be blocked by donutdns
by setting any of the DONUT_DNS_BLOCK
,
DONUT_DNS_BLOCK_FILE
, DONUT_DNS_BLOCK_DIR
environment variables.
Likewise, domains can be explicitly allowed by setting the DONUT_DNS_ALLOW
,
DONUT_DNS_ALLOW_FILE
, DONUT_DNS_ALLOW_DIR
environment variables. The allow lists
take precedense over the block lists.
For nasty companies like Facebook with dynamic subdomains, donutdns
supports blocking
domains by suffix matching. By setting any of the DONUT_DNS_SUFFIX
, DONUT_DNS_SUFFIX_FILE
,
DONUT_DNS_SUFFIX_DIR
any query matching the given suffix(es) will be blocked.
donutdns
can be used as a CoreDNS Plugin or standalone DNS Server.
The donutdns
standalone DNS Server is written in Go.
Pre-compiled binaries are available for download from the Releases page.
Docker images are available from Docker Hub.
With the Go toolchain, donutdns
standalone can be compiled and installed in one step:
go install github.com/shoenig/donutdns@latest
The donutdns
executable uses environment variables for configuration.
Environment Variable | Description | Default |
---|---|---|
DONUT_DNS_PORT |
The port to listen on | 5301 |
DONUT_DNS_NO_DEBUG |
Disable CoreDNS debug logging | unset |
DONUT_DNS_NO_LOG |
Disable CoreDNS logging | unset |
DONUT_DNS_ALLOW |
Comma separated list of domains to NOT block | unset |
DONUT_DNS_ALLOW_FILE |
File with list of domains to NOT block | unset |
DONUT_DNS_ALLOW_DIR |
Directory with one or more files of list of domains to NOT block | unset |
DONUT_DNS_BLOCK |
Comma separated list of domains to block | unset |
DONUT_DNS_BLOCK_FILE |
File with list of domains to block | unset |
DONUT_DNS_BLOCK_DIR |
Directory with one or more files of list of domains to block | unset |
DONUT_DNS_SUFFIX |
Comma separated list of domains to block by suffix | unset |
DONUT_DNS_SUFFIX_FILE |
File with list of domains to block by suffix | unset |
DONUT_DNS_SUFFIX_DIR |
Directory with one or more files of list of domains to block by suffix | unset |
DONUT_DNS_NO_DEFAULTS |
Disable blocking of default domain block lists | unset |
DONUT_DNS_UPSTREAM_1 |
Fallback DNS Server for non-blocked queries | 1.1.1.1 |
DONUT_DNS_UPSTREAM_2 |
Fallback DNS Server for non-blocked queries | 1.0.0.1 |
DONUT_DNS_UPSTREAM_NAME |
Fallback DNS Server TLS name | cloudflare-dns.com |
The donutdns
CoreDNS plugin is configured using the donutdns
block in a standard
CoreConfig configuration file.
Minimal donutdns
plugin configuration. defaults
can be set to true
or false
to enable or disable the use of default domain block lists.
donutdns {
defaults true
}
This configuration uses block_file
to explicitly block a set of domains listed
in a file on local disk.
donutdns {
defaults false
block_file /etc/blocked-domains.txt
}
This configuration uses block
and allow
to explicitly block and allow certain
domains.
donutdns {
defaults true
block facebook.com,www.facebook.com,m.facebook.com,fb.com
allow example.com
}
When using donutdns
as a CoreDNS plugin, the fallthrough behavior must be configured
as desired using one or more other plugins. To recreate the same recursive behavior
as the standalone executable, use the forward
plugin.
forward . 1.1.1.1 1.0.0.1 {
tls_servername cloudflare-dns.com
}
The file format for block_file
or DONUT_DNS_BLOCK_FILE
is simply a newline
delimited list of domains. Empty lines and lines beginning with #
are always
ignored. All other lines are scanned with a regular expression to find the first
plausible domain name in the line. social-media.list
contains an example file for blocking facebook, instagram, and whatsapp.
# An example block list
example.com
www.example.com
Use the check
command to simulate whether a DNS query would be blocked or allowed.
Usage: donutdns check [-quiet] [-defaults] <domain>
-quiet
will suppress verbose debug logging output
-defaults
will activate the built-in block lists (which is slow)
With no configuration, donutdns
will use the built-in domain block lists
by default.
$ donutdns
Use the environment variables described above to configure things.
$ DONUT_DNS_PORT=5533 DONUT_DNS_NO_DEBUG=1 donutdns
The donutdns.service file provides an example Systemd Service Unit file for running donutdns via systemd.
● donutdns.service - Block ads, trackers, and malicioius sites using DonutDNS.
Loaded: loaded (/etc/systemd/system/donutdns.service; enabled; preset: enabled)
Active: active (running) since Sun 2023-02-12 05:09:45 UTC; 13s ago
Main PID: 8117 (donutdns)
Tasks: 17 (limit: 18269)
Memory: 36.1M (max: 42.0M available: 5.8M)
CPU: 1.428s
CGroup: /system.slice/donutdns.service
└─8117 /opt/bin/donutdns
# A minimal unit file, see donutdns.service for more.
[Unit]
Description=Block ads, trackers, and malicioius sites using DonutDNS.
[Service]
Type=simple
User=nobody
ExecStart=/opt/bin/donutdns
Environment=DONUT_DNS_PORT=53
Environment=DONUT_DNS_SUFFIX_DIR=/etc/blocklists.d
MemoryMax=128M
CPUWeight=90
[Install]
WantedBy=multi-user.target
Typically this file would be created at /etc/systemd/system/donutdns.service
.
Configure systemd to run the new service.
sudo systemctl daemon-reload # update systemd configurations
sudo systemctl enable donutdns.service # enable donutdns service in systemd
sudo systemctl start donutdns # start donutdns service in systemd
sudo systemctl status donutdns # inspect status of donutdns service in systemd
When running as non-root user, we must set CAP_NET_BIND on the donutdns binary.
sudo setcap CAP_NET_BIND_service+eip /opt/bin/donutdns
donutdns
is available from Docker Hub
This will run the donutdns
Docker container as the nobody
user, mapping traffic from port 53.
docker run --rm -p 53:5301 -u nobody shoenig/donutdns:v0.3.2
using docker driver
job "donutdns" {
datacenters = ["dc1"]
group "donut" {
network {
mode = "bridge"
port "dns" {
static = 53
to = 5301
host_network = "public"
}
}
task "dns" {
driver = "docker"
user = "nobody"
resources {
cpu = 120
memory = 64
disk = 128
}
env {
DONUT_DNS_NO_DEBUG = 1
DONUT_DNS_BLOCK_FILE = "/local/blocks.txt"
}
config {
image = "shoenig/donutdns:v0.3.2"
}
template {
destination = "local/blocks.txt"
change_mode = "restart"
perms = "644"
data = <<EOH
# [example]
example.com
www.example.com
EOH
}
}
}
}
Certain systems (looking at you RHEL/CentOS) make running a useable DNS server particularly difficult. On my homelab CentOS 9 system I had to disable ipv6 at the kernel level, disable SELinux, and disable firewalld. You may need to do something similar (ideally updating rules rather than disabling things) on your system.
Need to disable systemd-resolved
first, which binds to :53
out of the box.
sudo systemctl disable systemd-resolved
sudo systemctl stop systemd-resolved
The github.com/shoenig/donutdns
module is always improving with new features
and bug fixes. For contributing such bug fixes and new features please file an issue.
The github.com/shoenig/donutdns
module is open source under the BSD-3-Clause license.