Ein DNS-Server mit PowerDNS und Docker – Teil 1: Das Docker-Compose-File

Vor einiger Zeit habe ich über die Einrichtung von Pi-hole als DNS-Server mit keepalived und Docker geschrieben. Die Konfiguration bestand aus zwei virtuellen Maschinen, auf denen sich jeweils die Master- und Slave-DNS-Server als Container befanden, wobei ich mich eines bind-Images bedient habe, das ich bis dato auch für externe DNS-Server eingesetzt hatte. Leider musste ich feststellen, dass dieses Image seit einiger Zeit nicht mehr gepflegt wird – oder dies zumindest den Anschein hat, denn plötzlich funktionierten die Zonen-Transfers nicht mehr, meine Anfrage dazu blieb jedoch unbeantwortet, dasselbe gilt für die Frage, ob überhaupt noch Arbeiten an dem Image stattfinden.

DNS und Bind – nahezu ein Synonym?

Nun ist bind nicht die einzige DNS-Server-Software, alternativ wäre beispielsweise PowerDNS zu nennen. PowerDNS ist natürlich Open Source und besteht aus den Komponenten Authoritative Server, Recursor und dnsdist. Eine Besonderheit von PowerDNS ist die Möglichkeit, zur Speicherung der DNS-Daten unterschiedliche Backends zu verwenden, neben Datenbanken wie MySQL/MariaDB, PostgreSQL, SQLite oder Oracle werden ebenso Zonendateien von bind unterstützt, des Weiteren kann ein Koprozess per Pipe-Backend angesteuert werden, der die Realisierung komplexer Szenarien (etwa Abfrage von GeoIP-Daten zum Aufbau eines CDN; Inhalt hinter Bezahlschranke) ermöglicht. So weit wollte ich jedoch gar nicht gehen, vielmehr bestand die Überlegung, das bisherige System von bind als Docker-Container durch PowerDNS – natürlich ebenfalls wieder mit Docker – zu ersetzen, um einerseits die lokale bzw. heimische Domain zu verwalten und andererseits DNS-Dienste für das darüber liegende Pi-hole bereit zu stellen. Pi-hole sollte im besten Fall von den Änderungen gar nichts mitbekommen, d.h. die PowerDNS-Server sollten auf denselben IP-Adressen verfügbar sein wie zuvor die bind-Container.

Tausche Bind gegen PowerDNS

Das Ziel bestand somit im Austausch der bisherigen Bind-Container gegen PowerDNS-Server, die wiederum innerhalb von Docker-Containern residieren sollten. Da PowerDNS aus mehreren Komponenten besteht, erschien es mir sinnvoll, jede dieser (und sonstiger) benötigten Komponenten in jeweils einem Container laufen zu lassen, so dass die zugrunde liegenden Docker-Images unabhängig voneinander sind. Das ermöglicht es beispielsweise , Updates unabhängig voneinander einzuspielen, einzelne Container zwecks Wartung herunter zu fahren, ohne dass der gesamte DNS-Server gestoppt werden muss usw.. Die Kommunikation der PowerDNS-Dienste untereinander erfolgt über den Docker-internen User-defined Bridge-Network-Layer, dazu später mehr.

PowerDNS Docker-Image – Make or Buy?

Bei der Suche nach geeigneten PowerDNS-Images fiel auf, dass es zwar etliche gibt, viele davon aber nicht ausreichend aktuell gehalten werden. Zum anderen packen nicht wenige Images mehrere PowerDNS-Komponenten zusammen, was dem eingangs erwähnten Ziel widerspricht. Beide Gründe zusammen führten mich dazu, entgegen meines ursprünglichen Vorhabens nicht auf bereits existierende Docker-Images zurück zu greifen, sondern erneut eigene Images zu bauen. Für MariaDB war dies glücklicherweise nicht nötig, hier konnten die offiziellen Images genutzt werden, ebenso wurde für PowerDNS-Admin ein viel versprechendes Docker-Image angeboten. Bei PowerDNS-Admin handelt es sich um ein web-basiertes Administrationstool für PowerDNS, was zwar nicht von den PowerDNS-Autoren selbst stammt, aber einen sehr guten Eindruck hinterließ. Alle Standard-Aktionen wie Anlegen von Domains oder Ändern von Domain-Daten, lassen sich mit PowerDNS-Admin sehr einfach und bequem durchführen, ohne dass man direkt per MySQL-/MariaDB-Kommandozeile mit der Datenbank kommunizieren müsste.

