Vorwort

Einführung

Im Internet von heute haben wir zunehmend mit riesigen, zentralen Entitäten bzw. Firmen mit Monopolstellung zu kämpfen. Diese akkumulieren immer mehr Daten, was gelinde gesagt gefährlich werden kann. Was gäbe es da Besseres, als diesem Trend eigenhändig ein Stück weit entgegenwirken zu können und für ein wenig mehr Dezentralität und persönliche Autonomie zu sorgen?

Auf dem Kuketz-Blog und im dazugehörigen Forum gibt es bereits viele Informationen zur digitalen Selbstbestimmung. Zum Beispiel wird nahegelegt, einen vertrauenswürdigen DNS-Server zu verwenden. Das ist ein erster Schritt, jedoch ändert es nichts daran, dass wir dieser Instanz vertrauen müssen. Alle Entitäten, die wir im Internet kontaktieren (Webseiten, E-Mail etc.), bekommen meist nur einen kleineren Abriss von uns zu sehen. Der DNS-Server jedoch erfährt nahezu alle Stellen, die wir per Internet kontaktieren (Ausnahme: Anonymisierungsdienste wie Tor; VPN jedoch ausdrücklich nicht). Wir wollen uns im Folgenden nun auch dieser zentralen Stelle entledigen und uns ein Stück freier und das Internet ein Stück dezentraler machen, indem wir einfach selbst einen DNS-Server aufsetzen.

Was haben wir vor?

Dies ist eine Schritt-für-Schritt-Anleitung von der ersten Einrichtung eines Raspberry Pis bis zu dem Punkt, an welchem der Pi alle DNS-Anfragen aus unserem Netzwerk beantwortet. Dies erreichen wir, indem wir uns des üblicherweise für alle Anfragen genutzten externen DNS-Servers entledigen und diesen stattdessen einfach selbst betreiben (Unbound). Die DNS-Anfragen werden dann je nach Inhalt auf verschiedene autoritative Server (Das sind die, die auch dem "gewöhnlichen" DNS-Server die Daten liefern.) verteilt. Zudem wenden wir eine Technik an (Hyperlocal), um die Anzahl der DNS-Anfragen, die unser Heimnetz verlassen, zu reduzieren, um möglichst wenige Datenspuren zu hinterlassen. Wer es genau wissen will, findet mehr dazu in der zweiten Hälfte der Fragen & Antworten. Darüber hinaus sollen ausgehende DNS-Anfragen an Werbe-Domains geblockt werden (Pi-hole), wobei auch das unter unserer Kontrolle stehen wird und nach Belieben eingerichtet werden kann. Neben der Problemstellung ist eine grundsätzliche Erklärung gegeben und auch jeder Schritt kommentiert. Denn das Ganze soll nicht nur nachgeahmt, sondern auch verstanden werden.

Mitwirkende

Erstellt von Tschaeggaer, erste veröffentlichung im Kuketz-Forum

Übertragen in mdBook von Tealk

Raspberry Pi einrichten

Vorab

Ich empfehle den Betrieb des Raspberry Pis mit Netzwerkkabel.

Die Anleitung wurde für Linux erstellt, auf anderen Systemen sind Anpassungen höchstens in der ersten Hälfte dieses Beitrags nötig (Kommandos mit vorangestelltem $).

Am Ende jedes Beitrags ist ein größerer Schritt abgeschlossen und der Raspberry Pi in diesem Stadium funktionsfähig. An diesen Stellen sind Unterbrechungen passend – egal ob für längere Pausen oder weil man nur Teile der Anleitung als Vorlage für ein anderes Projekt heranzieht.

SD-Karte vorbereiten

Zuerst holen wir uns das aktuelle Debian stable für den Pi; derzeit (bis 2023) Debian 11 Bullseye. Klickt den Link, sucht dort nach eurem Pi-Modell und ladet die zugehörigen drei Dateien hinter den Links xz-compressed image, sha256sum und GPG-signed sha256sum herunter. In meinem Fall lautet einer der Dateinamen 20210823_raspi_2_bullseye.img.xz. Je nach Datum und Pi-Modell werden diese jedoch variieren – passt die Befehle bitte entsprechend an.

Hinweis: Alle Varianten des Raspberry Pi 4 erfordern dasselbe Image. Zwar gibt es in der Übersicht zwei Zeilen dazu, diese stehen jedoch nur für verschiedene getestete Geräte. Die Dateien sind dieselben. Das Gleiche gilt für die Modellreihe Raspberry Pi 0/1.

Im Downloadverzeichnis öffnen wir ein Terminal. Als Erstes importieren wir den OpenPGP-Schlüssel und überprüfen die Authentizität und Integrität der Prüfsummen.

