Werbeblocker mit Pi-Hole und Unbound DNS-Server
[2023 Rewrite]

Lesezeit: 9 Minuten

Dieser Beitrag ist eine Neuauflage eines der beliebtesten Beiträge in diesem Blog (Link), um die in den letzten dreieinhalb Jahren passierten Änderungen zu berücksichtigen. Außerdem ist dieser Beitrag etwas kompakter aufgebaut.
Dieser Artikel kann auch als Fortsetzung zu meinem Artikel über das Aufsetzen eines eigenen VPN-Servers betrachtet werden.

Ziel der Übung ist es, einen Ubuntu Server 22.04 so zu konfigurieren, dass er einerseits DNS-Anfragen mittels Pi-Hole filtert und damit Werbung, Spam und Malware unterbindet und andererseits als rekursiver DNS-Resolver agiert, zu diesem Zweck soll Unbound zum Einsatz kommen.

Betriebssystem

Ich gehe von einem bereits laufenden Betriebssystem aus, ob auf einem physischen Rechner wie einem Raspberry Pi oder einem Cloud Server macht dabei keinen Unterschied. Ich nehme außerdem an, root zu sein. Sollte das nicht der Fall sein, muss man sich sudo vor die allermeisten Befehle denken.

Ich aktualisiere das Betriebssystem mit apt update && apt upgrade.

Netzwerk

IP-Adressen vergeben

Sollte man einen Cloud Server verwenden, hat dieser ohnehin schon fixe IP-Adressen zugewiesen bekommen. Andernfalls macht man das wie folgt:

Seit Ubuntu 17.10 wird Netplan verwendet. Das heißt, die unter /etc/netplan/ abgelegte Konfigurationsdatei wird gelesen und für die Netzwerkkonfiguration angewendet. Bei diesem Betriebssystem heißt diese Datei 50-cloud-init.yaml und beinhaltet standardmäßig folgendes:

# This file is generated from information provided by the datasource.  Changes
# to it will not persist across an instance reboot.  To disable cloud-init's
# network configuration capabilities, write a file
# /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg with the following:
# network: {config: disabled}
network:
    ethernets:
        eth0:
            dhcp4: true
            optional: true
    version: 2

Ich ändere die Konfiguration entsprechend meiner Wünsche. Sie sieht dann in etwa so aus:

# This file is generated from information provided by the datasource.  Changes
# to it will not persist across an instance reboot.  To disable cloud-init's
# network configuration capabilities, write a file
# /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg with the following:
# network: {config: disabled}
network:
    ethernets:
        eth0:
          dhcp4: no
          dhcp6: no
          accept-ra: true
          addresses:
            - 192.0.2.53/24
            - 2001:db8::53/64
          gateway4: 192.0.2.1
          gateway6: 2001:db8::1
          nameservers:
            addresses:
             - 9.9.9.10
             - 2620:fe::10
    version: 2

Ich habe die echten IP-Adressen durch welche, die zu Dokumentationszwecken gedacht sind, ersetzt. 192.0.2.1 bzw. 2001:db8::1 wäre also mein Router.

Laut der Info in der Datei überstehen Änderungen darin keinen Reboot. Ich befolge die Anleitung und erstelle die Datei 99-disable-network-config.cfg und befülle sie danach mit dem Text network: {config: disabled}:

nano /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg
network: {config: disabled}
netplan apply

Pi-Hole

Pi-Hole ist ein DNS-Server mit integrierter Filterung der Anfragen anhand von Blocklists. Das heißt, dass DNS-Anfragen für Domains, die bekannterweise Werbung, Spam oder Malware verbreiten, garnicht weitergeleitet werden.

Installation

Ich lade also Pi-Hole herunter und starte die Installation, dafür reicht folgender Befehl:

curl -sSL https://install.pi-hole.net | bash

Ich wähle zu Anfang immer die Standardeinstellungen.

Bei der Auswahl der Interfaces wähle ich wg0, da es sich bei meinem Server um einen Wireguard-Server handelt und dieser von WireGuard-Clients als DNS-Server genutzt werden soll. Grundsätzlich ist an dieser Stelle das interface zu wählen, das sich im lokalen Netzwerk befindet.

Die folgenden Dialoge werden alle mit Ok beantwortet.
Ist die Installation fertig, erscheint dieser Dialog mit dem initialen Passwort für die Admin-Weboberfläche.