Letztlich besteht das PowerDNS-Server-System aus insgesamt sechs Komponenten und somit Containern, die sich aus folgenden Images zusammensetzen:

  • MariaDB für PowerDNS-Authoritative Server (Image)
  • MariaDB für PowerDNS-Admin (Image s.o.)
  • PowerDNS Authoritative Server (Image), im Folgenden auch nur PowerDNS-Auth-Server oder PowerDNS-Server genannt
  • PowerDNS Recursor (Image), ein rekursiver, caching-only Nameserver
  • dnsdist (Image), der DNS-Loadbalancer
  • PowerDNS-Admin (Image), das web-basierte Administrationstool

Vorüberlegungen: Zonentransfer, IP-Adressen, Verzeichnisse

Während bei bind zum Zonentransfer die Verfahren AXFR oder IXFR genutzt werden, empfiehlt die PowerDNS-Dokumentation, das Kopieren der DNS-Daten vom Master- auf den Slave-Server von der darunter liegenden Schicht erledigen zu lassen. Im Fall von MySQL bzw. MariaDB als Persistenz-Schicht könnte dies mittels Replikation der Master-Datenbank auf eine oder mehrere Slaves stattfinden. Damit wäre PowerDNS von der Replikations-Aufgabe befreit. Letztlich stellt dies jedoch eine Verlagerung der Komplexität dar, wobei AXFR oder IXFR in Verbindung mit Notify genau dafür vorliegen, den Zonentransfer zu erledigen. Zudem ändern sich DNS-Daten weniger häufig als diejenigen Daten, die typischerweise in einem Datenbanksystem vorliegen, weshalb mir der Weg des Zonentransfers nach wie vor als sinnvoll erscheint. Glücklicherweise unterstützt PowerDNS auch die klassischen Zonentransfers, weshalb auf eine derartige Konfiguration zurückgegriffen wurde. Im Detail unterscheiden sich Master und Slave nur durch wenige Optionen, die Struktur der o.g. Docker-Container ist hingegen identisch, so dass alle Komponenten jeweils einmal für den Master-DNS-Server als auch für den Slave-Server existieren.

Da PowerDNS mittels Docker realisiert wird, sind auf dem Host so gut wie keine weiteren Vorbereitungen notwendig. Die Ausnahme ist die zusätzliche IP-Adresse, auf die der DNS-Server gebunden werden soll. Das bietet den Vorteil, dass der DNS-Server leicht von einem Host auf einen anderen umgezogen werden kann, wobei die IP-Adresse des DNS-Servers einfach mitgenommen wird. Da die DNS-Server grundlegend für den Betrieb des gesamten Netzwerks sind, wird hier auf feste IPs zurück gegriffen, so dass keine Abhängigkeiten zum DHCP-Server existieren. Seit Ubuntu 17.04 ist Netplan für die Netzwerk-Konfiguration zuständig, die dazu gehörige Datei /etc/netplan/01-netcfg.yaml lautet wie folgt:

# This file describes the network interfaces available on your system
# For more information, see netplan(5).
network:
  version: 2
  renderer: networkd
  ethernets:
    ens3:
      #dhcp4: yes
      addresses:
        - 192.168.10.104/24
        - 192.168.10.20/24
        - 192.168.10.220/24
      gateway4: 192.168.10.4
      nameservers:
        search: [geschke.net]
        addresses: [192.168.10.220,192.168.10.221]

Die zuerst genannte IP-Adresse 192.168.10.104 ist die primäre, alle weiteren werden als sekundäre Adressen eingetragen. Für den primären DNS-Server sind die Adressen 192.168.10.220 bzw. für den sekundären die 192.168.10.221 vorgesehen. Die Netzwerk-Konfiguration des sekundären DNS-Servers erfolgt analog.

