PowerDNS mit Pi-Hole auf RasPi

Eines der etwas länger schon mal angedachten Projekte, welches ich dann aufgrund Zeitmangels und auch etwas Frustration in die Ecke gelegt hatte ist ein eigener, lokaler DNS-Server den man auch schnell aktualisieren kann – wer häufiger in verschiedenen Projekten arbeitet weiß es zu schätzen wenn man den Stall der Entwicklungs-Container nicht per IP-Adresse auswendig lernen muss, sondern ihn per sprechenden Namen ansprechen kann. In diesem Post geht es erst einmal darum die Grundlagen zu schaffen, auch das ist bereits etwas mehr Arbeit als ich gedacht hätte.Wichtige Eckpunkte / Material zum Nachbauen:

  • Raspberry Pi (inklusive Netzteil)
  • als DNS-Server kommt PowerDNS zum Einsatz
  • MySQL/MariaDB als Datenbank für die DNS-Einträge
  • DNS-Dist als Loadbalancer für DNS-Queries
  • PiHole als Resolver mit Blacklist
  • Docker als Virtualisierung für alle Services
  • IPv6 als primäres Netzwerkprotokoll, IPv4 nur als Fallback

Raspi vorbereiten

Als erstes habe ich mir einen meiner Rapis geschnappt und die SD-Karte mit einem aktuellen Image bespielt – hierfür gibt es mehr als genügend Anleitungen im Netz, daher führe ich das nicht weiter aus. Zum Einsaz kommt in meinem Fall das aktuelle Raspberry Pi OS Lite alias „Bulls-Eye“. Ich habe für den Anfang erst einmal mit einer 8GB MicroSD-Karte gearbeitet, jetzt wo alles läuft ist da noch mehr als ausreichend Platz drauf (etwa 3,8GB derzeit belegt). Als Raspi habe ich einen Raspi 2 Model B heran gezogen, für die anstehenden Aufgaben reicht die Leistung aktuell mehr als aus.

Ein wichtiger Schritt zum Abschluss der Installation: die Datei mit dem Namen ssh auf der SD-Karte in der Boot-Partition ablegen, damit beim Hochfahren gleich der SSH-Server mit gestartet wird. Denn zumindest derzeit habe ich kein Display am Raspi vorgesehen (das könnte dann ein Projekt für Ostern werden, z.B. um ein Monitoring zu haben).

Danach sollte man sich ggf. noch ein paar Hilfsmittel / Shortcuts einrichten. Ich habe mir zum Beispiel einen Hostnamen für den RasPi eingerichtet, zudem sollte man das Passwort des „pi“-users natürlich auf etwas Sinnvolleres setzen als den üblichen Default. Ebenfalls ganz praktisch ist es wenn man sich für die Authentifizierung den Public-Key in der .ssh/authorized_keys einträgt.

Zudem gehört es zum üblichen Setup auch das System erst einmal auf Stand zu bringen


apt-get update

apt-get upgrade

Docker auf dem Raspi installieren

Auch das ist ein nicht gerade spannender Schritt, aber der Bau von Fundamenten muss eben sein. Die Installation ist auch an vielen anderen Stellen im Netz beschrieben, daher hier die Kurzfassung:


sudo curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker pi

#Testing
docker version
docker info
docker run armhf/hello-world

Das Eintragen des Users in die Docker-Gruppe kann man machen, ich finde es etwas angenehmer zum Arbeiten, aber es birgt eben auch ein gewisses Risiko, da jedes Mitglieder der Docker-Gruppe Container starten kann und dort auch beliebige System-Anteile aus dem Hostsystem mounten kann. Hier muss jeder selbst abwägen was ihm wichtiger ist.

IPv6 im Netzwerk und für Docker einrichten