Jetzt ändere ich noch das Passwort auf ein längeres und sichereres mit pihole -a -p.

Für den Moment bin ich mit Pi-Hole fertig, sobald Unbound läuft, muss ich hier noch ein paar Änderungen vornehmen


Unbound

Wie eingangs erwähnt, möchte ich Pi-Hole als Werbeblocker und Unbound als DNS-Server benutzen. Hierzu ein kleiner Exkurs, wie DNS im Prinzip funktioniert und warum ich Unbound verwenden möchte:

Angenommen, man tippt die URL https://blog.resch.cloud in seinen Browser, so fragt dieser das Betriebssystem, ob es die Domain kennt, also weiß, was die IP-Adressen dahinter sind. Kennt das Betriebssystem die Adressen nicht, so fragt es den dort eingestellten DNS-Server (z.B. den Router). Kennt auch dieser die Antwort nicht, fragt er den in ihm eingetragenen Server, z.B. Google (8.8.8.8 bzw. 2001:4860:4860::8888) und so weiter und so fort.

Angenommen, niemand kennt bis dahin die Domain, so endet die Anfrage bei einem der Root-Server. Dieser gibt zur Antwort, wer die Top-Level-Domain .cloud verwaltet. Also wird dann dieser Server befragt, und der weiß, welcher Nameserver .resch.cloud verwaltet und dieser kennt dann die ganze Antwort, also die IP-Adressen von blog.resch.cloud.

Der Haken an dieser Sache ist, dass alle diese Server und deren Besitzer:innen dann bescheid wissen, wer wann von wo welche Adressen aufruft.

Unbound ist ein Stück Software, das direkt die Root-Server befragt und damit alle Zwischenschritte überspringt. Somit wissen nur diejenigen über meine Anfragen bescheid, die auch tatsächlich eine Antwort darauf geben können.

Installation

Mit folgendem Befehl lade und installiere ich Unbound. Falls am Ende der Installation ein Fehler auftritt, liegt das wahrscheinlich an der noch fehlenden Konfiguration, was in Kürze behoben wird.

apt install unbound

Konfiguration

Ich erstelle eine Konfigurationsdatei mit nano /etc/unbound/unbound.conf.d/pi-hole.conf entsprechend dieser Anleitung und passe zwei Zeilen an, da mein Server native IPv6-Konnektivität hat:

do-ip6: yes
prefer-ip6: yes

Außerdem füge ich nach interface: 127.0.0.1 noch eine Zeile mit interface: ::1 hinzu, damit Unbound auch auf IPv6 erreichbar ist.

Ich starte den Dienst mit service unbound start.

Falls das fehlschlägt, sind in der Konfiguration wahrscheinlich noch Fehler, die man mit unbound-checkconf finden kann.

Ich teste mit dig nasa.gov @127.0.0.1 -p 5335 und dig nasa.gov @::1 -p 5335 ob unbound läuft.

root@srv1-fsn1:~# dig nasa.gov @127.0.0.1 -p 5335

; <<>> DiG 9.18.12-0ubuntu0.22.04.2-Ubuntu <<>> nasa.gov @127.0.0.1 -p 5335
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 60343
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;nasa.gov.                      IN      A

;; ANSWER SECTION:
nasa.gov.               574     IN      A       52.0.14.116
nasa.gov.               574     IN      A       23.22.39.120

;; Query time: 0 msec
;; SERVER: 127.0.0.1#5335(127.0.0.1) (UDP)
;; WHEN: Thu Aug 30 21:01:45 UTC 2023
;; MSG SIZE  rcvd: 69

root@srv1-fsn1:~# dig nasa.gov @::1 -p 5335

; <<>> DiG 9.18.12-0ubuntu0.22.04.2-Ubuntu <<>> nasa.gov @::1 -p 5335
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 7712
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;nasa.gov.                      IN      A

;; ANSWER SECTION:
nasa.gov.               566     IN      A       23.22.39.120
nasa.gov.               566     IN      A       52.0.14.116

;; Query time: 0 msec
;; SERVER: ::1#5335(::1) (UDP)
;; WHEN: Thu Aug 30 21:01:53 UTC 2023
;; MSG SIZE  rcvd: 69