Da die Anwendung aus mehreren Docker-Containern besteht, soll Docker-Compose zum Einsatz kommen. Dies ermöglicht nicht nur einen konsolidierten Start bzw. Stopp des Systems, sondern ist auch schlicht und einfach bequemer in der Bedienung als wenn jeder Container einzeln per Kommandozeile gestartet werden müsste.

Die meisten Konfigurationsoptionen werden als Umgebungsvariable übergeben und finden daher im docker-compose-File Platz. Die Ausnahmen bilden dnsdist, das sich auf eine Konfigurationsdatei stützt, sowie der PowerDNS-Recursor, der eine Liste der Domains bzw. Zonen erhält, die an den autoritativen DNS-Server weiter gereicht werden. Für beide Komponenten wird jeweils ein Verzeichnis zur Aufnahme der jeweiligen Konfigurationsdatei angelegt. Darüber hinaus benötigt MariaDB ein Verzeichnis zur Speicherung der Daten, woraus sich folgende Verzeichnisstruktur ergibt:

geschke@pankow:~/services/nsbackend1$ tree
.
├── dnsdist
│   ├── dnsdist.conf
├── mariadb_powerdns
│   └── data [...]
├── mariadb_powerdnsadmin
│   └── data [...]
├── nsbackend1.yml
├── recursor
│   ├── forward_zones
└── zone
    ├── 10.11.12.rev
    ├── 192.168.10.rev
    └── geschke.net.hosts

In der Docker-Compose-Datei nsbackend1.yml befindet sich die Definition des DNS-Servers bzw. seiner Komponenten. Das zone-Directory stellt eine Besonderheit dar. Darin befinden sich die Zone-Files von bind, die bei der Migration importiert werden können. Für den späteren Betrieb von PowerDNS sind diese Dateien hingegen nicht mehr notwendig, da sich alle DNS-Daten in der MariaDB-Datenbank befinden.

Definition der Anwendung per Docker Compose

Nun aber zum wichtigsten Teil des Nameservers, der Docker-Compose-Datei nsbackend1.yml. Nachfolgend werden die einzelnen Abschnitte, soweit notwendig, erläutert.

version: '3.7'
services:
  mariadb_powerdns:
    image: mariadb:latest
    restart: always
    volumes:
      - ./mariadb_powerdns/data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: PASSWORD_1
      MYSQL_DATABASE: powerdns
      MYSQL_USER: powerdnsuser
      MYSQL_PASSWORD: PASSWORD_2
    networks:
      dns_net:
        ipv4_address: 172.30.1.10
  mariadb_powerdnsadmin:
    image: mariadb:latest
    restart: always
    volumes:
      - ./mariadb_powerdnsadmin/data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: PASSWORD_3
      MYSQL_DATABASE: powerdnsadmin
      MYSQL_USER: powerdnsuser
      MYSQL_PASSWORD: PASSWORD_4
    networks:
      dns_net:
        ipv4_address: 172.30.1.20
  powerdns:
    image: geschke/powerdns-server
    restart: always
    environment:
      PDNS_BACKEND: mysql
      PDNS_API_KEY: API_KEY_1
      PDNS_LOCAL_PORT: 5300
      PDNS_MASTER: "true"
      PDNS_ALLOW_AXFR_IPS: "192.168.10.38/32,192.168.10.104/32,192.168.10.221/32,172.30.1.0/24"
      MYSQL_NAME: powerdns
      MYSQL_USER: powerdnsuser
      MYSQL_PASSWORD: PASSWORD_2
      MYSQL_HOST: mariadb_powerdns
    volumes:
      - type: bind
        source: ./zone
        target: /zones
    ports:
      - "8081:8081"
    networks:
      dns_net:
        ipv4_address: 172.30.1.30
  powerdns_recursor:
    image: geschke/powerdns-recursor
    restart: always
    environment:
      PDNS_ALLOW_FROM: "127.0.0.0/8, 10.0.0.0/8, 100.64.0.0/10, 169.254.0.0/16, 192.168.10.0/24, 172.16.0.0/12, 10.20.30.0/24, 172.30.1.0/24"
      PDNS_LOCAL_ADDRESS: "172.30.1.40"
      PDNS_LOCAL_PORT: "5301"
      PDNS_FORWARD_ZONES_FILEPATH: "/etc/powerdns/forward_zones"
      PDNS_API_KEY: API_KEY_2
    volumes:
      - type: bind
        source: ./recursor/forward_zones
        target: /etc/powerdns/forward_zones
    ports:
      - "8082:8081"
    networks:
      dns_net:
        ipv4_address: 172.30.1.40
  dnsdist:
    image: geschke/dnsdist
    restart: always
    ports:
      - "192.168.10.220:53:53/udp"
      - "192.168.10.220:53:53/tcp"
    volumes:
      - type: bind
        source: ./dnsdist/dnsdist.conf
        target: /etc/dnsdist/dnsdist.conf
    networks:
      dns_net:
        ipv4_address: 172.30.1.50
  powerdns_admin:
    image: ngoduykhanh/powerdns-admin:latest
    restart: always
    ports:
      - "80:80"
    logging:
      driver: json-file
      options:
        max-size: 50m
    environment:
      - SQLALCHEMY_DATABASE_URI=mysql://powerdnsuser:PASSWORD_4@mariadb_powerdnsadmin/powerdnsadmin
      - GUINCORN_TIMEOUT=60
      - GUNICORN_WORKERS=2
      - GUNICORN_LOGLEVEL=DEBUG
      - MAIL_SERVER=mailout.geschke.net
    networks:
      dns_net:
        ipv4_address: 172.30.1.60