gpg2 --keyserver keyserver.ubuntu.com --recv-keys E2F63B4353F45989
gpg --verify 20210823_raspi_2_bullseye.img.xz.sha256.asc

Wenn alles stimmt, lautet eine Zeile der Ausgabe so:

gpg: Korrekte Signatur von […]

Der Rest der Ausgabe warnt u. A., weil keine Vertrauenskette (chain of trust) von uns zum Schlüssel der signierenden Person führt. Das müssen wir an dieser Stelle so hinnehmen. Wir überprüfen noch, ob die Prüfsumme auch zu dem Image gehört und entpacken anschließend das Image.

sha256sum -c 20210823_raspi_2_bullseye.img.xz.sha256
20210823_raspi_2_bullseye.img.xz: OK
unxz 20210823_raspi_2_bullseye.img.xz

Es gilt nun herauszufinden, welche Bezeichnung die SD-Karte hat. Anhand des Befehls lsblk können wir diese ermitteln, für andere Infos kann man auch die Option -f anhängen (lsblk -f). Eine einfache Methode ist, den Befehl zweimal auszuführen: erst vor, dann nach Anstöpseln der SD-Karte. Ich gebe den Bezeichner vorsichtshalber mit /dev/sdX an, damit nicht blind kopiert wird – in meinem Fall lautet er /dev/sdc, bei euch kann das anders sein. Das Image schreiben wir anschließend auf die SD-Karte, /dev/sdX ersetzen wir entsprechend mit dem gefundenen Bezeichner. Seid wirklich vorsichtig und schaut lieber dreimal hin, was ihr bei of=… eingebt, sonst macht ihr im Zweifel das System platt.

lsblk
sudo dd bs=4M conv=fsync status=progress if=20210823_raspi_2_bullseye.img of=/dev/sdX

Nachdem der Vorgang abgeschlossen ist, bietet es sich an, die SD-Karte vorsichtshalber ab- und wieder anzustecken, um Fehlern vorzubeugen. Um später sofort eine SSH-Verbindung zum Pi aufbauen zu können, müssen wir jetzt noch ein paar Schritte am Rechner erledigen. Wir erstellen uns ein Schlüsselpaar. Dank Passwort-Manager können wir ein starkes Passwort vergeben. Die anderen Felder dürfen ruhig leer bleiben.

ssh-keygen -t ed25519 -a 100 -f ~/.ssh/id_ed25519_pidns

Wir hängen die SD-Karte temporär mit unseren gewöhnlichen Rechten am Rechner ein – an das Ersetzen von sdX denken –, …

sudo mkdir -p /media/SD
sudo mount -o uid="$(id -u $USER)" /dev/sdX1 /media/SD

… um den eben generierten öffentlichen Schlüssel in einer Datei zu hinterlegen. Zuerst die komplette Ausgabe des folgenden Befehls kopieren, …

cat ~/.ssh/id_ed25519_pidns.pub

dann die Datei bearbeiten, …

nano /media/SD/sysconf.txt

folgende Zeile suchen und wie folgt ändern (Die am Zeilenanfang entfernen!):

root_authorized_key=Kopiertes-hier-einfügen

Speichern und schließen. Bei Nano geht das, indem wir Strg+x drücken und die Nachfrage beim Speichern mit y (Englisch) oder j (Deutsch) bestätigen. Die vorigen Schritte des Einhängens und Bearbeitens dürfen natürlich auch gern über den Dateimanager erfolgen. Die SD-Karte wird wieder ausgehängt.

sudo umount /dev/sdX1

Karte abziehen, in den Pi stecken, Pi anstöpseln und los. :-)

Einrichtung Debians und des SSH-Zugangs

Im Router geben wir dem Pi per DHCP eine feste IP-Adresse. Ich wähle hier die 192.168.1.5, je nach eurer Netzwerkausgestaltung müsst ihr evtl. eine andere wählen. Auch kann ich das an dieser Stelle nicht abschließend für jeden Router erklären, da das stets verschieden einzurichten ist – Aber es gibt sicher Anleitungen zu eurem Router im Web. Bei einer Fritz!Box bspw. ist das im Webinterface unter Heimnetz → Netzwerk im Karteireiter Netzwerkverbindungen zu finden, indem man das entsprechende Gerät bearbeitet (Stift-Symbol). Dort kann die letzte (vierte) Zahl der IP-Adresse geändert und ein Haken bei Diesem Netzwerkgerät immer die gleiche IP-Adresse zuweisen gesetzt werden. Die hier eingestellte IP-Adresse müsst ihr in der Anleitung stets entsprechend ersetzen, falls ihr eine andere vergeben habt.

Damit die Adressänderung wirksam wird, kann man z. B. das Netzwerkkabel des Pis abziehen und wieder anstecken. Falls das nicht klappt, den Router und/oder Pi einfach mal neustarten.

Die SSH-Config auf unserem PC ändern wir so, dass wir schnell und einfach per SSH auf den Pi kommen, weil automatisch der passende Accountname und Schlüssel ausgewählt wird.

nano ~/.ssh/config

Folgendes fügen wir an, danach speichern und schließen:

Host 192.168.1.5
    IdentitiesOnly yes
    IdentityFile ~/.ssh/id_ed25519_pidns
    User root

Vom Pi erfragen wir nun den Fingerabdruck des Hostschlüssels. Dieser dient als eindeutiges Identifikationsmerkmal des Servers und ist ein Sicherheitsmerkmal von SSH, um Angriffe auf die Verbindung leichter erkennen zu können. Der Fingerabdruck wird auf unserem Rechner gehasht an die Datei angefügt, welche solche Kennungen sammelt.

ssh-keyscan -Ht ed25519 192.168.1.5 >> ~/.ssh/known_hosts

Wir initiieren eine SSH-Sitzung auf dem Pi.

ssh 192.168.1.5

Ab jetzt geht es auf dem Pi weiter. Als erstes stellen wir die passende Zeitzone ein.

timedatectl set-timezone Europe/Berlin

Die bei jedem Boot notwendige Zeitsynchronisation lassen wir über die NTP-Server von dismail.de vornehmen. Dazu fügen wir in einer Datei …

nano /etc/systemd/timesyncd.conf

… die IP-Adressen in die entsprechende Zeile ein (und entfernen wieder die #).

NTP=213.136.94.10 80.241.218.68 78.46.223.134

Datei speichern und schließen. Ein starkes Root-Passwort darf nicht fehlen.

passwd

Und auch der SSH-Server soll noch ein wenig abgesichert werden. Dazu bearbeiten wir eine Konfigurationsdatei …

nano /etc/ssh/sshd_config.d/custom.conf

… und fügen dort folgende Zeilen ein:

AllowStreamLocalForwarding no
AllowTcpForwarding no
AllowUsers root
ClientAliveInterval 600
PasswordAuthentication no
X11Forwarding no

Um unsere SD-Karte etwas zu schonen, stellen wir das System-Log so ein, dass es ausschließlich in den Arbeitsspeicher schreibt. Zwar sind die Logs dann nach einem Neustart verloren, jedoch erhöhen wir so die Lebenszeit unserer SD-Karte, da damit ein wesentlicher Teil der Schreibvorgänge eliminiert wird. Der Vorgang ist natürlich reversibel (einfach Datei wieder löschen, z. B. für Fehlersuche) und wir planen ohnehin nicht, den Pi oft neuzustarten. Nach dem Erstellen eines Verzeichnisses legen wir eine neue Datei an.

mkdir /etc/systemd/journald.conf.d
nano /etc/systemd/journald.conf.d/00-volatile-storage.conf

In diese kommen die notwendigen Konfigurationen.

[Journal]
Storage=volatile
RuntimeMaxUse=30

Als nächstes folgt ein vollständiges Update des Systems (Bestätigung erforderlich).

apt update && apt full-upgrade

Abschließend installieren wir noch ein Paket, welches für Lokalisierung (Anpassung von Sprache, Maßeinheiten etc.) erforderlich ist.

apt install locales

Zur Einrichtung der Lokalisierungen nehmen wir diesen Befehl

dpkg-reconfigure locales

Dort suchen wir die folgenden zwei Zeilen heraus und aktivieren diese mit der Leertaste (Aktivierung an * erkennbar).

de_DE.UTF-8 UTF-8
en_US.UTF-8 UTF-8

Mit Enter geht es weiter. Nun können wir noch eine Standard-Lokalisierung auswählen. Die obigen sind beide in Ordnung, je nachdem ob man lieber Englisch oder Deutsch hat – anvisieren und bestätigen. Nach kurzer Wartezeit noch ein abschließender Neustart und wir sind mit der Einrichtung fertig.

reboot

Disclaimer: Das ist keine Anleitung dazu, wie man einen Pi perfekt absichert. Hängt dieser hinter einem Router und es wurden keine Port-Weiterleitungen eingerichtet (Wenn du eine hättest, wüsstest du es vermutlich.), ist dies in meinen Augen jedoch hinreichend. Wichtig sind regelmäßige Updates – siehe dazu auch den Punkt Geschafft! Oder doch nicht? im Teil Unbound & Hyperlocal.

Pi-hole

Weiter geht's per SSH auf dem Pi.

ssh 192.168.1.5

Da das Image recht minimalistisch gehalten ist, müssen wir selbst ein Tool wie cURL erst manuell installieren. Darüber hinaus installiere ich hier gern noch ein Hilfswerkzeug. bash-completion vervollständigt viele Befehle, wenn man nur den Anfang eintippt und dann Tab drückt.

apt update && apt install curl bash-completion

Danach können wir uns das Installations-Skript für Pi-hole herunterladen.

curl -sSL "https://install.pi-hole.net" > install-pi-hole

An dieser Stelle empfehle ich, wenigstens kurz in die Datei reinzuschauen, ob etwas Sinnvolles heruntergeladen wurde.

less install-pi-hole

Mit q kann man die Betrachtung beenden. Danach folgt die eigentliche Pi-hole-Installation. Der Befehl dafür (erste Zeile) und die vorzunehmenden Einstellungen, die währenddessen vom Script erfragt werden, (restliche Zeilen) sehen so aus:

bash install-pi-hole

Upstream DNS Provider: Custom
Enter your desired upstream DNS provider(s): 5.1.66.255, 91.239.100.100
Die Blockierliste aktiviert lassen (OK)
IPv4/IPv6: beides aktiv lassen (OK)
Webinterface: On; Web Server: On; Log: On; Privacy Mode: 0 Show everything (Das ist schließlich nur für uns privat.)

Die hier gewählten DNS-Server werden vom Freifunk München und UncensoredDNS betrieben. Nach der Installation entfernen wir das Script wieder, da es seinen Dienst getan hat. Anschließend stellen wir für das Web-Interface ein sichereres Passwort ein.

rm install-pi-hole
pihole -a -p

An der Pi-hole-Konfiguration ändern wir noch eine Kleinigkeit, um die SD-Karte etwas zu schonen. Dazu bearbeiten wir diese Datei:

nano /etc/pihole/pihole-FTL.conf

und fügen dort das Folgende an:

#DNS-Anfragen x Tage speichern
MAXDBDAYS=30
#alle x Minuten pihole-FTL.db von RAM auf SD-Karte schreiben
DBINTERVAL=60

Den systemeigenen Resolver können wir deaktivieren, da wir nun unseren eigenen betreiben.

systemctl disable systemd-resolved

Aufgrund der ganzen Änderungen am System ein Neustart.

reboot

Nun machen wir über das Web-Interface weiter. Wir geben im Browser folgende Adresse ein und loggen uns unter "Login" mit dem vorhin vergebenen Passwort ein.

192.168.1.5/admin

Wir fügen dort für ordentliches Werbe-(und weiteres) Blocking ein paar Blockier-/Host-Listen hinzu. Möglich ist das unter Group Management → Adlists: Ich füge hier die Non-crossed-Liste der Blocklisten von Firebog hinzu (Selbstbeschreibung: "For when someone is usually around to whitelist falsely blocked sites"). Je nach gewünschtem Grad des (möglichen Over-)Blockings könnte man auch eine andere Liste wählen. Auf derselben Seite werden auch noch je eine striktere und eine sanftere, aber immer noch gut filternde Liste angeboten. Die Listen markieren wir allesamt und fügen sie in das Textfeld ein; als Comment bspw. firebog.net no-cross angeben. Anschließend navigieren wir noch zu Tools → Update Gravity und klicken dort auf Update, damit sich Pi-hole alle Listen herunterlädt und die Domains in die eigene Datenbank aufnimmt. Je nach Pi-Modell kann das einige Minuten dauern, also an dieser Stelle gern einen Kaffee kochen.

Auf dem Pi-hole-Dashboard sollte die Zahl der blockierten Domains mit einigen hunderttausend angegeben werden. Nun können wir den Pi auch schon als lokalen DNS-Server nutzen. Dazu öffnen wir die Router-Einstellungen und stellen beim DHCP(-Server) ein, dass alle Geräte den Pi bzw. dessen IP-Adresse (192.168.1.5) für DNS-Anfragen nutzen sollen. Das Vorgehen dabei variiert auch hier je nach Router, daher wieder nur für die Fritz!Box: Die IP-Adresse ist an zwei Stellen einzutragen. Erstens unter Heimnetz → Netzwerk → Karteireiter Netzwerkeinstellungen → weitere Einstellungen ausklappen → IPv4-Einstellungen → Lokaler DNS-Server. Dies ist der Resolver für die DHCP-Clients, also Geräte, die ihre IP-Adresse vom Router zugewiesen bekommen. Zudem zweitens unter Internet → Zugangsdaten → Karteireiter DNS-Server → Andere DNSv4-Server verwenden in beiden Feldern. Diese sind für die Anfragen der Fritz!Box selbst – und damit übrigens auch für das Gastnetz.

Für IPv6 würde ich die öffentliche Adresse wählen, welche mit fd beginnt, diese sieht z. B. so aus: fd00:0:0:0:affe:cafe:abba:fefe. Diese muss einerseits an derselben Stelle wie beim vorigen Zweitens eingetragen werden. Andererseits noch unter Heimnetz → Netzwerk → Karteireiter Netzwerkeinstellungen → weitere Einstellungen ausklappen → IPv6-Einstellungen → DNSv6-Server im Heimnetz. Zudem sollten im Regelfall Häkchen bei Router Advertisement im LAN aktiv und bei DNSv6-Server auch über Router Advertisement bekanntgeben (RFC 5006) gesetzt sein. Näher kann ich in diesem Tutorial nicht auf IPv6 eingehen, da das völlig ausufern würde.

Übrigens sind DNS-Anfragen unabhängig von der zur internen Vernetzung genutzten IP-Adresse für beide Adressversionen (v6/v4) möglich.

Unbound & Hyperlocal

Unbound

Hyperlocal ≙ DNS-Root-Zone lokal in Unbound vorhalten

Wir sind wieder per SSH mit dem Pi verbunden.

ssh 192.168.1.5

Die Installation Unbounds ist ein Kinderspiel. Zudem installieren wir noch man (steht für manual – Handbuch), um mit den Befehlen man unbound und man unbound.conf später nachschlagen zu können, wie Unbound funktioniert und konfiguriert wird. Doch nicht nur zu Unbound, sondern nahezu allem gibt es eine sog. man page, die man mit man XYZ aufrufen kann und eine gute erste Anlaufstelle für Fragen und Unklarheiten ist.

apt install unbound man

Das bejahen wir natürlich. Der Unbound-Service wird zunächst nicht starten können, da der Standard-Port 53, den Unbound verwenden will, schon durch Pi-hole belegt ist. Das und weitere Einstellungen gehen wir mit der Konfigurationsdatei an.

nano /etc/unbound/unbound.conf.d/server.conf

Eine ganze Menge soll dort hinein:

server:
	# If no logfile is specified, syslog is used
	#logfile: "/var/log/unbound/unbound.log"
	log-time-ascii: yes
	verbosity: 1

	port: 5335

	# for *native* IPv6 ISP connection you may set this to yes
	prefer-ip6: no

	# where to find root server data
	root-hints: /usr/share/dns/root.hints

	# Capitalization randomization: set to "no" on errors
	use-caps-for-id: yes

	# Perform prefetching of close to expired message cache entries
	# This only applies to domains that have been frequently queried
	prefetch: yes

	# fetch DS records earlier (DNSSEC): more cpu usage, less latency
	prefetch-key: yes

	# One thread should be sufficient, can be increased on beefy machines
	num-threads: 1

	# increase cache size to utilize more RAM
	msg-cache-size: 32m
	rrset-cache-size: 64m

	# Ensure privacy of local IP ranges
	private-address: 192.168.0.0/16
	private-address: 172.20.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


remote-control:
	control-enable: yes


# get data for all TLDs by IXFR (or AXFR) from root servers
# these are the only servers that answer an IXFR query
auth-zone:
	name: "."
	primary: 199.9.14.201       # b.root-servers.net
	primary: 192.33.4.12        # c.root-servers.net
	primary: 192.112.36.4       # g.root-servers.net
	primary: 2001:500:200::b    # b.root-servers.net
	primary: 2001:500:2::c      # c.root-servers.net
	primary: 2001:500:12::d0d   # g.root-servers.net
	fallback-enabled: yes
	for-downstream: no
	for-upstream: yes
	zonefile: /var/lib/unbound/root.zone

Wie immer: speichern und schließen. Der Teil auth-zone in der obigen Konfiguration ist dazu da, damit wir uns die jeweils erste DNS-Anfrage an die DNS-Root-Server durch Unbound sparen. Unbound muss dadurch gar nicht erst nachfragen, welche Server bspw. für .de oder .org zuständig sind, sondern hat die Daten stets parat und hält sie per Abgleich mit den Root-Servern aktuell. Nachdem wir Unbound neugestartet haben – dies sollte nun fehlerfrei klappen –, wollen wir das Ganze natürlich auch auf Funktionalität überprüfen; erst einfache DNS-Anfragen, dann DNSSEC.

systemctl restart unbound
dig kuketz-blog.de @127.0.0.1 -p 5335 +short
dig sigfail.verteiltesysteme.net @127.0.0.1 -p 5335
dig sigok.verteiltesysteme.net @127.0.0.1 -p 5335

Wenn alles funktioniert, liefert die erste dig-Anfrage nur eine IP-Adresse zurück (aktuell 185.163.119.132). Das zweite dig enthält u. A. status: SERVFAIL und das letzte status: NOERROR und flags: […] ad (d. h. DNSSEC ist für diese Anfrage valide). Nun loggen wir uns wieder im Pi-hole-Webinterface ein und stellen Unbound als DNS-Resolver im Feld Custom 3 unter Settings → DNS ein, indem wir das Häkchen setzen und ::1#5335 eintragen. Bei Custom 1 und 2 werden die Häkchen entfernt. Ganz unten speichern wir die Einstellungen (Save). Unbound und Pi-hole haben beide einen eingebauten Cache. Einer reicht jedoch aus, daher deaktivieren wir den von Pi-hole. Dazu müssen wir mehrere Dateien bearbeiten (immer anschließend speichern). Erstens:

nano /etc/pihole/setupVars.conf

Dort suchen und ändern:

CACHE_SIZE=0

Und zweitens:

nano /etc/dnsmasq.d/01-pihole.conf

Auch dort nur umändern:

cache-size=0

Eine weitere Einstellung, die uns erlaubt, das Ergebnis der von Unbound durchgeführten DNSSEC-Validierung an anfragende Clients weiterzuleiten, setzen wir in einer separaten Datei:

nano /etc/dnsmasq.d/10-pihole-extra.conf

Dort fügen wir ein:

proxy-dnssec

Abschließend noch ein Neustart.

reboot

Geschafft! Oder doch nicht?

Du hast nun die derzeit größtmögliche Selbstbestimmung und Freiheit in Bezug auf das Domain Name System erlangt. Doch sind wir jetzt für alle Zeit mit dem Projekt fertig? Nicht ganz, denn ein IT-System erfordert immer auch Pflege. Daher sollten wir uns einmal die Woche per SSH mit dem Pi verbinden und dort Updates einspielen. Das lässt sich einfach nebenbei erledigen, denn normalerweise muss hier weniger Arbeit, sondern vielmehr Zeit investiert werden, doch auch das in überschaubarem Maße: Ein wöchentliches Update sollte auch mit langsamem Pi-Modell und SD-Karte nicht mehr als wenige Minuten dauern. Die Anweisungen dafür kann man in einer Befehlszeile verketten:

apt update && apt upgrade && apt autoremove && pihole -up

Sind Updates fällig, ist während des Vorgangs noch eine Bestätigung notwendig. Es gibt auch die Möglichkeit, das Programm Unattended Upgrades zu verwenden, welches Sicherheitsupdates automatisch einspielt. Da ich jedoch ein Freund von Anwesenheit bei Updates bin, werde ich dies nicht weiter behandeln.

Fassen wir einmal zusammen, was wir erreicht haben: Unsere Geräte im Heimnetzwerk fragen nun stets unseren Raspberry Pi nach den IP-Adressen, die sich hinter Domainnamen wie www.kuketz-blog.de verbergen. Der Pi filtert zunächst Anfragen an Werbe-Domains heraus. Für alle anderen Anfragen kontaktiert unser Pi nur noch die entsprechenden autoritativen Nameserver. Davon gibt es mehrere, sodass wir nicht (mehr) von einer einzigen zentralen Instanz abhängig sind. Unbound unternimmt für uns außerdem weitere Schritte, um Anfragen nach außen zu minimieren – so zum Beispiel das Zwischenspeichern von Antworten auf DNS-Anfragen oder das Hyperlocal genannte vorhalten der Rootzonendaten.

Die komplette Einrichtung geht im Grunde locker von der Hand. Jetzt kann sich niemand mehr damit herausreden, die Installation wäre zu schwierig. Also: Richtet euch einen eigenen DNS-Server ein und macht das Internet ein Stück dezentraler, um der Machtkonzentration entgegenzuwirken.

Spannend bleibt außerdem, was aus den Bestrebungen der IETF wird, die Verschlüsselung der DNS-Anfragen an die autoritativen Server zu ermöglichen. Auch wenn die Entwicklung noch nicht abzuschätzen ist und eingeschlafen wirkt, werden wir diese verfolgen und entsprechende Schritte in die Tat umsetzen, sobald die Möglichkeit dazu besteht.

Spickzettel

Für Personen, die die Anleitung zum wiederholten Male durchführen wollen und daher die Erklärungen nicht mehr benötigen, gibt es hier den Spickzettel. Dieser beinhaltet kompakt alle Befehle und Dateiinhalte sowie die erforderlichen URLs. Längere Dateiinhalte sind zur Erkennbarkeit vorne und hinten mit ——— begrenzt, diese Trennung bitte nicht kopieren.

https://raspi.debian.net/tested-images/

gpg2 --keyserver keyserver.ubuntu.com --recv-keys E2F63B4353F45989
gpg --verify 20210823_raspi_2_bullseye.img.xz.sha256.asc

gpg: Good signature from […]

sha256sum -c 20210823_raspi_2_bullseye.img.xz.sha256

20210823_raspi_2_bullseye.img.xz: OK

unxz 20210823_raspi_2_bullseye.img.xz
lsblk
sudo dd bs=4M conv=fsync status=progress if=20210823_raspi_2_bullseye.img of=/dev/sdX
ssh-keygen -t ed25519 -a 100 -f ~/.ssh/id_ed25519_pidns
sudo mkdir -p /media/SD
sudo mount /dev/sdX1 /media/SD
cat ~/.ssh/id_ed25519_pidns.pub
sudoedit /media/SD/sysconf.txt

root_authorized_key=cat-Ausgabe-hier-einfügen

sudo umount /dev/sdX1
nano ~/.ssh/config

Host 192.168.1.5
    IdentitiesOnly yes
    IdentityFile ~/.ssh/id_ed25519_pidns
    User root

ssh-keyscan -Ht ed25519 192.168.1.5 >> ~/.ssh/known_hosts
ssh 192.168.1.5

timedatectl set-timezone Europe/Berlin

Fragen & Antworten

#Frequently Asked Questions (FAQ)

[F] Aber beim DNS-Server X ist alles mit DNS over TLS (DoT) oder DNS over HTTPS (DoH) verschlüsselt und keiner sieht meine Anfragen. Hier ist alles unverschlüsselt!

[A] Auch beim DNS-Server X sind die Anfragen nur von uns bis zum Server verschlüsselt. Dieser entschlüsselt die Anfragen und hat diese im Klartext vorliegen. Übrigens erhält er auch unverschlüsselt seine Daten von den autoritativen Servern. Die zentrale Instanz mit der kompletten Kenntnis aller Anfragen bliebe also – nämlich der DNS-Server X. Andere Entitäten dazwischen, bspw. der Internetprovider (ISP), sehen allerdings die DNS-Anfragen nicht mehr, soweit richtig. Doch kontaktieren wir direkt nach der DNS-Anfrage ohnehin meist die entsprechende Stelle. Selbst wenn dies verschlüsselt wäre, stünde die Ziel-IP-Adresse im Klartext in den Kopf-Daten der Pakete. Da können wir DNS-Anfragen verschlüsseln wie wir wollen, wir Posaunen unser Ziel anschließend sowieso raus. Der Informationsgehalt ist dabei nicht exakt derselbe, aber vergleichbar. Hier ist also kein Blumentopf zu gewinnen. Es ist derzeit auch schlicht nicht möglich, die Anfragen an die autoritativen Server zu verschlüsseln. Es gab u. A. schon eine Bestrebung in diese Richtung durch die IETF, allerdings sollten wir uns für die nahe Zukunft nicht allzu große Hoffnungen machen. Vertraulichkeit ist demzufolge nicht das beste Argument. Integrität der DNS-Daten schaffen DoT oder DoH auch nicht, da wir damit nicht überprüfen können, ob der DNS-Server überhaupt korrekte Daten losschickt, sondern nur, ob die Daten auf dem Weg zu uns nicht verändert wurden.


[F] Ein Online-DNS-Test sagt mir nach Einrichtung, ich würde einen DNS-Server meines Internet-Anbieters nutzen!?

[F] Der Test gibt den Hostnamen .dip0.t-ipconnect.de, .dyn.telefonica.de etc. oder meine eigene IP-Adresse aus, ist das korrekt?

[A] Wenn man die IP-Adresse des Tests unter die Lupe nimmt, ergibt sich die Antwort. Schaut bspw. bei nsupdate.info nach der eigenen IP-Adresse. Ist das dieselbe? Wahrscheinlich schon. Und welcher DNS-Server soll benutzt werden? Genau: Unser Unbound, welches hinter unserem Router an unserem Anschluss arbeitet und daher auch unsere IP-Adresse hat. Alles super also! Der o. g. Hostname ist nur eine Kennung, die der Provider dem eigenen Anschluss zuweist. Hinweis: Ich habe schon erlebt, dass nsupdate.info in Blocklisten vorkam und so durch Pi-hole blockiert wurde. Dann muss man diese Domain vorher per Webinterface in die Allowlist (Erlaubliste) aufnehmen.


[F] Nach dem Upgrade vom alten Tutorial (ohne komplette Neuinstallation) oder mit Raspberry Pi OS gibt es Probleme.

[A] Du hast damit die Pfade dieser Anleitung auf eigene Gefahr verlassen. Dennoch: Schau bitte zunächst, ob diese Beiträge dein Problem lösen.


[F] Ich will alles wissen!

[A] Na gut, du hast es so gewollt:

Begriffe

  • DNS Terminology
  • Resource Record (RR): Jede valide Anfrage an einen DNS-Server wird mit einem Resource Record beantwortet. Das kann eine IP-Adresse (A oder AAAA Record) sein, aber auch ein Verweis auf einen weiteren DNS-Server, der genauere Informationen liefern kann (NS), einen alternativen Namen für eine Domäne (CNAME) und noch viel mehr.
  • iterativer Resolver: ein DNS-Server, der Anfragen entgegennimmt und mit einem RR antwortet. Häufig sind das Verweise auf andere Server (NS RR), die Genaueres wissen, anstatt der gesuchten Antwort.
  • rekursiver Resolver: ein DNS-Server, der Anfragen entgegennimmt, eigenständig zu Ende bearbeitet und die vom Endgerät gesuchte Antwort (den gewünschten Resource Record) zurückliefert. Dazu müssen meist iterative Resolver befragt werden, nicht selten auch mehrere hintereinander. Der rekursive Resolver ist meist mit dem Wort DNS-Server gemeint. Name-/DNS-Server: iterativer oder rekursiver Resolver
  • autoritativer Server: iterativ arbeitender Resolver, der von sich aus Wissen über eine DNS-Zone hat, ohne andere Resolver fragen zu müssen
  • DNS-Zone: Ein Teil des hierarchisch aufgebauten DNS, implementiert als Konfiguration eines authoritativen Resolvers. Diese Konfiguration enthält das Wissen darüber, wo die Daten aller direkt untergeordneter Zonen zu finden sind. Z. B. kennen die Server der Root-Zone (.) alle Server, die für die Top-Level-Domains (TLDs: .de., .com., .org. usw.) zuständig sind. Zu jeder (inkl. nicht-vollständigen) Domäne gehört auch eine DNS-Zone.
  • Root-Server: die Server an der Spitze der Hierarchie des DNS, verantwortlich für die Root-Zone (.)

Wie muss man sich DNS-Anfragen vorstellen?

Schematisch könnte man eine übliche DNS-Anfrage z. B. nach www.kuketz-blog.de so darstellen (ein ausführlicheres Schaubild auf Wikipedia):

PC: "IP-Adresse für www.kuketz-blog.de.?"
  → rekursiver Resolver
–––
  rekursiver Resolver: "Wie ist die IP-Adresse für www.kuketz-blog.de.?"
    ← Server der Zone .: "<IP-Adresse des Servers zur Zone .de.>"
  rekursiver Resolver: "Wie ist die IP-Adresse für www.kuketz-blog.de.?"
    ← Server der Zone .de.: "<IP-Adresse des Servers zur Zone kuketz-blog.de.>"
  rekursiver Resolver: "Wie ist die IP-Adresse für www.kuketz-blog.de.?"
    ← Server der Zone kuketz-blog.de.: "123.45.67.89"
–––
rekursiver Resolver: "123.45.67.89"
  → PC

Nach dem Tutorial wird das Ganze so aussehen:

PC: "IP-Adresse für www.kuketz-blog.de.?"
  → Pi
–––
  Pi: "Wie ist die IP-Adresse für kuketz-blog.de.?"
    ← Server der Zone .de.: "<IP-Adresse des Servers zur Zone kuketz-blog.de.>"
  Pi: "Wie ist die IP-Adresse für www.kuketz-blog.de.?"
    ← Server der Zone kuketz-blog.de.: "123.45.67.89"
–––
Pi: "123.45.67.89"
  → PC

Anmerkung: Das Wissen über die Zone kuketz-blog.de. liegt aktuell auf einem Netcup-Server.

Wie wir sehen, treten in diesem Fall nur noch zwei externe Stellen auf – noch weniger Kontakt zu anderen Stellen ist derzeit technisch nicht möglich. Unser Pi bzw. das darauf laufende Programm Unbound kontaktiert also nur noch die wenigen passenden autoritativen Server direkt. Je nach Anfrage können sich die angerufenen Server unterscheiden. Somit sind wir nicht mehr von einem einzigen rekursiven Resolver abhängig, der alles von uns erfährt, sondern verteilen die Anfragen an verschiedene autoritative Server. Zudem versucht Unbound, jedem Server möglichst wenig zu verraten. Im Beispiel erkennen wir das daran, dass der Pi dem Server der Zone .de. nicht verrät, dass er auf die Subdomain www. des Kuketz-Blogs will. Als weitere Technik kommt zum Einsatz, dass der Pi DNS-Auflösungen zwischenspeichert, sogenanntes Caching. Die zu den Adressen gehörigen IP-Adressen werden dabei eine gewisse Zeit auf dem Pi vorgehalten, da es wahrscheinlich ist, dass sie zeitnah nochmals erfragt werden. So wird die Anzahl der Anfragen an nicht unter unserer Kontrolle stehende Instanzen noch weiter verringert.