DNSSEC

DNSSEC ist ein Mittel, um die Echtheit von DNS-Daten zu gewährleisten.

Eine ausführlichere Erklärung abgeben zu können, möchte ich mir nicht anmaßen, daher folgendes Video:

Abschließend teste ich die Validierung durch DNSSEC mit den folgenden Befehlen. Der erste soll den Status „SERVFAIL“ zurückgeben, der zweite „NOERROR„. Das ganze wiederhole ich per IPv6.

dig sigfail.verteiltesysteme.net @127.0.0.1 -p 5335
dig sigok.verteiltesysteme.net @127.0.0.1 -p 5335
dig sigfail.verteiltesysteme.net @::1 -p 5335
dig sigok.verteiltesysteme.net @::1 -p 5335

Pi-Hole

Jetzt muss Pi-Hole noch seine Anfragen an Unbound richten, daher werde ich die Konfiguration jetzt abschließen.

Konfiguration

Ich rufe die Weboberfläche von Pi-Hole unter http://ip/admin auf und melde mich an.

Unter Settings -> DNS deaktiviere ich alle vorgegebenen Anbieter und trage die IP-Adressen und Ports von Unbound ein, also die localhost-Adressen und den Port 5335.

Außerdem aktiviere ich Respond only on interface wg0, da wg0 mein LAN-Interface ist.

Die Option Use DNSSEC lasse ich deaktiviert, nachdem das Unbound ohnehin macht.

Abschließend starte ich Pi-Hole neu: pihole restartdns

Damit der Server auch DNS-Anfragen akzeptiert muss die Firewall entsprechend konfiguriert werden:

ufw allow 53/udp
ufw allow 53/tcp
ufw allow ssh
ufw allow http
ufw enable

Pi-Hole müsste jetzt aus dem gesamten Netzwerk per IPv4 und IPv6 über den Standardport als DNS-Server nutzbar sein. Ich teste das von meinem Windows-Rechner:

C:\Users\m.resch>nslookup resch.cloud 172.16.42.2
Server:  pi.hole
Address:  172.16.42.2

Nicht autorisierende Antwort:
Name:    resch.cloud
Addresses:  2a01:4f8:d0a:11f4::2
          188.40.30.46


C:\Users\m.resch>nslookup resch.cloud 2a01:xxxx:xxxx:xxxx:42::2
Server:  pi.hole
Address:  2a01:xxxx:xxxx:xxxx:42::2

Nicht autorisierende Antwort:
Name:    resch.cloud
Addresses:  2a01:4f8:d0a:11f4::2
          188.40.30.46


C:\Users\m.resch>

VPN-Server

Für den Fall, dass du diese Installation gerade auf deinem WireGuard-Server vorgenommen hast, muss die Konfiguration auf den Clients angepasst und die Adressen bei DNS auf die Adressen des WireGuard-Servers geändert werden:

[Interface]
PrivateKey = <Kpriv Server>
# Eindeutige Adressen aus den zuvor definierten Adressbereichen:
Address = 172.16.42.101/24, <IPV6_PREFIX>::42:101/112
DNS = 172.16.42.2/24, <IPV6_PREFIX>::42:2/112

[Peer]
# Public Key des Servers:
PublicKey = <Kpub Server>
AllowedIPs = 0.0.0.0/0, ::/0
# Öffentliche Adresse oder Hostname und zuvor definierter Port des Servers:
Endpoint = <SERVER-IP>:51234
PersistentKeepalive = 15

Pi-Hole-Update

Pihole lässt sich mit sudo pihole -up aktualisieren.

OS-Update

sudo apt update && sudo apt upgrade
sudo reboot
sudo ufw allow 1022/tcp
sudo apt install update-manager-core
sudo do-release-upgrade
sudo reboot
sudo ufw delete allow 1022/tcp


Wenn du Feedback loswerden möchtest, nutze bitte das Kontaktformular.

Falls dir mein Beitrag weitergeholfen hat, würde ich mich sehr über einen kleinen Kaffee freuen. Oder du nutzt einen meiner Empfehlungslinks und sparst dir damit etwas Geld: Tesla (500€ Rabatt), Hetzner Cloud (20€ Guthaben), Ufodrive (30€ Rabatt). Außerdem habe ich eine Wunschliste bei Amazon.