networks:
  dns_net:
    ipam:
      driver: default
      config:
        - subnet: 172.30.1.0/24

Die Konfigurationsdatei besteht aus zwei Bereichen – zunächst erfolgt die Definition der Services, anschließend findet sich die Beschreibung des Netzwerks.

Netzwerk

Das Netzwerk stellt eine Besonderheit dar, denn üblicherweise ist keine Angabe der IP-Adressen der einzelnen Container notwendig, da diese von Docker intern verwaltet und vergeben werden. Mittels der “ipam“-Option (“IP Address Management”) ist es jedoch möglich, innerhalb der Docker-Engine ein Subnetz mit statischen IP-Adressen festzulegen, aus dessen Pool die Container jeweils eine IP-Adresse erhalten. Damit erhalten die Container beim Start jeweils immer dieselben IP-Adressen, was wiederum Voraussetzung dafür ist, dass der PowerDNS-Recursor Anfragen bzgl. bestimmter Domains (definiert in der Datei ./recursor/forward_zones) an den PowerDNS-Auth-Server weiterleiten kann. Zwar können einzelne Container innerhalb desselben Netzwerk unter ihrem Service-Namen (etwa “powerdns“, “mariadb_powerdns” etc.) angesprochen werden, da Docker intern die Auflösung der Namen in IP-Adressen vornimmt, jedoch funktioniert dies für PowerDNS-Recursor nicht, da dabei zwingend eine IP-Adresse angegeben werden muss, die sich auch nicht ändern darf, da ansonsten alle Angaben in der Konfigurationsdatei angepasst werden müssten, nachdem Docker die Zuteilung des Netzwerks vorgenommen hat. Den einzelnen Containern bzw. Services wird daher jeweils eine IP-Adresse statisch zugewiesen, die aus dem Subnetz stammt, das im Bereich “networks” definiert wurde.

Datenbank(en)

Bei den ersten beiden Services handelt es sich um die MariaDB-Datenbanken für PowerDNS-Auth-Server sowie PowerDNS-Admin. Die Beschreibungen ähneln sich bzw. unterscheiden sich nur bzgl. der Kennungen für Datenbank, User, Passwörter, Daten-Verzeichnisse und der jeweiligen IP-Adresse, die Struktur ist jedoch identisch. Während in der PowerDNS-Datenbank die DNS-Daten Platz finden, dient die PowerDNS-Admin-Datenbank zur Ablage von Account-Daten und sonstiger Konfigurationsoptionen. Da die Kommunikation von PowerDNS-Admin mit dem PowerDNS-Server über die REST-API stattfindet, ist kein Zugriff auf eine gemeinsame Datenbank notwendig. Das offizielle MariaDB-Image stellt die Basis für beide Container dar. Ein lokales Verzeichnis dient zur Aufnahme der Daten und wird in den Container unter /var/lib/mysql/ gemountet. Per Umgebungsvariablen werden das MariaDB-Root-Passwort sowie die Zugangsdaten (Datenbank, User und Passwort) für die zu verwendende Datenbank gesetzt. Sofern diese Parameter übergeben werden, werden beim ersten Start des Containers die entsprechende Datenbank mit den jeweiligen Accountdaten angelegt. Dies gilt natürlich nur dann, falls sich im gemounteten Verzeichnis nicht bereits die dazu gehörigen Dateien befinden. Anstatt der Umgebungsvariablen können auch Docker Secrets genutzt werden. Zuletzt erfolgt die Angabe der statischen IP-Adresse aus dem Netzwerk, das weiter unten in der Datei definiert wird. Damit sind die Service-Beschreibungen bereits abgeschlossen.

