Menu

 

scripts.rootarea.de

scripts and more

At home, I wanted to bring up two pihole docker containers: One to block ads for all devices in my home network, the other one to additionally block youtube and adult content for our kids only.

First, I just blocked some domains via the FritzBox's childlock capabilities.
But this leads to generally blocking all client requests on IPs...

So if you enable website filtering on your FritzBox all calls by IP are blocked...
This makes it impossible to watch Netflix, as the Netflix client always calls its streaming servers via IP, not DNS name :|

I was looking for a solution which allowed me to

  • block youtube and adult content on kids' devices
  • block ads on all devices
  • allow Netflix

Finally I was able to do it with two pihole docker containers.

The piholes run on docker on my gentoo server at home.
Setup and configuration of the piholes is straight forward as described on pihole's github for the docker image.
The piholes get fixed IPs, so the iptables rules always point to these fixed addresses for DNS lookups.

To route only the kids' devices to the kids' pihole, I'm routing their DNS traffic based on source MAC address.
So to achieve this task, your iptables have to support the mac matching extension (CONFIG_NETFILTER_XT_MATCH_MAC=y).
You may compile it into your kernel or load it as a module.

I realized, many android devices nowadays set google's DNS as default or secondary DNS or switch to other DNS services if they cannot reach a DNS server.
For this case I reroute these connections via my FritzBox back to my home server's piholes.

haproxy is used to distinguish between calls to the piholes' http ports and calls to my apache running on the same host.

My setup

 

Start the piholes

Set DNS to the IP of your FritzBox and the ServerIP to your docker host.
Keep the port forwardings, so docker will make the appropriate iptables entries in the chains POSTROUTING and DOCKER.

 

pihole-adults.yml
 

version: '2.4'
services:
  pihole-adults:
    image: pihole/pihole:latest
    container_name: pihole-adults
    restart: unless-stopped
    volumes:
      - pihole-etc:/etc/pihole/
      - pihole-dnsmasq.d:/etc/dnsmasq.d/
    ports:
      - "192.168.0.11:53:53/tcp"
      - "192.168.0.11:53:53/udp"
      - "192.168.0.11:67:67/udp"
      - "192.168.0.11:8080:80/tcp"
#      - "192.168.0.11:8443:443/tcp"  # not neccessary, haproxy offloads ssl here
    environment:
      TZ: "Europe/Berlin"
      DNS1: 192.168.0.1
      ServerIP: 192.168.0.11
    dns:
       - 192.168.0.1
       - 127.0.0.1
    cap_add:
      - NET_ADMIN
    networks:
      default:
        ipv4_address: 172.31.10.10

volumes:
  pihole-etc:
  pihole-dnsmasq.d:

networks:
  default:
    driver: bridge
    ipam:
      driver: default
      config:
      - subnet: 172.31.0.0/24

 

pihole-kids.yaml
 

version: '2.4'
services:
  pihole-kids:
    image: pihole/pihole:latest
    container_name: pihole-kids
    restart: unless-stopped
    volumes:
      - pihole-etc-kids:/etc/pihole/
      - pihole-dnsmasq.d-kids:/etc/dnsmasq.d/
    ports:
      - "192.168.0.11:9953:53/tcp"
      - "192.168.0.11:9953:53/udp"
      - "192.168.0.11:9967:67/udp"
      - "192.168.0.11:9980:80/tcp"
#      - "192.168.0.11:9443:443/tcp"  # not neccessary, haproxy offloads ssl here
    environment:
      TZ: "Europe/Berlin"
      DNS1: 192.168.0.1
      ServerIP: 192.168.0.11
    dns:
       - 192.168.0.1
       - 127.0.0.1
    cap_add:
      - NET_ADMIN
    networks:
      default:
        ipv4_address: 172.168.5.10

volumes:
  pihole-etc-kids:
  pihole-dnsmasq.d-kids:

networks:
  default:
    driver: bridge
    ipam:
      driver: default
      config:
      - subnet: 172.168.5.0/24

 

Save these docker-compose.ymls with your favourite editor and launch the piholes.

 

# docker-compose -y pihole-adults up -d
# docker-compose -y pihole-kids up -d

 

After starting, you should note the random generated admin passwords from the log output.

 

