Prototyping an AdGuard Blocker

Jon Brookes

2025-01-03

I’m looking at creating a containerised solution that offers customisable block / allow lists using DNS for small networks.

Having worked in this zone before my interest was piqued when a friend a colleague cam to me with an idea for a home / small office system to filter internet traffic for all the devices on the network.

Pi-Hole and AdGuard are possible candidates to assist in some of these requirements. So first off, I set up a lab to see how AdGuard works and if it can do what we need of it.

docker-compose.yml :

name: adguard-v1
services:
    adguardhome:
        container_name: adguardhome
        restart: unless-stopped
        volumes:
            - ./workdir:/opt/adguardhome/work
            - ./confdir:/opt/adguardhome/conf
        ports:
            - 53:53/tcp
            - 53:53/udp
            - 67:67/udp
           #- 68:68/udp
            - 80:80/tcp
            - 443:443/tcp
            - 443:443/udp
            - 3000:3000/tcp
            - 853:853/tcp
            - 784:784/udp
            - 853:853/udp
            - 8853:8853/udp
            - 5443:5443/tcp
            - 5443:5443/udp
        image: adguard/adguardhome

I needed to comment out port 68 used for DHCP clients, as this is not required and clashes what that already running on this VM.

Likely many more of these port exposures may be removed and more over the rmaining ones bound to a specific interfaces if not disabled in some way entirely.

Digging deeper, in confdir/AdGuardHome.yaml of the container config there is to found the following :

dns:
  bind_hosts:
    - 0.0.0.0
  port: 53

where dns is set to listen on all interfaces and port 53, so as others on reddit report that other ports for https and http can be changed this is possibly how adguard could be changed for this to work for port mappings to its DNS daemon.

However, lets not get too far ahead of ourselves. Running this on a simple docker host in a test lab, this is me testing AdGuard with all its default settings - first I dig cloudflare, unfiltered dns for youtube, which works


dig @1.1.1.1 www.youtube.com

; <<>> DiG 9.18.28-0ubuntu0.24.04.1-Ubuntu <<>> @1.1.1.1 www.youtube.com

.....

;; Query time: 15 msec
;; SERVER: 1.1.1.1#53(1.1.1.1) (UDP)
;; WHEN: Fri Jan 03 15:11:49 GMT 2025
;; MSG SIZE  rcvd: 318

which is first to prove I have DNS resolution working so next I try it with my local adguard on a client that is super restricted :

dig @192.168.88.10 www.youtube.com

; <<>> DiG 9.18.28-0ubuntu0.24.04.1-Ubuntu <<>> @192.168.88.10 www.youtube.com

.......

;; QUESTION SECTION:
;www.youtube.com.		IN	A

;; ANSWER SECTION:
www.youtube.com.	10	IN	A	0.0.0.0

so this would not be able to get to YouTube and would return the address of 0.0.00 to the user

then I change my AdGuard client config to allow this user to get to youtube :

dig @192.168.88.10 www.youtube.com

; <<>> DiG 9.18.28-0ubuntu0.24.04.1-Ubuntu <<>> @192.168.88.10 www.youtube.com

.....

;www.youtube.com.		IN	A

;; ANSWER SECTION:
www.youtube.com.	300	IN	CNAME	youtube-ui.l.google.com.
youtube-ui.l.google.com. 282	IN	A	142.250.180.14

....

so adguard is relatively easy to get running in docker, easy to add / edit clients easy to block a whole array of services like youtube, betfair, amazon

AdGuard there for looks like a good candidate for us.

There is an api for adguard at its git repo so in theory it is programable which is what we need where it suggest running the OpenAPI specification from this directory using yarn which I did and can browse the JSON spec more conffortably that grokking its JSON.

I confirmed also checked if openwrt is in theory capable of capturing DNS from specific IP or MAC addresses and sending these to a custom DNS server. As I’ve done this and similar with IPTables in the past it would be highly unlikely not to be the case in Netfilter.

OpenWRT however uses its own Unified Configuration Interface UCI for most if not all of tis configuration through this command line interface. So I was hoping to use something like

uci set firewall.@rule[-1].name="Redirect DNS for $SPECIFIC_MAC"
uci set firewall.@rule[-1].src="*"
uci set firewall.@rule[-1].proto="tcp udp"
uci set firewall.@rule[-1].dest_port="53"
uci set firewall.@rule[-1].target="DNAT"
uci set firewall.@rule[-1].dest_ip="$CUSTOM_DNS"
uci set firewall.@rule[-1].extra="--mac-source $SPECIFIC_MAC"
**# Commit and restart firewall**
uci commit firewall
/etc/init.d/firewall restart

Which will need tested in my lab a this has come fresh from an AI served to me by copilot so it could be hallucination.

So, pending more tests to prove the above we could have a working prototype.

🚀