PowerDNS Authoritative Server

Als nächstes folgt die Beschreibung für den PowerDNS-Auth-Server. Diesem liegt das weiter oben erwähnte PowerDNS-Server-Image zugrunde. Auch hier erfolgt die Konfiguration mittels Umgebungsvariablen, die ebenfalls mit der _FILE-Extension (siehe MariaDB-Image) vorbereitet sind für die Nutzung von Docker Secrets. Als Backend-Datenbanken unterstützt das Image aktuell SQLite3 und MySQL, wobei Letzteres hier genutzt wird. Der API-Key dient zur Kommunikation mit dem PowerDNS-Server über dessen REST-API. Das Setzen des API-Keys sorgt ebenfalls dafür, dass der PowerDNS-eigene Webserver gestartet wird. Dessen Port 8081 wird hier mit der “ports“-Einstellung nach außen weitergegeben. PowerDNS stellt hier interne Informationen (Log-Messages, Statistiken etc.) zur Verfügung. Für den reinen Nameserver-Betrieb ist die Freigabe des Ports hingegen nicht notwendig, da der Zugriff im Docker-internen Netzwerk (dns_net) erfolgt. In aller Öffentlichkeit haben jene Informationen jedoch ungeschützt nichts verloren, weshalb entweder darauf verzichtet oder der Zugang mittels zusätzlicher Maßnahmen geschützt werden sollte. Für das heimische Netzwerk habe ich auf Derartiges jedoch zunächst verzichtet.  Anschließend erfolgt die Angabe des lokalen Ports für den PowerDNS-Server. Die Änderung des standardmäßigen Ports auf Port 5300 wäre eigentlich nicht notwendig, da alle Komponenten in unterschiedlichen Containern und somit unterschiedlichen IP-Adressen laufen.

Ich habe mich dennoch für einen anderen Port nach dem Beispiel des zweiten Szenarios der Migration zur Nutzung des Recursors aus der PowerDNS-Dokumentation entschieden, da dies die Trennung der Dienste noch einmal verdeutlicht. Sollten wie im Beispiel alle Komponenten auf einem Server, einer VM oder in einem Container mit ein- und derselben IP laufen, ist die Nutzung unterschiedlicher Ports hingegen zwingend notwendig. Als nächstes wird der PowerDNS-Server als Master gesetzt. Dies muss bei PowerDNS explizit geschehen, ebenso wie die Konfiguration als Slave, da ansonsten die “native” Replikation der DNS-Daten angenommen wird, d.h. die Replikation auf Datenbank-Ebene anstatt mittels AXFR/IXFR. Danach werden diejenigen IP-Adressen bzw. Netze übergeben, von denen der Zonentransfer initiiert werden darf. Neben der Angabe der IP-Adressen des Slave im lokalen Netz (etwa zu Testzwecken) ist auch das Docker-interne Netzwerk dns_net dafür freigeschaltet. Der Rest erklärt sich von selbst – als Hostname für den MariaDB-Server dient der Service-Name, daneben werden die Zugangsdaten für die PowerDNS-Datenbank übergeben. Die Netzwerkeinstellungen sind analog zu den MariaDB-Containern konfiguriert, natürlich mit einer anderen IP-Adresse. Eine Besonderheit, die nur zur Migration dient, ist das Volume. Dabei wird das lokale Verzeichnis “zone” in “/zones/” innerhalb des Containers gemountet. Während des laufenden Betriebs kann dieser Bereich gelöscht werden, dazu später mehr.  Damit ist die Konfiguration des PowerDNS-Auth-Servers bereits abgeschlossen.