# docker logs pihole-adults | grep random
# docker logs pihole-kids | grep random

 

Setup haproxy

As mentioned above, I'm running a local apache which serves other stuff.
pihole needs to be available on ports 80 and 443 via the docker host's IP to serve its "blocked by pihole" site.

So at home I'm addressing my apache websites via the hostname of my server (here: thrall) and all other requests by IP should be answered by pihole-adults / pihole-kids.
My apache binds to port 8000 and haproxy does the proxying work.

Just replace thrall and 192.168.0.11 with your server's hostname / IP and (re)start your haproxy.

 

/etc/haproxy/haproxy.cfg
   global
        log /dev/log local0 info
        daemon
        maxconn 256

defaults
        mode http
        option http-server-close
        timeout connect 5000ms
        timeout client 50000ms
        timeout server 50000ms

frontend http-in
        bind :::80 v4v6
        bind :::443 v4v6 ssl crt /etc/ssl/apache2/apache.pem
        option forwardfor

    # Define hosts
        acl host_thrall hdr(host) -i thrall
        use_backend apache if host_thrall

        default_backend pihole-http

backend apache
        server thrall 127.0.0.1:8000 maxconn 32

backend pihole-http
        server thrall 192.168.0.11:8080 maxconn 32

 

pihole Admin Panels

Your pihole admin panels should now be available on

pihole-adults: http://192.168.0.11/admin
pihole-kids: http://192.168.0.11:9980/admin

 

Block Youtube and adult websites on pihole-kids

Go to the admin panel of your pihole-kids and login.

 

Youtube

Add these domains to your blacklist:

 

youtube.de
youtube.com
m.youtube.com
m.youtube.de
youtube-nocookie.com
youtu.be
ytimg.com
googlevideo.com
ggpht.com

 

Youtube regexes

Add these to your Blacklist in pihole-kids as regex:

 

.*sn-\S{4,}-\S{4,}\.googlevideo\.com
r[0-9]+---sn-.*\.googlevideo\.com$

 

Adult Websites

I found an article which points to a list hosted on github containing 11868 domains at the time of this writing.

Just add this URL

https://raw.githubusercontent.com/chadmayfield/pihole-blocklists/master/lists/pi_blocklist_porn_top1m.list

to "Settings" -> "Block Lists".

 

Gather MAC addresses of your kids' devices

Now there is everything in place to serve filtered DNS to all devices in the LAN and extra filtered DNS for our kids' devices.
But now we have to distinguish between the kids' devices and all the other ones.

This is done by MAC address filtering capability of iptables.

So first, we need to look up the MACs of the kids' devices, eg. in the networking overview of the FritzBox.
Just click on the edit button on one of the kids' devices to get further information.
The MAC address is shown, so we can simply copy+paste it.
Cancel if you are done with the device.

 

Reroute DNS traffic via iptables

We now need to add the rules to our docker host's iptables NAT table.
Before any other actions are done by iptables, the packages run through the PREROUTING table.
We fetch packages for the ports 67 and 53 and send them to the correct piholes based on the latter gathered MAC addresses.

To easily add / remove those rules, I wrote a simple script which is loaded at boot time on my home server.
So if you're on gentoo, you could save this script to eg. /etc/local.d/piholes_iptables_rules.start

This script also adds routing of packages which request popular DNS servers like google.dns or opendns.net.
These rules still don't have any effect, as no device in the LAN ask our home server about these DNS server IPs.
We will enforce this in the next step.

Alter the script with your values.

MACS = MACs of your kids' devices you collected before, space separated
DOCKER_HOST_IP = your docker host / home server ip
ETH = your ethernet device on your docker host / home server

Save it with your favourite text editor, make it executable and run it as root.

 

# chmod 750 ./piholes_iptables_rules.sh
# ./piholes_iptables_rules.sh

 

If you made any mistake, you could remove these rules again by calling the script with the "-D" switch before you make any changes to the script!
Altered rules will not be removed as they have to be in the same state as commited before.

  # ./piholes_iptables_rules.sh -D

 

You should now have a bunch of new rules in your NAT table.

  # iptables -t nat -L -n -v

 

piholes_iptables_rules.sh
 

#!/bin/env bash

### just for info :)

