In diesem Blog-Post beschreibe ich, wie man mit wenigen Schritten PowerDNS als Nameserver einrichtet. Alle Dienste werden mittels Docker-Compose als Container betrieben. Als Backend kommt MariaDB mit Master-Slave-Replikation zum Einsatz. Als WebUI setze ich PowerDNS-Admin ein.
Nodes vorbereiten
Für den Test habe ich in der Hetzner-Cloud zwei kleinstmögliche Nodes (CX11, 3€ pro Monat) erstellt und mit Debian 10 initialisiert.
IP Node 1: 116.202.111.94
IP Node 2: 116.202.111.112
Anschließend habe ich Docker und Docker-Compose installiert:
apt install docker.io docker-compose
Docker-Compose für den Master
Die Passworte hier habe ich natürlich für mein Testsystem geändert 😉 Für eigene Installationen empfehle ich das auch zu tun…
--- version: "3" services: mariadb: image: bitnami/mariadb:10.4 volumes: - mariadb-data:/bitnami/mariadb environment: MARIADB_ROOT_PASSWORD: iegie8wohd2Aeta MARIADB_DATABASE: pdns MARIADB_USER: pdns MARIADB_PASSWORD: teiGuna5ophie6a MARIADB_REPLICATION_MODE: master MARIADB_REPLICATION_USER: replicator MARIADB_REPLICATION_PASSWORD: dieB6ahghoothie ports: - 3306:3306 pdns: image: psitrax/powerdns:v4.2 environment: MYSQL_HOST: mariadb MYSQL_DB: pdns MYSQL_USER: pdns MYSQL_PASS: teiGuna5ophie6a MYSQL_DNSSEC: "no" MYSQL_AUTOCONF: "true" ports: - 53:53/tcp - 53:53/udp command: - --api=yes - --api-key=faiquahz2haeXie - --webserver-address=0.0.0.0 - --webserver-allow-from=0.0.0.0/0 admin: image: ngoduykhanh/powerdns-admin:0.2.2 environment: SQLALCHEMY_DATABASE_URI: sqlite:////data/pdnsadmin.db volumes: - admin-data:/data ports: - 80:80 volumes: mariadb-data: driver: local admin-data: driver: local
Ab Zeile 5 wird der MariaDB-Master konfiguriert. Ich verwende das Bitnami-Image, da man dort die Replikation einfach über Umgebungsvariablen aktivieren kann. Der Port 3306 muss vom Slave aus erreichbar sein, für mein Testsetup gebe ich ihn einfach extern frei. Für ein produktives Setup sollte unbedingt ein internes Netz erstellt werden (bei Hetzner mit wenigen Klicks möglich – anlegen des Netzwerks und zuweisen zu den Nodes reicht für das Debian-Standard-Image aus) oder die Kommunikation zwischen den MySQL-Instanzen mittels TLS (schwierig) oder Wireguard (einfach) abgesichert werden. Das Port-Binding ist bei Verwendung eines internen Netzes oder von Wireguard so anzupassen, dass Port 3306 nicht mehr öffentlich erreichbar ist.
Ab Zeile 19 wird PowerDNS konfiguriert. Das verwendete Image initialisiert MySQL gleich beim Start mit dem korrekten Schema. Schema-Upgrades werden derzeit nicht unterstützt! Extern wird der DNS-Port 53 für die Protokolle TCP und UDP freigegeben.
In den Zeilen 32-35 wird der API-Server von PDNS aktiviert und per API-Key gesichert für das Docker-Netzwerk freigegeben.
In den Zeilen 36-43 wird der PowerDNS-Admin konfiguriert. Die Verbindung zu PowerDNS erfolgt später in der grafischen Oberfläche. Im Testsystem gebe ich direkt Port 80 frei, in einem Produktivsystem sollte HTTPs verwendet werden, beispielsweise durch Traefik als Proxy.
Mittels docker-compose up -d
kann der Stack nun gestartet werden.
Docker-Compose für den Slave
Auf dem Slave sieht die Konfiguration ähnlich aus. Es wird jedoch auf die WebUI verzichtet:
--- version: "3" services: mariadb: image: bitnami/mariadb:10.4 volumes: - mariadb-data:/bitnami/mariadb environment: MARIADB_REPLICATION_MODE: slave MARIADB_MASTER_HOST: 116.202.111.94 MARIADB_MASTER_ROOT_USER: root MARIADB_MASTER_ROOT_PASSWORD: iegie8wohd2Aeta MARIADB_REPLICATION_USER: replicator MARIADB_REPLICATION_PASSWORD: dieB6ahghoothie pdns: image: psitrax/powerdns:v4.2 # mariadb slave is readonly, so we cannot autoconfigure the database environment: MYSQL_AUTOCONF: "false" # all database options must be passed as command args # unless https://github.com/psi-4ward/docker-powerdns/blob/master/entrypoint.sh # contains an option to disable schema updates only command: - --gmysql-host=mariadb - --gmysql-dbname=pdns - --gmysql-user=pdns - --gmysql-password=teiGuna5ophie6a - --gmysql-dnssec=no ports: - 53:53/tcp - 53:53/udp volumes: mariadb-data: driver: local
Da MariaDB hier im Slave-Mode läuft, werden nur das Root-Passwort und das Replication-Passwort des Masters sowie dessen IP angegeben. Der „pdns“-Benutzer wird vom Master automatisch mit synchronisiert.
Das PowerDNS-Image versucht im „AUTOCONF“-Modus, die Datenbank zu initialisieren. Dies ist auf dem Slave weder möglich (read-only) noch nötig (wird ja vom Master repliziert). Da es im Image aktuell keine Option gibt, die Initialisierung abzuschalten, wird AUTOCONF komplett deaktiviert und die Datenbankoptionen werden als Befehlsargumente an PDNS übergeben.
Auch dieser Stack kann mittels docker-compose up -d
gestartet werden.
PowerDNS-Admin einrichten
Nun kann der PowerDNS-Admin über die Adresse [TBD] aufgerufen werden. Es erscheint der Login-Dialog mit der Option, einen neuen Benutzer anzulegen, mit dem man sich anschließend einloggen kann.
Signup deaktivieren
Da nicht jeder zufällige Besucher Zugang zur DNS-Verwaltung bekommen soll, wird als nächstes unter Settings -> Authentication das Häkchen bei „Allow users to sign up“ entfernt.
Verbindung zu PowerDNS einrichten
Unter Settings -> PDNS sind die URL, der API-Key und die Version des PowerDNS einzutragen. Die URL ist http://pdns:8081/api, der API-Key wie im Docker-Compose verwendet. Version ist aktuell 4.2.
Im Menüpunkt Administration -> PDNS kann man die Verbindung prüfen – sie funktioniert, wenn dort Daten angezeigt werden.
Testdomain anlegen
Unter „+New Domain“ kann nun eine Testdomain eingerichtet werden. Der „Type“ ist native, da die Replikation im Backend erfolgt. Ich habe als Domain test1.example.com verwendet.
Im Dashboard kann nun mit „Manage“ ein A-Record zur Domain eingetragen werden. Als Name wird @ verwendet, das ist die Domain selbst. Als IP kann 1.2.3.4 eingetragen werden. Nach „Save“ muss die Änderung noch einmal mittels „Apply Changes“ übernommen werden, sonst geht sie verloren.
Nachdem der Record gespeichert wurde, können beide Nameserver direkt mittels „dig“ Befehl abgefragt werden und liefern die IP:
#> dig @116.202.111.94 test1.example.com ;; ANSWER SECTION: test1.example.com. 3600 IN A 1.2.3.4 #> dig @116.202.111.112 test1.example.com ;; ANSWER SECTION: test1.example.com. 3600 IN A 1.2.3.4
Die Testdomain kann nun wieder gelöscht werden.
Weitere Schritte
Für ein Produktivsystem sollte wie oben schon geschrieben ein Proxy mit HTTPs eingerichtet werden (beispielsweise Traefik) sowie die Kommunikation zwischen den MySQL-Instanzen gesichert werden.
Es sollte auch dringend ein Backup eingerichtet werden. Dies kann auf MySQL-Ebene geschehen, es wäre aber auch ein Dump der Nameserver-Zonen denkbar. Auch die Datenbank der WebUI sollte gesichert werden.
Es ist ebenso sinnvoll, Templates für Zonen zu erstellen und an die eigenen Wünsche anzupassen. Unter Settings -> Basic können noch weitere Optionen passend eingestellt werden.
Für automatische Upgrades der Container kann Watchtower zum Einsatz kommen.
Soll IPv6 unterstützt werden, ist es notwendig, den PDNS-Container auf dem Hostnetzwerk laufen zu lassen. In dem Fall ist es wichtig, die API-Server auf das interne Docker-Netzwerk zu beschränken.
Hallo!
Danke erstmal für die Anleitung 🙂
Leider scheitere ich derzeit beim Starten. Ich erhalte von dem MariaDB-Container den Fehler „mkdir: cannot create directory ‚/bitnami/mariadb/data‘: Permission denied“. Hast du eine Idee, wie das zu lösen ist?
Docker Host-System ist ein Debian 10, wie bei dir. Einziger Unterschied ist, dass ich ebenfalls versuche das ganze hinter den Traefik zu schieben. Da das aber eher beim PDNSadmin angesiedelt ist, wüsste ich nicht wieso das Auswirkungen auf den Container haben sollte.
Danke für einen Tipp 🙂
Hallo Patrick,
verwendest Du denn das Docker-Compose-File genau so, wie in meinem Post – insbesondere die Volumes? Ich habe es eben noch einmal auf einem frischen System getestet und konnte das Problem nicht nachstellen.
Solltest Du hingegen statt dem Volume ein Bind-Mount (z.B. hostPath auf Kubernetes oder ein pfad-gebundenes Volume auf Docker) verwenden, ist die Ursache, dass das Verzeichnis dann die falschen Zugriffsrechte erhält. Ein möglicher Lösungsansatz dazu ist unter https://github.com/bitnami/bitnami-docker-mariadb/issues/186#issuecomment-660318927 zu finden.
Viele Grüße,
Michael.
Hi,
zunächst einmal danke für die gut verständliche Anleitung.
Könnten sie diese ggf. aktualisieren um mit den neuesten Containern zu arbeiten?
PowerDNS-Admin hat inzwischen auch den Besitzer gewechselt und ist aktuell auf v 0.4.1
Beste Grüße
Christian
Da die skizzierte Lösung letztlich nicht zum Einsatz gekommen ist, gibt es leider auch keine Updates mehr dazu von mir.