Ich hab schon lange bei mir einen privaten Nameserver laufen. Das hat einfach den Vorteil, dass ich jedes Gerät problemlos mit einem Namen ansprechen kann und mir keine IP-Adressen merken muss. Bisher hab ich dafür immer Bind verwendet. Jetzt ist es allerdings so, dass Bind nicht unbedingt einfach zu konfigurieren ist und daher fasse ich das Ding lieber nicht an, sondern erweitere die Konfiguration immer nur um neue Geräte wenn was dazukommen sollte. Frei nach dem Motto “never touch a running system

Da ich aber gerade dabei bin, das ich für mein Heimnetzwerk einen neuen Server einrichte - Ich hab mir einen Intel Nuk J5005 geleistet - will ich auch den Nameserver darauf umziehen. Aber… und das ist der Clou daran: Ich möchte alles möglichst kapseln und daher will ich den Nameserver in einem Docker Container laufen lassen.

Dazu steht auch schon ein schönes Docker-Image zur Verfügung: https://hub.docker.com/r/cytopia/bind
Das Image scheint ziemlich weit verbreitet zu sein und ist auch leicht zu konfigurieren.

Anforderungen

Da es sich nicht um einen öffentlichen Nameserver handelt, sind die Anforderungen an den Nameserver eher überschaubar.

  • eine lokale Domain zur Verfügung stellen. Das heißt alle Geräte sollen über einen Namen mit der tld (top level domain) .hofmann.lan erreichbar sein.
  • Verschiedene Geräte stellen verschiedene Dienste zur Verfügung. Zum Beispiel einen WebServer, einen FileServer, Portainer zur Verwaltung von Docker, Datenbank mit mysql usw. Diese Dienste sollen alle durch einen eindeutigen Namen ansprechbar sein. Zum Beispiel files.hofmann.lan zum FileServer, db.hofmann.lan zur Datenbank usw.
  • Anfragen, die nicht verarbeitet werden können, sollen weitergeleitet werden an einen öffentlichen Nameserve. Als öffentlichen Nameserver verwende ich dabei allerdings meine Fritzbox, da diese dann ihrerseits die Anfragen weiterleitet, so wie es vom Provider empfohlen ist. Man könnte auch direkt an öffentliche Nameserver weiterleiten, aber ich lass das mal so, wie es bisher auch läuft.

Das sollte sich eigentlich alles ganz leicht abbilden lassen.

Grundsätzliche Konfiguration des Docker Containers

Ich bin dabei streng nach Anleitung vorgegangen und hab nur den Container mit folgendem Kommando gestartet:

docker run -i \
  -p 53:53/tcp \
  -p 53:53/udp \
  -e WILDCARD_DNS='\
  nuk.hofmann.lan=192.168.0.39,\
  docker.hofmann.lan=192.168.0.39, \
  odroid.hofmann.lan=192.168.0.5, \
  loki.hofmann.lan=192.168.0.46 \
  ' \
  -e DNS_FORWARDER='192.168.0.1' \
  -t cytopia/bind

Das ist jetzt natürlich nicht vollständig, aber an Hand der Beispiele kann man sehr gut sehen, wie die einfache Konfiguration funktioniert. Mit der Umgebungsvariable WILDCARD_DNS legt man die einzelnen Subdomains an. Mit DNS_FORWARDER leite ich den Rest an meine Fritzbox weiter.

Die Ernüchterung kam dann allerdings, als ich das Kommando ausführen wollte:

docker: Error response from daemon: driver failed programming external connectivity on endpoint
priceless_bohr 7f5ef34f4325d7c3581c75e20301af7ab99df45263510462da424aac32507c80): Error
starting userland proxy: listen tcp 0.0.0.0:53: bind: address already in use.

