Hardware
- ein Computer auf dem Du Rootrechte hast
- Raspberry Pi 4 mit SSD
- USB-SATA-Adapter
- Netzteil mit mindestens 10W Leistung
- Nur für die Installation: einen Monitor und eine Tastatur am Pi
Grundsystem
Unter https://www.raspberrypi.com/software/operating-systems/ liegen angepasste Debian-Images. Wähle das schlankste 64Bit-Image aus, heute ist das https://downloads.raspberrypi.com/raspios_lite_arm64/images/raspios_lite_arm64-2025-12-04/2025-12-04-raspios-trixie-arm64-lite.img.xz
Flashe das Image auf die z.B. SSD. Stelle sicher, unter welcher environment variable (/dev/sdX) die SSD nach dem Anschluß an den Rechner zu finden ist.
# dmesg|tail
# xzcat /path/to/2025-12-04-raspios-trixie-arm64-lite.img.xz | dd of=/dev/sdX bs=64k oflag=dsync status=progress
Anschließend den Pi von der SSD booten. Schließe die SSD an einen der beiden USB2-Ports an. An USB3 bricht der Bootprozess ab.
Weitere nützliche Pakete installieren:
$ sudo apt update && apt upgrade
$ sudo apt install vim zsh openssh-server
SSH
Bereite auf dem lokalen Rechner eine sichere Verbindung zum Pi vor.
Generiere ein ssh key pair, das nur mit dem Pi verwendet wird,
$ ssh-keygen -f ~/.ssh/id_lokalercomputer_2_ph.pub
Schicke den zuvor erstellten public key an den Pi:
$ ssh-copy-id -i ~/.ssh/id_lokalercomputer_2_ph.pub -p 22 user@local-ip-of-Pi
Log in den Pi vom lokalen Rechner:
ssh user@local-ip-of-Pi
Editiere die /etc/ssh/sshd_config auf dem Pi:
$sudo vim /etc/ssh/sshd_config
Passe dabei Portnummern und authentification modes an:
Port $portnumber
PubkeyAuthentication yes
PasswordAuthentication no
Auf dem Pi starte ssh (nicht sshd) neu:
$sudo systemctl restart ssh
Logge dich vom lokalen Rechner ein:
ssh -i ~/.ssh/id_lokalercomputer_2_ph 'user@local-ip-of-Pi' -p $portnumber
Wenn alles wie erwartet funktioniert, passe die lokale .ssh/config an, z.B.:
# pi-hole
Host ph
Hostname <lokale IP, z.B. 192.168.2.99>
Port < >1024, z.B. 16634>
User <user>
IdentityFile ~/.ssh/id_lokalercomputer_2_ph
Pi hole
Erneuter Login in den Pi. Downloade das Installationsscript von https://install.pi-hole.net
$ wget -O basic-install.sh https://install.pi-hole.net
Lese das Skript. Wenn alles plausibel ist und keine Modifikation erforderlich ist, benutze das Skript für die Installation der Pi-hole-Umgebung:
$ sudo bash basic-install.sh
Um die Pi-ho-Installation zu administrieren, werde Mitglied der entsprechenden Gruppe:
# sudo usermod -aG pihole $USER
Update die Pi-hole-Umgebung
$ sudo pihole up
Im Router nun die DNS-Auflösung auf den Pi umleiten. In einer aktuellen FritzBox findet sich das im Pfad Internet > Zugangsdaten > DNS-Server. Dort Andere DNSv4-Server verwenden und Andere DNSv6-Server verwenden auswählen und die lokalen IPs eingeben, z.B. 192.168.2.99. Die lokale IPv6 beginnt mit fe80:. Überprüfe auf einem lokalen Gerät und rufe eine Seite auf, von der Du weißt, dass sie viele Werbebanner mitbringt. Das Pi-hole macht nur DNS-Manipulation, vom Server der aufgerufenen Webseite ausgelieferte Werbung kann das Pi-hole nicht ausfiltern. Aber von Dritten ausgelieferte Werbung sollte verschwunden sein.
Lokale DNS-Auflösung
Installiere unbound und dns-root-data
sudo apt install unbound dns-root-data
Schreibe eine config, wie unbound mit Anfragen von pi-hole umgehen soll:
vim /etc/unbound/conf.d/pi-hole.conf
server:
# If no logfile is specified, syslog is used
logfile: "/var/log/unbound/unbound.log"
log-time-ascii: yes
verbosity: 1
interface: 127.0.0.1
port: 5335
do-ip4: yes
do-udp: yes
do-tcp: yes
# May be set to no if you don't have IPv6 connectivity
do-ip6: yes
# You want to leave this to no unless you have *native* IPv6. With 6to4 and
# Terredo tunnels your web browser should favor IPv4 for the same reasons
prefer-ip6: no
# Use this only when you downloaded the list of primary root servers!
# If you use the default dns-root-data package, unbound will find it automatically
#root-hints: "/var/lib/unbound/root.hints"
# Trust glue only if it is within the server's authority
harden-glue: yes
# Require DNSSEC data for trust-anchored zones, if such data is absent, the zone becomes BOGUS
harden-dnssec-stripped: yes
# Don't use Capitalization randomization as it known to cause DNSSEC issues sometimes
# see https://discourse.pi-hole.net/t/unbound-stubby-or-dnscrypt-proxy/9378 for further details
use-caps-for-id: no
# Reduce EDNS reassembly buffer size.
# IP fragmentation is unreliable on the Internet today, and can cause
# transmission failures when large DNS messages are sent via UDP. Even
# when fragmentation does work, it may not be secure; it is theoretically
# possible to spoof parts of a fragmented DNS message, without easy
# detection at the receiving end. Recently, there was an excellent study
# >>> Defragmenting DNS - Determining the optimal maximum UDP response size for DNS <<<
# by Axel Koolhaas, and Tjeerd Slokker (https://indico.dns-oarc.net/event/36/contributions/776/)
# in collaboration with NLnet Labs explored DNS using real world data from the
# the RIPE Atlas probes and the researchers suggested different values for
# IPv4 and IPv6 and in different scenarios. They advise that servers should
# be configured to limit DNS messages sent over UDP to a size that will not
# trigger fragmentation on typical network links. DNS servers can switch
# from UDP to TCP when a DNS response is too big to fit in this limited
# buffer size. This value has also been suggested in DNS Flag Day 2020.
edns-buffer-size: 1232
# Perform prefetching of close to expired message cache entries
# This only applies to domains that have been frequently queried
prefetch: yes
# One thread should be sufficient, can be increased on beefy machines. In reality for most users running on small networks or on a single machine, it should be unnecessary to seek performance enhancement by increasing num-threads above 1.
num-threads: 1
# Ensure kernel buffer is large enough to not lose messages in traffic spikes
so-rcvbuf: 1m
# Ensure privacy of local IP ranges
private-address: 192.168.0.0/16
private-address: 169.254.0.0/16
private-address: 172.16.0.0/12
private-address: 10.0.0.0/8
private-address: fd00::/8
private-address: fe80::/10
# Ensure no reverse queries to non-public IP ranges (RFC6303 4.2)
private-address: 192.0.2.0/24
private-address: 198.51.100.0/24
private-address: 203.0.113.0/24
private-address: 255.255.255.255/32
private-address: 2001:db8::/32
Ich habe natives IPv6, daher ändere ich prefer-ip6: no zu prefer-ip6: yes.
Debian installiert routinemäßig konkurrierende Namensauflösung (openresolv). Dienst deaktivieren:
$ sudo systemctl disable --now unbound-resolvconf.service
Verhindern, dass bei einen Aufruf von resolvconf die Datei resolvconf_resolvers.conf angelegt wird:
$ sudo sed -Ei 's/^unbound_conf=/#unbound_conf=/' /etc/resolvconf.conf
$ sudo rm /etc/unbound/unbound.conf.d/resolvconf_resolvers.conf
Logging einschalten:
$ sudo mkdir -p /var/log/unbound
$ sudo touch /var/log/unbound/unbound.log
$ sudo chown unbound /var/log/unbound/unbound.log
Unbound neu starten:
$ sudo service unbound restart
Im Pi-hole-Interface die Namensauflösung umstellen: Uncheck alle Haken bei den großen DNS-Providern, bei Custom DNS servers eintragen:
127.0.0.1#5335
Achtung: Der lokale Port is 5335, nicht 5353. Funktioniert die lokale Auflösung?
dig heise.de @127.0.0.1
; <<>> DiG 9.20.15-1~deb13u1-Debian <<>> heise.de @127.0.0.1
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 11410
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;heise.de. IN A
;; ANSWER SECTION:
heise.de. 86400 IN A 193.99.144.80
;; Query time: 43 msec
;; SERVER: 127.0.0.1#53(127.0.0.1) (UDP)
;; WHEN: Mon Dec 15 10:24:09 CET 2025
;; MSG SIZE rcvd: 53
Checke das log:
$ sudo tail /var/log/pihole/pihole.log
Dec 15 10:24:09 dnsmasq[882]: query[A] heise.de from 127.0.0.1
Dec 15 10:24:09 dnsmasq[882]: forwarded heise.de to 127.0.0.1#5335
Dec 15 10:24:09 dnsmasq[882]: reply heise.de is 193.99.144.80
Manche blockierte URLs will ich doch freigeben, z.B. für Kommentarfunktionen auf blogs. Electrek.co nutzt einen Dienst namens spot.im, den ich als regex komplett freigegeben habe: Domains > List of Domains.
Zusätzliche Blocklisten können hilfreich sein. Einige finden sich auf https://github.com/RPiList/specials/blob/54876178ffa7e4d1224ac81b00bedd0040f65802/Blocklisten.md.
Done!