# Kid1
# FireTV                A0:XX:XX:XX:XX:XX
# Smartphone            B0:XX:XX:XX:XX:XX

# Kid2
# Tablet                C0:XX:XX:XX:XX:XX

###

MACS="A0:XX:XX:XX:XX:XX B0:XX:XX:XX:XX:XX C0:XX:XX:XX:XX:XX"
PIHOLEIP_ADULTS="172.31.10.10"
PIHOLEIP_KIDS="172.168.5.10"
DOCKER_HOST_IP="192.168.0.11"
IPT="iptables"
ETH="enp5s0"

# have to be rerouted to host from fritzbox
POPULAR_DNS="208.67.222.222 209.244.0.0 8.8.4.4 8.8.8.8"

##################################################################################
##################################################################################

ACTION="-I"

if [ "$1" = "-D" ] ; then
        ACTION="-D"
fi

#
## reroute to pihole-adults
#
# popular DNSses

for prot in tcp udp ; do
    $IPT -t nat $ACTION POSTROUTING -o $ETH -s $PIHOLEIP_ADULTS -m $prot -p $prot --sport 53 -j MASQUERADE
done
for dns in $POPULAR_DNS ; do
    for prot in tcp udp ; do
        $IPT -t nat $ACTION PREROUTING -i $ETH -d $dns -m $prot -p $prot --dport 53 -j DNAT --to-destination $PIHOLEIP_ADULTS:53
    done
done

# all other DNS requests
for prot in tcp udp ; do
        $IPT -t nat $ACTION PREROUTING -i $ETH -d $DOCKER_HOST_IP -m $prot -p $prot --dport 53 -j DNAT --to-destination $PIHOLEIP_ADULTS:53
done

#
## reroute to pihole-kids
#
# popular DNSses
for prot in tcp udp ; do
    $IPT -t nat $ACTION POSTROUTING -o $ETH -s $PIHOLEIP_KIDS -m $prot -p $prot --sport 53 -j MASQUERADE
done
for mac in $MACS ; do
    for dns in $POPULAR_DNS ; do
        for prot in tcp udp ; do
            $IPT -t nat $ACTION PREROUTING -i $ETH -d $dns -m mac --mac-source $mac -p $prot --dport 53 -j DNAT --to-destination $PIHOLEIP_KIDS:53
        done
    done
done

# all other DNS requests
for mac in $MACS ; do
    for prot in tcp udp ; do
        $IPT -t nat $ACTION PREROUTING -i $ETH -m mac --mac-source $mac -p $prot -d $DOCKER_HOST_IP --dport 53 -j DNAT --to-destination $PIHOLEIP_KIDS:53
    done
done

# BOOTPS
for mac in $MACS ; do
    $IPT -t nat $ACTION PREROUTING -i $ETH -m mac --mac-source $mac -p udp -d $DOCKER_HOST_IP --dport 67 -j DNAT --to-destination $PIHOLEIP_KIDS:67
done

 

Reroute google.dns / popular DNS servers to your piholes

You will find many website with the specific topic "block google dns on fritzbox" which reroute the requests to a non-existant IP in your network.
Yes, this works, but will ridiculously increase website loading time in your browsers at home.
The browsers will wait for a timout of their requests ... which could take a very long time, just try it :)

So we point those DNS servers back to our home server to send them to our piholes.
We already defined the corresponding iptables rules in the previous step.

I block these DNS servers for direct use by our LAN devices:

 

# primary google.dns
IPv4-Network: 8.8.8.8
Subnet: 255.255.255.255
Gateway: 192.168.0.11

# secondary google.dns
IPv4-Network: 8.8.4.4
Subnet: 255.255.255.255
Gateway: 192.168.0.11

# opendns.com
IPv4-Network: 208.67.222.222
Subnet: 255.255.255.255
Gateway: 192.168.0.11

# Level3.net
IPv4-Network: 209.244.0.0
Subnet: 255.255.255.248
Gateway: 192.168.0.11

 

Replace 192.168.0.11 with your docker host's / home server's IP.
Add them to your FritzBox's IPv4 routing tables.
Do not forget to activate the entry by clicking on the "enable" checkbox.

Open your FritzBox admin panel and go to "Network" -> "Network Settings" -> "Static Routing Table" -> "IPv4 Routing".