Wohl dem der ein statisches Prefix für seinen Internet-Anschluss sein Eigen nennt, ich habe es leider noch nicht – immerhin bekomme ich aber mittlerweile von meinem ISP natives IPv6 geboten (das war leider nicht immer so). Eine Sache die mit Docker derzeit überhaupt nicht sinnvoll funktioniert ist IPv6 mit dynamischen Prefixes. Es fehlt schlichtweg ein Mechanismus um die Netzwerk-Prefixe dynamisch zu aktualisieren. Einziger mir derzeit bekannter Weg hierzu ist der Neustart der Container, wozu man aber auch die Netzwerke mit dem geänderten Prefix neu anlegen muss. Eine ziemlich mühsame und fehlerträchtige Sache, ich hoffe in den kommenden Jahren tut sich da noch etwas um hier Möglichkeiten zu schaffen die Netzwerke, unter anderem die Docker default brige „docker0“ aber eben auch die user-created networks entsprechend per DHCP-Prefix-Delegation (PD) aktualisieren zu können. Wer sich unter dem Begriff nichts vorstellen kann, hier die einfachste Umschreibung die den meisten etwas bringen dürfte: Ein Gerät im Netzwerk kann bei einem DHCPv6-Server nicht nur eine einzelne Adresse anfordern (das kennen wir schon von IPv4), sondern man kann sich (zusätzlich) ein ganzes Subnetz zuweisen lassen, der DHCP-Server/Router kümmert sich dann darum das alles was in dieses Segment gehen soll an den entsprechend weiter „downstream“ gelegenen Knoten geroutet wird.

So lange das nicht funktioniert muss man einige Umwege / Workarounds machen damit docker mit IPv6 sinnvoll zurecht kommt. Der Ausweg sind derzeit die sogenannten Unique Local Addresses (ULA) – diese sind vergleichbar wenn auch nicht identisch zu den privaten IP-Adressen aus der IPv4-Welt. Mit IPv6 ist es nichts ungewöhnliches mehr, dass ein (physikalisches) Netzwerkinterface auf mehrere Adressen hört (das konnte man auch schon mit IPv4, auch wenn es dort auch eher ein Workaround ist). Daher kann man die meisten handelsüblichen Router dazu bewegen zusätzlich zur Verteilung der global güliten IPv6-Adressen anhand des zugewiesenen Prefixes auch entsprechende ULAs zu verteilen. Nebenstehender Screenshot zeigt z.B. die Einstellung einer Fritz!Box. Als Beispiel habe ich hier das Prefix „fdaf:ffee:c0ff:ee00″/64 gewählt. Korrekterweise sollte man für die Erzeugung des Prefixes aber einen entsprechenden Generator nutzen – sonst ist die Wahrscheinlichkeit das die Adresse wirklich Unique eher gering.

Damit hat man im ersten Moment noch nichts gewonnen, denn die Prefix-Delegation funktioniert damit immer noch nicht in Docker. Nun kann man sich aber natürlich zusätzliche Netzwerke ausdenken die es auch geben soll – je nach Gusto kann man für das nachgelagerte Docker-Netzwerk einfach die letzten 16 Bit des erwürfelten Prefixes inkrementieren, in unserem Beispiel könnte es auf auf „fdaf:ffee:c0ff:ee01″/64, „fdaf:ffee:c0ff:ee02″/64 usw. lauten (auch nach „unten“ wäre noch etwas Luft).

Das Config-File für Docker sieht dann ungefähr wie folgt aus:

{ 
 "bip" : "198.18.1.1/24", 
 "default-address-pools":
    [ 
{ 
"base" : "198.18.0.0/15", 
"size" : 24 
}, 
{ 
"base" : "fdaf:ffee:c0ff:ee02::/64", 
"size": 80 
} 
], 
"ipv6" : true, 
"fixed-cidr-v6" : "fdaf:ffee:c0ff:ee01::/64", 
}