Irgendwie scheint auf dem frisch installierten Ubuntu 18.04 bereits irgendwas an diesem Port zu laufen. Nur was? ich hab da nichts installiert und es wäre mir neu, dass Ubuntu standardmäßig einen Nameserver installiert.
Nach einigem Suchen hab ich den “Übeltäter” auch gefunden. Es handelt sich dabei wirklich um einen Nameserver, nämlich um systemd-resolv, einem lokalen Nameserver. Ich muss zugeben, dass ich davon noch gar nichts wusste. Da ich aber Bind verwenden will, stört mich in diesem Fall dieser Nameserver nur und ich müsste mich da auch erst einmal einlesen, ob ich damit meine Anforderungen nicht vielleicht auch abbilden könnte. Daher wird das Ding jetzt einfach abgeschaltet.

sudo systemctl stop systemd-resolved
sudo systemctl disable systemd-resolved

Zusätzlich muss man auch noch wissen, dass man auch noch einen Eintrag in der Datei /etc/resolv.conf entfernen muss. Hier wird nämlich auf den lokalen Nameserver verwiesen:

# auskommentiert für bind nameserver
# nameserver 127.0.0.53
search fritz.box

Und jetzt kann es losgehen und wir führen das obige Statement einfach nochmal aus. Das Ergebnis sieht gleich viel besser aus.

[INFO] Debug level: 1
[INFO] Using default DNS TTL time: 3600 sec
[INFO] Using default DNS Refresh time: 1200 sec
[INFO] Using default DNS Retry time: 180 sec
[INFO] Using default DNS Expiry time: 1209600 sec
[INFO] Using default DNS Max Cache time: 10800 sec
[INFO] Adding wildcard DNS: *.nuk.hofmann.lan -> 192.168.0.39
[INFO] Adding wildcard DNS: *.docker.hofmann.lan -> 192.168.0.39
[INFO] Adding wildcard DNS: *.odroid.hofmann.lan -> 192.168.0.5
[INFO] Adding wildcard DNS: *.loki.hofmann.lan -> 192.168.0.8
[INFO] Not adding any extra hosts
[INFO] DNSSEC Validation: no
[INFO] Adding custom DNS forwarder: 192.168.0.1
[INFO] Starting BIND 9.11.4

Testen wir noch kurz, ob das alles auch so funktioniert:

~$ ping odroid.hofmann.lan
PING odroid.hofmann.lan (192.168.178.5) 56(84) bytes of data.
64 bytes from 192.168.178.5 (192.168.178.5): icmp_seq=1 ttl=64 time=0.593 ms

~$ ping google.de
PING google.de(fra15s29-in-x03.1e100.net (2a00:1450:4001:806::2003)) 56 data bytes
64 bytes from fra15s29-in-x03.1e100.net (2a00:1450:4001:806::2003): icmp_seq=1 ttl=56 time=20.8 ms

Also sowohl im lokalen Netzwerk also auch in Internet funktioniert die Namensauflösung. Daraus folgt: Test erfolgreich

Feinschliff

Jetzt brauchen wir nur noch ein paar Schönheitskorrekturen. Aktuell ist es so, dass der Container manuell gestartet werden muss und durch den Parameter -i auch noch eine offene Konsole benötigt.
Dazu ändern wir den Parameter -i (interaktiv) zu -d (daemon).
Für den Neustart verwendet man die Docker Restart Policy mit dem Parameter –restart always.

Eine weitere Sache will ich auch noch geändert haben: Der Container soll einen vernünftigen Namen haben. Standardmäßig erzeugt Docker immer zufällige Namen, wenn ein Container gestartet wird. Ich persönlich finde das aber etwas unübersichtlich, daher möchte ich haben, dass der Container künftig auf den Namen bind hört. Dazu gibt es den Parameter –name mit dem man dann einen namen angeben kann.

Das geänderte Statement zum Anlegen/Starten des Containers sieht jetzt folgendermaßen aus:

docker run -d \
  --restart always \
  --name bind \
  -p 53:53/tcp \
  -p 53:53/udp \
  -e WILDCARD_DNS='\
  nuk.hofmann.lan=192.168.0.39,\
  docker.hofmann.lan=192.168.0.39, \
  odroid.hofmann.lan=192.168.0.5, \
  loki.hofmann.lan=192.168.0.46 \
  ' \
  -e DNS_FORWARDER='192.168.0.1' \
  -t cytopia/bind