PowerDNS Recursor

Als nächstes erfolgt die Definition des PowerDNS-Recursor-Services. Auch dabei gibt es einen API-Key, der in den Umgebungsvariablen übergeben wird. Die IP-Adressen bzw. Netze, denen der Zugriff auf den Recursor erlaubt werden, finden ebenfalls in diesem Bereich Platz. Dabei wird sämtlichen lokalen Netzwerken die Erlaubnis erteilt. Die lokale Adresse sowie der lokale Port werden ebenfalls gesetzt. Bei einer Anfrage an den Recursor muss dieser wissen, bei welchen Domains die Anfrage an den PowerDNS-Server weitergeleitet werden soll. Diese Domains können entweder direkt in der Umgebungsvariable angegeben werden, praktischer ist aber die Nutzung einer eigenen Datei, in der die Domains enthalten sind. Diese Datei sieht für mein heimisches Netzwerk wie folgt aus:

geschke.net=172.30.1.30:5300
10.168.192.in-addr.arpa=172.30.1.30:5300
12.11.10.in-addr.arpa=172.30.1.30:5300


Hier werden die Domain “geschke.net” sowie die PTR (Pointer-) Resource Records, d.h. die Auflösung von IP-Adressen in deren Hostnamen angegeben. DNS-Anfragen werden an die IP-Adresse des PowerDNS-Servers weitergeleitet. Diese Stelle ist auch der Grund dafür, dass die Container mit statischen IP-Adressen ausgestattet werden, denn eine Angabe eines Host- bzw. Docker-Service-Namens wird mit einer Fehlermeldung quittiert. Die Datei wird an eine geeignete Stelle – hier “/etc/powerdns/forward_zones” in den Container gemountet, wobei dieser Dateiname als Umgebungsvariable angegeben wird. PowerDNS-Recursor besitzt ebenfalls einen Webserver bzw. eine API, da der nach außen freizugebende Port 8081 jedoch bereits von PowerDNS-Server belegt ist, wird der Port 8082 genutzt und intern 8081 angesprochen. Die Netzwerk-Konfiguration ist wieder analog zu den anderen Containern.

dnsdist

Es folgt der Service für dnsdist. Zwar wäre dieser DNS-Loadlbalancer und Router im heimischen Netz nicht unbedingt nötig, andererseits ließe sich die Konfiguration mit wenigen Änderungen auch auf öffentlich im Internet genutzte DNS-Server-Systeme übertragen. Dieselben Maßnahmen, die das Routing anbetreffen, können sowohl in kleinen, privaten, als auch in größeren, öffentlichen Netzen Verwendung finden. Die Konfiguration findet hierbei nicht mittels Umgebungsvariablen statt, sondern mit einer Datei dnsdist.conf, die auf dem Host vorliegt und nach /etc/dnsdist/dnsdist.conf gemountet wird. Nach außen hin freigegeben sind für die IP-Adresse des DNS-Servers UDP sowie TCP jeweils auf Port 53. Die Datei dnsdist.conf zeigt sich wie folgt:

addLocal('0.0.0.0:53')
setACL({'0.0.0.0/0', '::/0'})

newServer({address='172.30.1.30:5300', pool='auth'})
newServer({address='172.30.1.40:5301', pool='recursor'})

recursive_ips = newNMG()
recursive_ips:addMask('192.168.10.0/24')
recursive_ips:addMask('172.16.0.0/12')
recursive_ips:addMask('10.0.0.0/8')
recursive_ips:addMask('10.11.12.0/24')

addAction(AndRule({OrRule({QTypeRule(dnsdist.AXFR),
                           QTypeRule(dnsdist.IXFR)}),
          OrRule({makeRule('192.168.10.38/32'),makeRule('192.168.10.221/32') })}),
        PoolAction('auth'))

addAction(NetmaskGroupRule(recursive_ips), PoolAction('recursor'))
addAction(AllRule(), PoolAction('auth'))

