DNS Sinkholing: Blocking Malware Domains at the Network Level
Stopping callbacks and bad domains before a single packet leaves

Almost every nasty thing a compromised device does starts with a DNS lookup. Malware phones home, a phishing link resolves a lookalike domain, a tracker beacons to an analytics endpoint — all of it begins with “what’s the IP for this?” Which makes DNS the single best chokepoint on your whole network. Block the name and the connection never happens; no firewall rule per IP, no deep packet inspection, no certificate gymnastics. That’s DNS sinkholing, and on a home or small-business network it’s the highest-leverage security control I run.
1 What “sinkholing” actually means
A sinkhole is just a DNS resolver that, for a curated list of bad domains,
answers with a dead end instead of the real address. Ask for
malware-c2.example.bad and instead of the attacker’s server you get back
0.0.0.0 (or NXDOMAIN), so the connection dies in the client’s network stack.
The malware can’t reach its command server; the tracker can’t beacon; the phishing
page never loads.
The mechanism is identical to what an ad-blocking resolver does — it’s the same
trick, just pointed at threat-intelligence lists rather than ad networks. The two
classic tools are Pi-hole and the more configurable Unbound, but you can build a
perfectly good sinkhole with dnsmasq and a cron job. Let me show the bones of
it.
2 A minimal sinkhole with dnsmasq
dnsmasq will read any hosts-format file and answer authoritatively for the names
in it. Point a blocklist into it and you have a sinkhole.
# /etc/dnsmasq.d/sinkhole.conf
# upstream resolvers for everything not blocked
server=9.9.9.9
server=1.1.1.1
# our curated blocklist, hosts format
addn-hosts=/etc/dnsmasq.d/blocklist.hosts
# return NXDOMAIN-ish dead end and log blocks while testing
log-queriesThe blocklist is plain hosts format — point every bad domain at the unroutable
0.0.0.0:
# /etc/dnsmasq.d/blocklist.hosts
0.0.0.0 malware-c2.example.bad
0.0.0.0 tracker.shady.example
0.0.0.0 phish-login.example.badPull a real, maintained list rather than hand-curating one. There are reputable aggregated feeds; fetch and reload on a schedule:
#!/usr/bin/env bash
set -euo pipefail
LIST=/etc/dnsmasq.d/blocklist.hosts
TMP=$(mktemp)
curl -fsSL https://example-threatfeed.invalid/hosts \
| grep -E '^0\.0\.0\.0 ' > "$TMP"
# sanity check before swapping in — never reload an empty list
if [ "$(wc -l < "$TMP")" -gt 1000 ]; then
mv "$TMP" "$LIST"
systemctl reload dnsmasq
logger -t sinkhole "blocklist updated: $(wc -l < "$LIST") entries"
else
logger -t sinkhole "refusing tiny blocklist; keeping old"
rm -f "$TMP"
fiThat sanity check is not optional. A truncated download that reloads as an empty list silently disables your protection — guard against it.
3 Make every device actually use it
A sinkhole only protects what queries it. The clean way is to hand it out via
DHCP so every device gets it automatically, then block outbound port 53 (and
DoH where you can) at the firewall so nothing bypasses your resolver by talking
to 8.8.8.8 directly.
# force all DNS through the sinkhole; reject direct external resolvers
$ sudo nft add rule inet filter forward \
ip daddr != 192.168.1.2 udp dport 53 reject
$ sudo nft add rule inet filter forward \
ip daddr != 192.168.1.2 tcp dport 53 rejectEncrypted DNS (DoH) is the awkward part: browsers that fall back to their own DoH endpoint over 443 will route around you entirely. The pragmatic answer is to block the known DoH bootstrap domains (Firefox respects a canary domain; Chrome’s behaviour depends on policy) and, on managed devices, disable browser-level DoH via policy. It’s a cat-and-mouse corner, but worth doing.
4 Watching it work
The point of logging during setup is to see the sinkhole earn its place:
$ sudo journalctl -u dnsmasq -f | grep -i config
dnsmasq: config tracker.shady.example is 0.0.0.0
dnsmasq: config malware-c2.example.bad is 0.0.0.0Each config ... is 0.0.0.0 line is a request that got stopped at the door.
After a week you’ll have a feel for what your network was quietly reaching out to
— it’s usually more, and more surprising, than you’d guess. Once you trust it,
turn the per-query logging back off; it’s noisy and a mild privacy liability to
keep forever.
5 The verdict
Worth it? For a home or small-office network, emphatically yes — a sinkhole is cheap (a Raspberry Pi will do), protects every device including the ones you can’t install software on (the smart TV, the random IoT plug), and stops whole classes of attack at the name-resolution stage. The honest limits: it’s a blocklist, so it only catches known-bad domains and a fresh attacker domain will slip through until the feed catches up; and encrypted DNS gives sophisticated malware and nosy browsers a way around it. Treat it as one strong layer, not the whole wall. This is for anyone who runs their own network and wants meaningful protection for the price of a Pi and an afternoon. Just don’t reload an empty blocklist — ask me how I know.




