Network wide ad-blocking with dnsmasq

Mask and ShieldPiHole is a thing, so is AdGuard Home— these are both excellent, and work well. They’re easy. you don’t have to be a network administrator to get up and running.

I’ve been a satisfied PiHole user for about a year, but I wanted something a little cleaner. Here is what I don’t like about PiHole:

  1. It isn’t a “normal” package. Perhaps “conventional” would be a better word; You need to use their install script. This makes updating a pain, and personally I think it is a messy way of doing things.
  2. The web interface wants to install its own server, on port 80. You can change this, and I did. Things were working fine, then I updated and the web portion no longer worked because they’ve switched to Lua… so more configuration needed, or use the web server it comes with.
  3. It is essentially just a re-release of dnsmasq, with a web front end slapped on.

So, let’s talk about doing the exact same thing, with the normal dnsmasq package that your distro comes with

IMO, the special sauce of PiHole is Stephen Black’s hosts list. This is what PH uses out of the box, to block ads, trackers and other malicious sites. Available on github here: https://github.com/StevenBlack/hosts

This file is laid out like a normal hosts file (0.0.0.0 somename.com) and we need to change that to something dnsmasq will understand. Dnsmasq needs it written like this, address=/somename.com/0.0.0.0

We can do that with a simple script. In my case, I wrote one which will grab the list for me, format it for dnsmasq and then put it in the dnsmasq.d config directory. Note, this does mean you’ll need to run with sudo, or do this in a way that you’re putting it in with the correct permission to do so.

#!/bin/bash

BLOCKLIST_URL="https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts"
BLOCKLIST_FILE="/tmp/stephenblack_hosts"
OUTPUT_FILE="/etc/dnsmasq.d/100_stephenblack.conf"

# Download, process, and create dnsmasq config
wget -q -O "$BLOCKLIST_FILE" "$BLOCKLIST_URL" && \
awk '!/^#/ && NF > 1 {print "address=/" $2 "/0.0.0.0"}' "$BLOCKLIST_FILE" > "$OUTPUT_FILE" && \
systemctl restart dnsmasq && \
echo "Blocklist update and dnsmasq configuration complete!" || \
{ echo "Error occurred."; exit 1; }

Now, to get this to work, you’ll have to edit /etc/dnsmasq.conf and comment or add conf-dir=/etc/dnsmasq.d This is a massive file, so use search in your editor. Because the file is so large, make yourself a different file in dnsmasq.d called 99_custom.conf and we can put DNS related stuff in there. Here is mine, it has most of what one might want to play with dns-wise.

# Custom Configuration file for dnsmasq.
# ---------------------------------------
# These are the most relevant, DNS related options.
# All DHCP related options are in /etc/dnsmasq.conf

# To set upstream servers here; in case resolv.conf changes
no-resolv
server=1.1.1.1
server=9.9.9.9

# If you don't want dnsmasq to poll /etc/resolv.conf for changes
#no-poll

# Never forward plain names (without a dot or domain part)
domain-needed
# Never forward addresses in the non-routed address spaces.
bogus-priv

# Uncomment these to enable DNSSEC validation and caching:
# (Requires dnsmasq to be built with DNSSEC option.)
#conf-file=%%PREFIX%%/share/dnsmasq/trust-anchors.conf
#dnssec

# Replies not DNSSEC signed may be legitimate. Because the domain
# is unsigned, or may be forgeries. Dnsmasq can check unsigned replies.
#dnssec-check-unsigned

# Change this line if you want dns to get its upstream servers from
# somewhere other that /etc/resolv.conf
#resolv-file=

# Use upstream DNS server in order, or any available.
#strict-order

# Add other name servers here, (if non-public domains).
#server=/localnet/192.168.0.1

# Add local-only domains here, queries in these domains are answered
# from /etc/hosts or DHCP only.
local=/lan/

# Add domains which you want to force to an IP address here.
# This is also how ad-blocking works. (point @ 0.0.0.0)
#address=/double-click.net/127.0.0.1

# Run dnsmasq as...
#user=
#group=

# Use specific network interface, bind to LAN (only) if doing NAT.
# You don't want to make your DNS avail to the public internet.
#bind-interfaces

# Set the cache size here. Default is 100, max is 10000
cache-size=10000

# If you want to disable negative caching (non-working names)
#no-negcache

# May serve potentially stale date, you can set a custom time-to-live
local-ttl=900

# For debugging purposes, log all queries (will use many MB in a day)
#log-queries

# Good idea if you're passing out this DNS server directly to clients
addn-hosts=/etc/hosts

# Option to disable ipv6, shouldn't need to enable
#no-ipv6

I’ve got no-resolv set here, because if you tell your router to hand out this machine for DNS then it’ll get only itself as a source and well, you won’t have working DNS. So either keep no-resolv and set your upstream servers in this custom file, or make sure you’re not using anything which is going to overwrite your resolv.conf entries.

For those interested, here’s how you could deal with that:

Adding dns-nameservers 1.1.1.1 9.9.9.9 to /etc/network/interfaces (if you’re using ifupdown)

Putting supersede domain-name-servers 1.1.1.1, 9.9.9.9; into your /etc/dhcp/dhclient.conf file, should you be using dhclient for a dynamically assigned address. Good idea to do this, if you use any NICs with DHCP unless you told dnsmasq to ignore resolv.conf.

And well, I think that’s about it. The last step is going into your router, setting the machine /w dnsmasq as the DNS server… and of course, adding any names you want/need to resolve on your LAN to the DNS server’s /etc/hosts file.

Enjoy!

© 2025 LostGeek.NET - All Rights Reserved. Powered by ClassicPress, NGINX, Debian GNU/Linux.