Es reicht leider nicht eifnach nur „ipv6: true“ zu setzen, man muss zusätzlich sowohl ein Prefix für die docker Default-Brigdge (fixed-cidr-v6) und die Konfiguration für die Adress-Pools angeben. „base“ gibt hierbei den Adressbereich / Prefix an und „size“ die zu verwendende Bitmask bzw. Prefix-Länge an. Mit der gezeigten Konfiguration hätten wir also für unsere selbst definierten Docker-Netzwerke 16 Bit zur freien Verfügung: „fdaf:ffee:c0ff:ee02:0000::/80“ bis „fdaf:ffee:c0ff:ee02:ffff::/80“ – das sollte auch für eine umfangreichere Anzahl Projekte ausreichen (zumindest so lange das Projekt auf einem Raspi läuft).

Was man jetzt noch machen muss, ist diese Routen auch im Router einzutragen, damit weiß dieser wohin er die Pakete weiterleiten soll die an ein bestimmtes Subnetz gebunden sind. Hierbei ist wichtig, dass man die sogenannte „link-local“ IPv6 als Ziel einträgt, anhand dieser weiß der Router über welches Interface er die Pakete an die Zielstation zugestellt bekommt. Der Screenshot zeigt wiederum wie das auf der Fritzbox aussehen würde.

Was haben wir bis jetzt erreicht: Unser Netzwerk funktioniert auch weiterhin wenn der Internet-Anschluss und somit auch mal das Prefix von extern nicht verfügbar ist. Zudem weißt Docker nun unseren benutzerdefinierten Netzwerken entsprechende IPv6-Adressen zu. Mit den statischen Routen werden diese auch durch andere Hosts im Netzwerk erreichbar. Es empfiehlt sich das ausreichend zu testen. Ich nehme hierfür gerne das busybox-Image, es tut aber auch ein debian oder jedes andere einfache Image.


pi@raspberrypi:~/ $ docker run -it busybox

Nun sollte man sehen dass man von verschiedenen Geräten (ggf. auch aus dort eingerichteten Docker-Netzwerken) den gestarteten Container erreichen kann. Die aktuell zugewiesene Adresse erhält man mit

# ip addr show

Zudem sollte man sich ein benutzerdefiniertes Netzwerk in Docker anlegen und dort ggf. auch nochmals die Erreichbarkeit testen.

docker network create --ipv6 --subnet "fdaf:ffee:c0ff:ee02:42::/80" mydnsnetwork

Datenbank-Server – MySQL oder MariaDB installieren

Nachdem der letzte Schritt doch etwas aufwändiger war, ist es nun nur ein kleiner Schritt.


pi@raspberrypi:~/ docker run -d --network mydnsnetwork --ipv6=fdaf:ffee:c0ff:ee02:42::2/80 -v /var/lib/mysql:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=supersecretpassword jsurf/rpi-mariadb

Somit haben wir einen Datenbank-Server auf dem Raspi am Laufen. Ich habe mich für MariaDB entschieden, da es doch etwas moderner ist und dennoch auch gut mit PowerDNS zusammen spielt. Man kann hier natürlich auch ein MySQL Image nehmen, oder wenn man es möchte die Datenbank auch auf einem gänzlich anderen Server laufen lassen.

Auch hier sollte man wieder die Erreichbarkeit und Nutzbarkeit des Datenbankservers testen.

Zusätzlich empfiehlt es sich den DNS Benutzer und die Datenbank zu initialisieren die man später verwenden möchte.


create use 'dns'@'%' identified by 'supersecretpassword';

create database dns;

grant all on dns.* to 'dns'@'%';

Wichtig hierbei ist, dass wir den User nicht auf localhost einschränken (eine Sicherheitsvorkehrung die man in klassischen Umgebungen machen sollte), in den Containern muss der Prozess aber eben von einem anderen Host aus erreichbar sein und auch der Benutzer wird in den seltensten Fällen aus dem eigenen Datenbank-Container darauf zugreifen.