Der Aufbau ist vergleichbar mit dem Beispiel der Dokumentation. Zwei “Pools” werden angelegt, einer für den PowerDNS-Auth-Server, der auf die interne IP-Adresse des entsprechenden Services weiterleitet, und einer für den Recursor. Die für rekursive Abfragen erlaubten Netze werden einer NetmaskGroup-Variablen “recursive_ips” zugeordnet. Sofern keine Zonentransfers notwendig wären, würden die letzten beiden addAction()-Zeilen ausreichen. In der vorletzten Zeile wird allen IP-Adressen, die zu den zuvor definierten Netzwerkmasken in “recursive_ips” gehören, der Weg auf den Recursor(-Pool) der Zugriff gewährt. Die letzte Zeile besagt, dass alles andere, d.h. Anfragen von allen anderen Adressen direkt an den PowerDNS-Auth-Server geleitet werden. Da dieser nur die eigenen Domains kennt und somit keine rekursiven Abfragen erlaubt, können hier auch keine Daten “fremder” Domains in Auskunft gebracht werden.

Eine Besonderheit sind Zonentransfer-Anfragen, d.h. AXFR oder IXFR. Normalerweise würden Requests von lokalen IP-Adressen einfach an den Recursor gehen, der sie wiederum bei den gelisteten Domains an den Auth-Server weitergibt. Der Recursor ignoriert jedoch AXFR komplett, so dass derartige Anfragen den Auth-Server erst gar nicht erreichen. Des Weiteren ließe sich vom Auth-Server nicht eingrenzen, von welchen IP-Adressen Zonentransfers erlaubt sind, da aufgrund der Verwendung von dnsdist als Docker-Container sämtliche Anfragen von (Docker-) internen IP-Adressen aus dem Netzwerk dns_net zu kommen scheinen. Hier setzt dnsdist an, da alle Requests von außen erstmal bei dnsdist ankommen, ist dnsdist die Quell-Adresse bekannt, somit ist eine Filterung möglich. Für weitere Anwendungen wird dies in der Dokumentation bzgl. AXFR, IXFR und Notify beschrieben. Eine ähnliche Regel wird hier angewendet. Diese besagt, dass für AXFR- und IXFR-Requests Requests, die von einer aus zwei IP-Adressen (beides IPs des zweiten bzw. Slave-DNS-Servers) stammen, der Pool des Auth-Servers angesprochen wird. Da diese Action vor den anderen Actions eingerichtet ist, hat die entsprechend Vorrang. Sollte eine der beiden Bedingungen nicht zutreffen, wird das Standard-Verfahren genutzt, d.h. der Weg über den Recursor. Damit ist sichergestellt, dass nur Zonentransfers vom zweiten DNS-Servers ausgeführt werden können. Natürlich ist dies nur ein sehr kleines Beispiel der umfassenden und mächtigen Funktionen von dnsdist, hier dürften auch für sehr spezielle Nutzungsszenarien keine Wünsche offen bleiben.

PowerDNS-Admin

Als letztes wird der PowerDNS-Admin-Service eingerichtet. Die Konfiguration wurde weitestgehend aus dem Beispiel eines Docker-Compose-Files übernommen. Nach außen hin ist jedoch der Port 80 freigegeben, so dass man mit einem Request per Browser auf den DNS-Server standardmäßig PowerDNS-Admin erreicht. Für die Nutzung in öffentlichen Netzen sollte hier natürlich https genutzt werden, beispielsweise durch Vorschalten eines Proxys. Die Umgebungsvariable mit der Datenbank-URI wurde entsprechend auf die Gegebenheiten angepasst, hier kommen nun endlich die Zugangsdaten der PowerDNS-Admin-Datenbank zum Einsatz.

..and let the system run, run, run… (^1)

Der nächste Schritte wäre nun der Start des PowerDNS-Systems mit docker-compose, die Migration der bestehenden bind-Zonendateien und der Aufbau des Slave-Servers. Über die Hürden und Fallstricke, aber natürlich auch gelöste Probleme berichte ich im zweiten Teil. Als kleiner Vorgeschmack nach so viel Text ein kleiner Blick auf die Web-UI von PowerDNS-Admin.

Ein DNS-Server mit PowerDNS und Docker - Teil 1: Das Docker-Compose-File 1


(^1)

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Tags: