oDroid XU4 / Raspberry Pi: PiHole mit DNS-over-TLS in Docker
I have translated the article for the official odroidmagazine to english: https://magazine.odroid.com/article/pihole/
Seitdem Docker den Support für den Raspberry Pi und somit sämtliche ARM-Prozessoren aufgenommen hat, ist es ein leichtes Software auf unseren geliebten günstigen Single-Board-Computern zu deployen. So kam es, dass ich vor kurzem auf das DNS-over-TLS (DoT) Docker Image von qdm12 auf GitHub gestoßen bin. Ein DNS-„Proxy“, der DNS Anfragen annimmt und sie verschlüsselt bei Google (8.8.8.8), CloudFlare (1.1.1.1) oder anderen DoT-Servern abfragt. Dies ließ sich auch ratzfratz auf meinem oDroid XU4 und Raspberry Pi 3 einrichten (zwei Geräte, dass falls mal ein Gerät ausfällt, das Internet zuhause problemlos funktioniert). Kurz noch die DNS-Server der Fritzbox auf die beiden IPs der Geräte eingestellt und schon waren die DNS-Abfragen meines Heimnetzes verschlüsselt und gingen nicht mehr im Klartext durch meine Leitung. Während ich mich mit dem Thema DNS beschäftigte, bin ich dann auch noch auf PiHole (Docker Image) gestoßen. Ein DNS-Server, der DNS-Anfragen beantwortet, außer sie gehen an Werbe-Server. Somit kann man praktisch auf unterster Ebene Ad- und Tracking-Blocking bewerkstelligen, z.B. auch für Geräte wo sich keine AdBlocker installieren lassen. Beides kombiniert, PiHole mit DNS-over-TLS, ergibt, durch das Deployment auf zwei Geräten, ein ausfallsicheres, verschlüsseltes, werbefreies und trackingfreies DNS-Setup für die ganze Familie. In diesem Beitrag möchte ich einmal kurz erklären wie man sich dieses aufbaut. Dieses Tutorial richtet sowohl an mein Stammpublikum, Fans vom oDroid XU4, als auch Raspberry Pi Benutzer, da das Setup identisch ist.
Docker installieren
Die Installation von Docker und Docker-Compose ist kinderleicht. Öffnet eine SSH-Verbindung und gebt folgende Befehle ein:
1 2 3 4 5 6 7 8 9 10 11 12 13 | sudo su # Als root anmelden cd ~ # Home-Dir öffnen curl -fsSL https://get.docker.com -o get-docker.sh # Docker Paketquellen-Import von Docker, Inc. sh get-docker.sh # Paketquellen importieren apt install docker-ce python3-pip # Docker und pip3 installieren systemctl enable docker # Docker beim Systemstart starten systemctl start docker # Docker jetzt starten pip3 install docker-compose # Docker-Compose installieren |
Docker-Compose ist für die Orchestrierung von mehreren Docker-Containern zuständig und vereinfacht diese stark.
PiHole und DoT-Proxy installieren
Haben wir Docker und Docker-Compose installiert, können wir uns auch schon an’s Eingemachte begeben. Wir werden einen PiHole und zwei DNS-over-TLS Server starten. Ich habe die DoT-Server auf verschiedene DNS-Upstream Server gerichtet, sodass einer bei Cloudflare die Anfragen stellt und der andere beim Google Public DNS. Beide haben eine Verfügbarkeit zwischen 99% und 99,99%. Solange kein DNS-Resolver eine Uptime von 100% gewährleisten kann, würde ich also jedem empfehlen immer zwei Anbieter zu nutzen. Das Docker-Image von qdm12 bietet neben beiden erwähnten auch noch quad9, quadrant und cleanbrowsing an. Ansonsten kann man sich gerne durch die entrypoint.sh wühlen und dort eigene Provider eintragen.
Wir klonen also erstmal das Git-Repository vom DoT-Server und richten diesen ein. Wenn das läuft werden wir PiHole mit in die docker-compose.yml integrieren.
1 2 3 4 5 | apt install git cd ~ && git clone https://github.com/qdm12/cloudflare-dns-server.git ./dns-server cd dns-server |
In der docker-compose.yml könnt ihr nun die Einstellungen anpassen. Unter anderem würde ich dazu raten BLOCK_MALICIOUS auf off zu stellen, da der mir nach meinem Geschmack zu viel wegblockt. VERBOSITY habe ich auf 0 gesetzt. Und bei provider habt ihr wie gesagt die Möglichkeit zwischen google, cloudflare, quad9, cleanbrowsing und quadrant zu wählen. Einen genaueren Einblick zu den Einstellmöglichkeiten findet man auf der README Seite des Git Repos. Anschließend kann man mit docker-compose up --build #optional -d um den Prozess in den Hintergrund zu schieben den Compose-Stack builden und starten. Wenn Änderungen an den Konfig-Files vorgenommen worden sind, würde ich immer den Stack mit --build starten. Ansonsten reicht docker-compose up -d zum hochfahren und docker-compose down zum herunterfahren. Wie testen wir jetzt, ob der DNS-Server funktioniert? Dazu gibt es das kleine Tool dig:
1 2 3 | apt install dig dig blaumedia.com @127.0.0.1 |
der Befehl sollte dann sowas zurückgeben:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | ; <<>> DiG 9.11.3-1ubuntu1.3-Ubuntu <<>> blaumedia.com @127.0.0.1 ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 42751 ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 4096 ;; QUESTION SECTION: ;blaumedia.com. IN A ;; ANSWER SECTION: blaumedia.com. 86400 IN A 159.69.38.130 ;; Query time: 100 msec ;; SERVER: 127.0.0.1#53(127.0.0.1) ;; WHEN: Wed Feb 13 18:14:59 CET 2019 ;; MSG SIZE rcvd: 58 |
Ihr habt es soweit geschafft? Sehr gut! Dann machen wir aus einem DoT-Server nun zwei und fügen noch PiHole hinzu. Der Einfachheit halber zeige ich euch erstmal mein docker-compose.yml File und werde anschließend erklären, was ich da genau mache. Mein docker-compose.yml File:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | version: '2' services: cloudflare-dns-tls: build: . restart: always image: qmcgaw/cloudflare-dns-server container_name: cloudflare-dns-tls environment: - VERBOSITY=0 - VERBOSITY_DETAILS=0 - BLOCK_MALICIOUS=off - LISTENINGPORT=53 - PROVIDER=cloudflare networks: dnsbridge: ipv4_address: 10.5.0.2 google-dns-tls: build: . restart: always image: qmcgaw/cloudflare-dns-server container_name: google-dns-tls environment: - VERBOSITY=0 - VERBOSITY_DETAILS=0 - BLOCK_MALICIOUS=off - LISTENINGPORT=53 - PROVIDER=google networks: dnsbridge: ipv4_address: 10.5.0.3 pihole: image: pihole/pihole:4.2.1 dns: - 127.0.0.1 - 192.168.1.1 ports: - "53:53/tcp" - "53:53/udp" - "80:80/tcp" - "443:443/tcp" cap_add: - NET_ADMIN networks: dnsbridge: ipv4_address: 10.5.0.4 environment: ServerIP: DEVICE_IP (192.168.1.100 z.B.) TZ: Europe/Berlin DNS1: 10.5.0.2 DNS2: 10.5.0.3 WEBPASSWORD: PIHOLE_PASSWORD volumes: - '/etc/pihole/:/etc/pihole/' - '/etc/dnsmasq.d/:/etc/dnsmasq.d/' restart: always networks: dnsbridge: driver: bridge ipam: config: - subnet: 10.5.0.0/16 gateway: 10.5.0.1 |
Es sind viele Änderungen, deshalb empfehle ich euch euer docker-compose.yml einfach mit meiner zu überschreiben. Zu aller erst solltet ihr in Zeile 47 die IP eures Gerätes angeben, unter dem PiHole erreicht werden kann. Mit ip route get 1.1.1.1 | awk '{print $NF; exit}' könnt ihr schnell herausfinden, welche dies ist (Quelle: AskUbuntu.com). Anschließend unter Zeile 51 das Passwort für das Web Interface von PiHole festlegen.
Was machen wir da nun genau? Um die beiden DoT-Server für PiHole erreichbar zu machen, erstellen wir ganz unten eine Network-Bridge. Diese sorgt dafür, dass wir den einzelnen Containern feste IP Adressen zuweisen können. Docker handhabt das normalerweise so, dass die Container sich immer untereinander mit den Container-Namen erreichen können. DNS Server wie dnsmasq von PiHole kennen aber Upstream-Server mit Domain-Namen nicht, sondern möchte selbstverständlich IP Adressen. Wie sollte er diese denn die Domains auch auflösen? Also den Container fixe IPs geben, easy. Diese legen wir in Zeile 49 und 50 als Upstream-Server für PiHole fest. In Zeile 34 und 35 legen wir die DNS-Server des Containers selber fest, die z.B. beim hochfahren der Prozesse genutzt werden. An erster Stelle muss immer 127.0.0.1 stehen, an zweiter kann ein beliebiger stehen. Ich habe dort die IP meiner Fritzbox.
In Zeile 53 und 54 mounten wir noch die Konfig-Ordner von PiHole auf unserem Hostsystem, damit diese nicht bei jedem Buildvorgang gelöscht werden und persistent bleiben. Das wäre es auch schon soweit, den Rest solltet ihr kennen oder ist selbstverständlich. Mit docker-compose up --build -d können wir nun alles builden und starten.
PiHole Ad-Lists einfügen
Das Web Interface von PiHole erreichen wir nun mit unserem Browser, indem wir einfach die IP des Raspberry Pi/oDroids aufrufen.
Mit dem Link kommt ihr nun zum Interface. Links in der Navigation „Login“ auswählen und das Passwort eingeben, welches ihr soeben in der Konfig festgelegt habt. Voilà! Testet am besten nun nochmal mit dig, ob DNS Abfragen funktionieren. Läuft alles tadellos, können wir nun Listen bei PiHole einpflegen, die zum Blockieren der Werbung zuständig sind. Ich beziehe meine Ad-Lists in der Regel ausschließlich von firebog.net. Einpflegen in PiHole könnt ihr diese dann unter Settings -> Blocklists.
Da in meinem Haushalt mehr Personen als ich das Internet nutzen, sperre ich lieber weniger als zu viele DNS-Adressen. Wenn nämlich irgendwo irgendwas aufgrund des DNS-Blackholes nicht geht, ist’s nervig für alle. Das ist am Ende aber natürlich jedem selbst überlassen. Am Ende auf Save and Update klicken und schon werden die DNS Abfragen, die in den Listen enthalten sind, blockiert. Die Listen werden nachts um 00:00 Uhr automatisch aktualisiert.
Schluss
Ihr habt nun einen PiHole Server, der seine DNS-Anfragen verschlüsselt erhält. Wenn ihr sicher seid, dass alles funktioniert, könnt ihr nun euren PiHole-Server als DNS-Server in eurem Router angeben. Wir haben hier eine Fritzbox und die Konfiguration darin sieht so aus:
Tragt dort die interne IP Adresse eures PiHole-Geräts ein und speichert die Einstellungen. Wie ihr seht, habe ich dort zwei IPs eingetragen. An erster Stelle kommt der oDroid XU4, an zweiter mein Backup-Pi3.
Tada! Absofort laufen die DNS-Abfragen des gesamten Netzwerkes gefiltert über PiHole -> DNS-over-TLS -> Cloudflare/Google/Quad9/…
Seid ihr auf Fragen oder Probleme gestoßen? Meldet euch gerne in den Kommentaren!
Kommentare (16)
Moin Dennis,
vielen Dank für die sehr coole Anleitung. Ich habe die Installation zwar auf meinem Raspberry Pi 3 B+ durchgeführt , hat aber auch auf Anhieb geklappt.
Mehr davon !
Beste Grüße,
Kolya
Hallo Dennis,
super Anleitung. Eine Frage hätte ich trotzdem dazu:
Wie kann ich prüfen, ob tatsächlich die Verbindung verschlüsselt ist?
Mit tcpdump im Container?
Mit jnettop (einfach apt install jnettop) kann man, wie ich finde, ziemlich unkompliziert die offenen Verbindungen mit den dazugehörigen Ports sehen. Tcpdump kenne ich nicht, wird aber vermutlich ähnlich wie Wireshark sein nehme ich an? Damit sollte es im Zweifel dann wohl auch gehen 🙂
Hallo,
danke für Deine Arbeit und ausführliche Anleitung. Leider hakt es bei mir schon am Anfang. Sind die Eingabezeilen heute (20-10-2019) noch aktuell?
Bereits hier wirft mir das Terminal einen Fehler zurück
>> pip3 install docker-compose
>> Command „python setup.py egg_info“ failed with error code 1 in /tmp/pip-build-b6j3aaa3/pynacl/
und trotz aller Trickserei komme ich leider nicht weiter.
Hast Du eine Idee?
Viele Grüße
Paul
Moin Paul!
Das ist noch alles aktuell, ich hab’s eben nochmal getestet. Schau mal, ob deine Build-Tools aktuell sind:
pip3 upgrade--upgrade setuptools
Hi Dennis,
kann man auch von außerhalb auf die Web Admin Oberfläche von Pi-Hole zugreifen?
Bei mir klappt das leider nur direkt vom Raspi über die Docker IP 10.5.0.4.
Danke,
Wiborgit
Moin Wiborgit!
Das sollte eigentlich per Default möglich sein. Dafür sind nämlich die Ports-Angaben im Docker-Compose File vorgesehen:
ports:
– „53:53/tcp“
– „53:53/udp“
– „80:80/tcp“
– „443:443/tcp“
Damit wird Port 80 und 443 an den Host durchgereicht, wodurch du eigentlich auch innerhalb deines Netzwerkes auf PiHole über die IP des oDroid/Raspi zugreifen können solltest. Gib mir sonst nochmal deine genaue Docker-Compose Konfig durch, dann schaue ich da mal drüber.
Hallo Dennis,
vielen Dank erstmal für die super gute Anleitung. Bzgl. der Portdefinition habe ich allerdings das Composer Build File geändert, weil meine ursprüngliche Idee war, das ich gleichzeitig einen Nextcloud Server über den Raspi integriere. Dementsprechend habe ich die Ports verbogen:
ports:
– „53:53/tcp“
– „53:53/udp“
– „6080:6080/tcp“
– „6443:6443/tcp“
Damit hatte ich die Hoffnung, das ich von extern auf PiHole zugreifen kann über die Adresse:
https://192.168.2.110:6443 (dabei ist die 192.168.2.110 die feste IP meines Raspi ist).
Mir ist inzwischen klar, das es genau dran klemmt. Ich müsste also für ein einzelnes Docker Image eine zusätzliche IP erzeugen, welche ich dann korrekt adressieren kann – allerdings habe ich gerade noch keine Idee, wie das in Docker zu machen ist.
Danke und Gruss,
Wiborgit
Noch eine Ergänzung….
bis dato bekomme ich bei der Verwendung des Ports 80 eine Fehlermeldung, das dieser Port in Gebrauch ist (bind: address already in use).
2. Ergänzung….
Habe jetzt den Prozess, der die Adresse nutzt (war der lighttpd von nextcloud) gestoppt. Dann mit dem orginalen Composer Build File nochmal durchgestartet. Jetzt kann ich über die IP vom Raspi natürlich direkt die PiHole Admin Konsole auch von extern erreichen.
Sprich meine nächste Aufgabe ist einfach nur…zusätzlich einen docker Nextcloud Service mit eigener IP (die dann auch von extern erreichbar ist) einzurichten…..
Hallo
Habe noch nicht so viele Erfahrungen mit Compose
Bei mir gehts leider auch nicht. Ich versuche das via Container Station in der QNAP zu starten.
Dort kann man ja via YML das Compose File hinzufügen.
Allerdings darf ich kein Netzwerk erstellen sondern ein vorhandenes verwenden. Kannst mir bitte kurz schreiben wie der Stringaufbau ist bei der Verwendung von einem vorhanden Netzwerk.
Name = docker0
GW = 10.0.5.1
Net = 10.0.5.0/24
DHCP = 10.0.5.2 ~ 10.0.5.254
DNS = 10.0.5.1
Danke für deine HIlfe
Moin, ich weiß leider nicht genau was du meinst. Möchtest du ein neues Netzwerk-Interface anlegen?
In deiner Anleitung erstellst du eine Netzwerkbridge
Das lässt er nicht zu. Gibts eine Möglichkeit Files hochzuladen dann könnte ich die Screenshots posten.
Auf der QNAP heißt es
Netzwerkeinstellungen (lxcbr0)
Bridge-Name docker0
Netzwerkeinstellungen (docker0)
Bridge-Name docker0
———————— YML BEGINN ——————
version: ‚2‘
services:
cloudflare-dns-tls:
build: .
restart: always
image: qmcgaw/cloudflare-dns-server
container_name: cloudflare-dns-tls
environment:
– VERBOSITY=0
– VERBOSITY_DETAILS=0
– BLOCK_MALICIOUS=off
– LISTENINGPORT=53
– PROVIDER=cloudflare
networks:
dnsbridge:
ipv4_address: 10.5.0.2
google-dns-tls:
build: .
restart: always
image: qmcgaw/cloudflare-dns-server
container_name: google-dns-tls
environment:
– VERBOSITY=0
– VERBOSITY_DETAILS=0
– BLOCK_MALICIOUS=off
– LISTENINGPORT=53
– PROVIDER=google
networks:
dnsbridge:
ipv4_address: 10.5.0.3
pihole:
image: pihole/pihole:4.2.1
dns:
– 127.0.0.1
– 172.22.222.1
ports:
– „53:53/tcp“
– „53:53/udp“
– „80:80/tcp“
– „443:443/tcp“
cap_add:
– NET_ADMIN
networks:
dnsbridge:
ipv4_address: 10.5.0.4
environment:
ServerIP: 172.22.222.227
TZ: Europe/Berlin
DNS1: 10.5.0.2
DNS2: 10.5.0.3
WEBPASSWORD: PIHOLE_PASSWORD
volumes:
– ‚/etc/pihole/:/etc/pihole/‘
– ‚/etc/dnsmasq.d/:/etc/dnsmasq.d/‘
restart: always
networks:
dnsbridge:
driver: bridge
ipam:
config:
– subnet: 10.5.0.0/16
gateway: 10.5.0.1
————– YML ENDE ————-
Fehlermeldung von QNAP
Fehler 2020/09/11 12:23:10 Container admin Failed to do background task (application_custom, stonepi)
Fehler 2020/09/11 12:23:10 Container admin Background task error for application_custom stonepi: Failed to build service image. Please check your YAML.
———-
Danke für die HIlfe
Terminaleingaben als Foto, cool.
Weshalb sudo im ~/ ?
root@odroid:~# curl -fsSL https://get.docker.com -o get -docker.sh
curl: (22) The requested URL returned error: 403 Forbidden
Tja, sscheint nicht mehr aktuell zu sein.
Wäre cool wenn du da mal schaust was noch geht und was nicht.
Grüße
Das muss ein Netz-Wackler gewesen sein… bei mir lässt sich der Link ohne Probleme abrufen.
Und wo findest du Terminal Eingaben als Foto in meinem Beitrag?
Hello,
I’m using the latest ubuntu on odroid XU4 and having probkem to pass the docker-compose installation. After many problems with cffi, bcrypt and python, it now fails with:
adding license file ‚LICENSE‘
writing manifest file ’src/bcrypt.egg-info/SOURCES.txt‘
copying src/bcrypt/_bcrypt.pyi -> build/lib.linux-armv7l-cpython-310/bcrypt
copying src/bcrypt/py.typed -> build/lib.linux-armv7l-cpython-310/bcrypt
running build_ext
running build_rust
=============================DEBUG ASSISTANCE=============================
If you are seeing a compilation error please try the following steps to
successfully install bcrypt:
1) Upgrade to the latest pip and try again. This will fix errors for most
users. See: https://pip.pypa.io/en/stable/installing/#upgrading-pip
2) Ensure you have a recent Rust toolchain installed. bcrypt requires
rustc >= 1.56.0.
Python: 3.10.6
platform: Linux-5.4.211-407-armv7l-with-glibc2.35
pip: 22.0.2
setuptools: 65.4.0
setuptools_rust: 1.5.2
rustc: n/a
=============================DEBUG ASSISTANCE=============================
error: can’t find Rust compiler
I’ve tried to update the setuptools, but they seem to be the latest. Anything else I can do please?