Authorative Nameserver (PowerDNS) installieren / Docker Image erzeugen

Das Dockerfile ist recht fix gemacht, ich habe hier nichts fertiges nehmen wollen, denn er Inhalt ist absolut klein und ich verlasse mich dann doch eher auf „offizielle“ Images, dort ist die Gefahr, dass sie irgendwann nicht mehr verfügbar sind etwas geringer und man holt sich ggf. auch nicht irgendwelchen Mist mit an Bord den man gar nicht haben möchte. Ich habe hier ein Alpine-Image heran gezogen, man könne aber genauso gut ein Debian als Grundlage verwenden, Alpine ist aufgrund der Größe des Images gerade auf dem Raspi aber eine gute Wahl. Wichtig ist, dass man auch das passende Sub-Package mit mysql-Support installiert, sonst kann PowerDNS die MySQL-Datenbank nicht ansprechen. Das Dockerfile sieht wie folgt aus, und man sollte es sich in ein passendes Unterverzeichnis legen.


FROM alpine
RUN apk add pdns pdns-backend-mysql
ENTRYPOINT [ "/usr/sbin/pdns_server" ]

Wichtig ist: Das Docker-Image sollte auf dem Raspi gebaut werden (oder man hat eine Machine mit Multi-Arch-Builds, aber für dieses Beispiel ist das eher weniger notwendig. Wichtig bei docker build: der „.“ am Ende für das lokale Verzeichnis als Build-Umgebung.


pi@raspberrypi:~/pdns_authorative $ docker build -t powerdns_authorative .

[shell]

Jetzt kann man das Image auch schon fast starten. Vorher sollte man aber noch die Konfiguration bereit legen. Diese ist ja nicht im Docker-Image enthalten, das kann man zwar machen, ist aber nicht der empfohlene Weg, Stichwort Wiederverwendbarkeit und auch die Image-Weitergabe (z.B. über eine Registry wie Docker-Hub) mit Passwörtern ist eher uncool.

[shell title=/etc/powerdns/pdns.conf]

# Launch gmysql backend
launch=gmysql

# gmysql parameters
gmysql-host=fdaf:ffee:c0ff:ee02:42::2
gmysql-port=3306
gmysql-dbname=dns
gmysql-user=powerdns
gmysql-password=supersecret
gmysql-dnssec=yes

#Mode of operation
master=yes

Zusätzlich sollte man noch das Basis-Schema von PowerDNS in der Datenbank einspielen.


pi@raspberrypi:~/ curl -o pdns.sql  https://raw.githubusercontent.com/PowerDNS/pdns/master/modules/gmysqlbackend/schema.mysql.sql
pi@raspberrypi:~/ mysql -u powerdns -p dns < pdns.sql

GGf. sollte man sich auch einige Einträge in der Datenbank anlegen, also die Domains für welche der Server zuständig sein soll und die entsprechenden Records. Das hier aufzuführen würde den Artikel sprengen, daher sei erst einmal auf die gute PowerDNS-Doku verwiesen.

Nun kann man aber auch schon den DNS-Server in Docker starten:


pi@raspberrypi:~/ $ docker run -d --network mydnsnetwork --ipv6=fdaf:ffee:c0ff:ee02:42::3 -v /etc/powerdns/:/etc/pdns --name pdns_authorative powerdns_authorative

Auch hier gilt wieder: Erreichbarkeit testen mit Einträgen die man in der Datenbank hinterlegt hat schadet nicht. Das sollte sowohl vom Raspi aus funktionieren als auch ggf. von einem anderen Rechner im System,


someone@someMachine: ~/ dig @fdaf:ffee:c0ff:ee02:42::3 mytest.entry.on.mypi.tld

; <<>> DiG 9.16.8-Ubuntu <<>> @fdaf:ffee:c0ff:ee02:42::3 mytest.entry.on.mypi.tld
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 4657
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;mytest.entry.on.mypi.tld. IN        A

;; ANSWER SECTION:
mytest.entry.on.mypi.tld. 3600 IN A 196.12.12.12

;; Query time: 3 msec
;; SERVER: fdaf:ffee:c0ff:ee02:42::3#53(fdaf:ffee:c0ff:ee02:42::3)
;; WHEN: So Jan 16 16:59:41 CET 2022
;; MSG SIZE  rcvd: 92

Hier muss man natürlich wissen: per IPv4 ist der Authorative nicht zu erreichen, denn wir haben (aus gutem Grund) im Docker kein Port-Forwarding eingetragen welches es ermöglichen würde den Authorative auch über den Port 53 des Raspis zu erreichen.

PiHole in Docker installieren

Der PiHole ist recht leicht zu installieren:


pi@raspberrypi:~/ $ sudo mkdir -p /etc/pihole/{pihole,dnsmasq.d}

pi@raspberrypi:~/ $ docker run -d --network mydnsnetwork --name pihole -v /etc/pihole/pihole/:/etc/pihole -v /etc/pihole/dnsmasq.d:/etc/dnsmasq.d --ip6=fdaf:ffee:c0ff:ee02:42::4 -e TZ="Europe/Berlin" pihole/pihole:latest

Der Start kann ein wenig dauern, da der PiHole einiges initialisieren muss. Zudem muss man leider sagen ist PiHole in Docker kein Musterschüler was das Konzept von MicroServices ist. Stattdessen verwendet der Container ein internes (wenn auch sehr schmalbrüstiges) Init-System namens S6. Ich werde hierzu bei Gelegeneheit einmal Versuche unternehmen das Ganze in getrennte Services zu verpacken. Für den ersten Versuch des Setups wäre das aber dann doch etwas zu viel des Guten. Verfolgen kann man die die Logs recht gut:


pi@raspberrypi:~/ $ docker logs pihole

Da sieht man ggf. auch recht schnell ob etwas nicht funktioniert.

Das Testen der Funktionalität geht analog zu oben für den authorative, nur kann und sollte man hier auch global gültige DNS-Einträge abfragen. Hier bieten sich so Dinge wie google.de oder jede andere beliebige Domain an.

Zudem bringt PiHole ein recht schönes Webtinterface mit das man per: http://[fdaf:ffee:c0ff:ee02:42::4]:80/admin erreicht. Auch hier gilt: Per IPv4 schaut man in die Röhre, wir haben kein Portforwarding für den Webserver definiert – wenn man es unbedingt wollte könnte man es per -p 80:80 als Parameter mit ins den docker run aufnehmen, aber wir wollen ja möglichst viel IPv6 machen, also erst recht für den Webserver des PiHole. Mit dem Authorative-Nameserver kann man sich dann aber natürlich auch passende lokale Domains und Namen defineren, damit man sich nicht die IPv6 merken muss, dazu kommen wir dann gleich.

DNSDist installieren

DNSDist ein ein Load-Balancer für DNS-Services, ursprünglich einmal eingeführt durch PowerDNS um eine saubere Trennung zwischen Cache/Resolver und eigentlichem DNS-Server herbei zu führen und zusätzlich auch noch einiges an Verteilung und Regeln zu machen. Für den Anwendungsfall hier nutzen wir nur einen Bruchteil der Funktionalität.

Das Dockerfile ist auch wieder sehr übersichtlich, einiziger Fallstrick ist die Angabe von „–supervised“, sonst startet der Server zwar kurz, fährt dann aber wieder herunter weil ihm das Terminal für die normale LUA-Konsole fehlt die DNSDist normalerweise anbietet. LUA ist eine Skriptsprache und wird in DNSDist zur Konfiguration verwendet, der Vorteil liegt darin, dass man den Server auch zur Laufzeit neu konfigurieren kann – für einen LoadBalancer keine schlechte Idee.


FROM alpine:latest
RUN apk add dnsdist

ENTRYPOINT ["/usr/bin/dnsdist"]
CMD ["--supervised"]

Gebaut bekommt man das Image ganz regulär:


pi@raspberrypi:~/dnsdist $ docker build -t dnsdist .

Das Config-File dazu sieht wie folgt aus:


setSecurityPollSuffix("")
addLocal("0.0.0.0:53")
addLocal("[::]:53")
setACL({"0.0.0.0/0","::/0"})

-- Authorative Server
newServer({address="[fdaf:ffee:c0ff:ee02:42::3]:53", pool="authorative", name="authorative"})

-- PI-Hole as sink
newServer({address="[ fdaf:ffee:c0ff:ee02:42::4]:53", pool="hole", name="Pi-Hole"})

addAction("alles.was.der.authorative.beantwortensoll.", PoolAction("authorative"))
addAction(AllRule(), PoolAction("hole"))

Die Config ist eigentlich recht gut selbst erklärend, man definiert zwei Pools (die jeweils nur einen Host enthalten, man kann sich natürlich auch Gedanken zur Redundanz machen und mehrere Raspis mit PiHole und Authorative NameServern bestücken. Wichtig sind die Regeln anhand derer der Load-Balancer entscheidet wohin mit dem Paket. Es ist zulässig mehrere Domains an den gleichen Authorative Server weiter zu geben, z.B. wenn man meineDomain.de und meineDomain.com durch den gleichen Server betreut wissen möchte. Die AllRule sorgt dafür dasss alles was bis dahin noch nicht absortiert wurde an den PiHole weiter gegeben wird.

Gestartet wird der Container dann wie folgt:

docker run -d –ip6=fdaf:ffee:c0ff:ee02:42::5 –network dns -p 53:53/tcp -p 53:53/udp –name dnsdist -v /etc/dnsdist/dnsdist.conf:/etc/dnsdist.conf dnsdist

Hier sieht man sehr schön, dass man jetzt auch das Portmapping eintragen sollte/muss, das wird vor allem benötigt wenn man den Raspi als Nameserver auch für IPv4-Geräte nutzen möchte. Das Log verrät dann auch ob die Konfiguration passt und ob die nachgelagerten Server (Authorative und PiHole) erreichbar sind. Zudem prüf DNSDist die Erreichbarkeit kontinuierlich und nimmt ggf. Server aus dem Load-Balancing wenn diese nicht erreichbar sein sollte. Das sieht man auch im Frontend des PiHole, dort prasseln im Normalfall sekündlich Anfragen von dnsdist ein, das sind die Tests ob der PiHole noch läuft.

Automatisiserung mit docker-compose

Damit man nicht jedes Mal die ganzen Befehle runter rattern muss, bietet es sich an ein kurzes Docker-Compose-File für die Images zu erzeugen.


version: '2.4' 
services: 
 dnsdist: 
   build: 
     context: ../dnsdist/arm7 
     dockerfile: Dockerfile 
   image: dnsdist 
   container_name: dnsdist 
   restart: "unless-stopped" 
   volumes: 
     - ${DNSDIST_CONF_DIR:-/etc/dnsdist/}:/etc/dnsdist/ 
   networks: 
     dns: 
       ipv6_address: ${IPV6_PREFIX}::6 
   logging: 
     driver: "json-file" 
     options: 
       max-file: "1" 
       max-size: "1m" 
 powerdns_authorative: 
   build: 
     context: ../authorative/arm7 
     dockerfile: Dockerfile 
   image: powerdns-authorative 
   container_name: powerdns-authorative 
   restart: "unless-stopped" 
   volumes: 
     - ${POWERDNS_CONF_DIR:-/etc/powerdns}:/etc/pdns 
   networks: 
     dns: 
       ipv6_address: ${IPV6_PREFIX}::3 
   logging: 
     driver: "json-file" 
     options: 
       max-file: "1" 
       max-size: "1m" 
 db: 
   image: jsurf/rpi-mariadb 
   container_name: db 
   environment: 
     MYSQL_ROOT_PASSWORD : ${MYSQL_ROOT_PASSWORD:?NO_MYSQL_ROOT_PW} 
   restart: "unless-stopped" 
   volumes: 
     - ${MARIADB_DATA_DIR:-/var/lib/mysql/dns/}:/var/lib/mysql 
   networks: 
     dns: 
       ipv6_address: ${IPV6_PREFIX}::2 
   logging: 
     driver: "json-file" 
     options: 
       max-file: "1" 
       max-size: "1m" 
 pihole: 
   image: pihole/pihole:latest 
   container_name: pihole 
   restart: "unless-stopped" 
   networks: 
     dns: 
       ipv6_address: ${IPV6_PREFIX}::5 
   volumes: 
     - ${PI_HOLE_DNSMASQ_CONF_DIR:-/etc/pihole/dnsmasq.d}:/etc/dnsmasq.d", 
     - ${PI_HOLE_PIHOLE_CONF_DIR:-/etc/pihole/pihole}:/etc/pihole 
#### User Defined Networks #### 
networks: 
 dns: 
   enable_ipv6: true 
   name: ${DNS_NETWORK:-dns} 
   driver: bridge 
   ipam: 
     driver: default 
     config: 
       - subnet: ${IPV6_PREFIX}/{$IPV6_PREFIX_LENGTH} 

Wichtig zu erwähnen ist: man muss derzeit eine Version 2.x in den Kopf schreiben, die 3.x Versionen beherrschen leider kein IpV6 – warum das so ist, wissen wohl nur die Macher von Docker und Docker-Compose. Für die meisten Werte stehen sinnvolle defaults in der Datei, was man natürlich machen muss ist die passenden Config-Files noch an die richtige Stelle legen. Man könnte das auch per environment-Variablen und entrypoint-Skripten lösen, allerdings finde ich es praktischer die Config außerhalb des Containers aufzubewahren und auch dort fast schon „klassisch“ editieren zu können.

Zusammenfassung

Jetzt hat man einen vollständig in Docker laufenden PiHole, mit der Option zusätzliche eigene Domains vorher abzufiltern indem man den Authorative entsprechend konfiguriert und die Regeln für den DNSDist entsprechend anpasst. Der PiHole selbst bietet auch eine ähnliche Möglichkeit mit dem Menüpunkt LocalDNS, allerdings ist das bei Weitem kein vollständiger DNS-Server mit allen Funktionalitäten wie PowerDNS. Unter anderem bietet PowerDNS die Funktionalität dynamischer Updates (DynDNS bzw. RFC2136). Dazu werde ich auch mal noch einen Artikel verfassen.

Es gibt auch noch einige Punkte die noch nicht angesprochen wurden, gerade was IPv6 betrifft: Mit der hier vorgestellten Konfiguration erfolgt die externe Abfrage von nicht geblockten DNS-Einträgen weiterhin nur per IPv4 (natürlich werden aber IPv6-Records (AAAA) aufgelöst wenn danach gerfragt wird), denn der Container hat ja keine globale IPv6-Adresse mit welcher er nachfragen könnte. Das Problem liegt hier nicht an der Konfiguration sondern ist vielmehr ein Docker-Problem: Docker kann mit IPv6 nur sehr rudimentär umgehen (was sehr schade ist) – unter anderem kann Docker keine Prefix-Delegation, welche es erlauben würde dass die globalen Adressen sich automatisch anpassen wenn der vorgelagerte Router ein entsprechend anderes Präfix seitens des Providers zugewiesen bekommt. Wohl dem der eine statische IPv6-Adresse am Heimanschluss sein Eigen nennt. Ansonsten werde ich da auch noch ein paar Dinge ausprobieren damit ich möglichst viel des IPv4-Anteils noch reduzieren kann.