DNS Ad-blocking with Blocky

2023-06-29 · 747 words · 4 minute read

I ran Pi-hole as a docker container for several years now in order to block various unwanted DNS requests in my home network. That kind of worked but I had a lot of issues that I wasn’t able to solve or at least to figure out what caused them.

A few days ago this went sideways in a way that my entire network became unusable. So I decided to get rid of Pi-hole and use the normal DNS servers provided by my ISP.

I considered using AdGuard Home but I’ve read many things about it that let me hesitate. Fortunately I stumbled accross Blocky after looking for an altenative.

Blocky is a quote “DNS proxy and ad-blocker for the local network written in Go”. It ticked several of my checkboxes:

  • Docker container available
  • Small footprint
  • Open Source Software
  • Easy to configure

So I gave it a shot and it didn’t dissapoint me!

Here’s my docker-compose.yaml snippet

 1  blocky:
 2    container_name: blocky
 3    image: spx01/blocky
 4    restart: unless-stopped
 5    hostname: blocky
 6    ports:
 7      - "53:53/tcp"
 8      - "53:53/udp"
 9      - "4000:4000/tcp"
10    environment:
11      - TZ=Europe/Berlin
12    volumes:
13      - /etc/localtime:/etc/localtime:ro
14      - /opt/docker/blocky/config.yml:/app/config.yml

And my config file (slightly redacted 😏)

 1upstream:
 2  default:
 3    - 5.9.164.112
 4    - 1.1.1.1
 5    - tcp-tls:fdns1.dismail.de:853
 6    - https://dns.digitale-gesellschaft.ch/dns-query
 7
 8upstreamTimeout: 2s
 9
10connectIPVersion: dual
11
12customDNS:
13  customTTL: 1h
14  filterUnmappedTypes: true
15  mapping:
16    router: 192.168.88.1
17    heatpump: 192.168.88.5
18    feinstaubsensor: 192.168.88.20
19    homeassistant: 192.168.88.25
20
21blocking:
22  blackLists:
23    iot:
24      - |
25        /broadlink.com.cn/
26    ads:
27      - https://s3.amazonaws.com/lists.disconnect.me/simple_ad.txt
28      - https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts
29      - http://sysctl.org/cameleon/hosts
30      - https://s3.amazonaws.com/lists.disconnect.me/simple_tracking.txt
31    special:
32      - https://raw.githubusercontent.com/StevenBlack/hosts/master/alternates/fakenews/hosts
33  clientGroupsBlock:
34    default:
35      - ads
36      - special
37      - iot
38  blockType: zeroIp
39  blockTTL: 1m
40  refreshPeriod: 4h
41  downloadTimeout: 4m
42  downloadAttempts: 5
43  downloadCooldown: 10s
44
45caching:
46  minTime: 5m
47  maxTime: 30m
48  maxItemsCount: 0
49  prefetching: true
50  prefetchExpires: 2h
51  prefetchThreshold: 5
52  prefetchMaxItemsCount: 0
53  cacheTimeNegative: 30m
54
55prometheus:
56  enable: true
57  path: /metrics
58
59minTlsServeVersion: 1.3
60bootstrapDns:
61  - tcp+udp:1.1.1.1
62  - https://1.1.1.1/dns-query
63  - upstream: https://dns.digitale-gesellschaft.ch/dns-query
64    ips:
65      - 185.95.218.42
66
67ports:
68  dns: 53
69  tls: 853
70  http: 4000
71
72log:
73  level: info
74  format: text
75  timestamp: true
76  privacy: false

This config is mostly based on the reference config file

I copy and pasted this reference config file to my config.yml and started commenting out most of the file.

My router is a Mikrotik hAP which also acts as my DHCP server. So the only thing I had to do is to set the IP of my server in the DHCP server confog as DNS server address:

1[bouni@router] /ip/dhcp-server/network> print
2Columns: ADDRESS, GATEWAY, DNS-SERVER
3# ADDRESS          GATEWAY       DNS-SERVER
4;;; defconf
50 192.168.88.0/24  192.168.88.1  192.168.88.20

Whenever a client obtains a new DHCP lease it gets this IP as DNS server and all its DNS queries are served by Blocky.

I don’t bother that Blocky does not come with an UI for its configuration, for me that even a plus. But not having nice graphs was something that needs to be solved.

On a cloud server I already run Prometheus and Grafana wanted to use that for visualisation of Blocky’s data.

To make the /metrics endpoint of blocky available for Prometheus I set up a subdomain metrics.tld.de which is a CNAME for my DDNS. Next I setup my local caddy to reverse proxy the /metrics endpoint:

 1
 2*.tld.de, tld.de {
 3  
 4  tls {
 5    dns hetzner {env.HETZNER_AUTH_API_TOKEN}
 6  }
 7
 8  @metrics host metrics.tld.de
 9  handle @metrics {
10      basicauth * {
11        prometheus $2a$14$Zkx19XLiW6VYouLHR5NmfOFU0z2GTNmpkT/5qqR7hx4IjWJPDhjvG 
12      }
13      reverse_proxy blocky:4000 
14  }
15}

As you can see I configured HTTP basic auth so that not everybody can query the data. In my Prometheus scrape config I set this up as a target using the basic auth credentials:

1scrape_configs:                                                                                                                                                                                                  
2  - job_name: 'blocky'                                                                                                                                                                                           
3    scrape_interval: 60s                                                                                                                                                                                         
4    static_configs:                                                                                                                                                                                              
5      - targets: ['metrics.tld.de']                                                                                                                                                                            
6    basic_auth:                                                                                                                                                                                                  
7      username: 'prometheus'                                                                                                                                                                                     
8      password: 'hiccup'

Blocky provides a ready to use dashboard for Grafana but that wants an API endpoint. I couldn’t figure out how to set this up properly as my Prometheus instance is not on the same network as my Blocky instance, so I edited that out and made my own version

All in all I’m very happy with Blocky. It was such a smooth experiance compared to Pi-hole that I can only recommend to switch to Blocky 😎