Tutorial: Eigener DDNS-Server (auf 1&1 Virtual Host)

Ein eigener DNS-Server bietet einem viele Möglichkeiten – vorallem wenn der vom Anbieter bereit gestellte Server nicht so sonderlich viele Konfigurationsmöglichkeiten bietet.

Außerdem ist es möglich, für den privaten Haushalt zu Hause (mit einer nicht statischen IP) einen dauerhaft funktionsfähigen DDNS Eintrag anzulegen. Hierfür gibt es zwar viele (kostenlose) Anbieter, doch bei den meisten kostenlosen Diensten ist der Trend zu erkennen, dass man alle paar Wochen persönlich die IP eintragen muss, damit die Einstellungen dort nicht gelöscht werden.

Also: Ein eigener DNS-Server für die eigene Domain muss her – inklusive Dynamischer DNS Subdomain, welche von FritzBox und Speedport unterstützt wird.

Das Ganze soll auf einem Virtual Server von 1&1 gemacht werden (das entspricht meiner Situtation). Die meisten Schritte sind jedoch unabhängig vom Anbieter.

Ausgangssituation

Ein Linux Server (Hier Ubuntu) mit statischer IP, eine eigene Domain. Hier wird example.de verwendet – das muss dann natürlich bei der Umsetzung angepasst werden. Die statische IP von eurem Server sei 123.123.123.123

Vorbereitung

Für alle folgenden Schritte wird root-Access vorausgesetzt – ein Login als root wird also angenommen.

Bevor es losgeht, müssen wir einen DNS Server installieren. Ich verwende bind.

apt-get install bind9 bind9-doc

Nachdem bind installiert wurde, geht es an die Konfiguration. Es werden sowohl Zonen als auch Regeln für diese Zonen benötigt. Die Zone besteht einfach gesagt aus DNS Einträgen für eure Domain. In den Regeln wird festgelegt, wer mit eurem DNS-Server kommunizieren kann, ob Updates erlaubt sind (DDNS) und noch einige weitere Einstellungen.

Zone anlegen

Als erstes legen wir eine Zonendatei an. Diese liegen im Verzeichnis /var/cache/bind und enthalten die nötigen DNS Einstellungen.

cd /var/cache/bind/
nano example.de.zone

Wir öffnen also eine neue Datei example.de.zone mit dem Texteditor nano. In diese müssen wir dann unsere Domain example.de konfigurieren.
Hierzu zählen auch Einstellungen für Subdomains (sofern ihr diese nicht in einer separaten Zone anlegen wollt – darauf gehe ich jetzt hier nicht näher ein).

Habt ihr also z.B. einen Webserver, der über www.example.de erreichbar sein soll und einen Mailserver, der über imap.example.de, smtp.example.de und mail.example.de (er verwendet im HELO mail.example.de), so sollte folgendes in die Datei eingetragen werden:

$ORIGIN .
$TTL 86400  ; 1 day
example.de     IN SOA ns1.example.de.  root.example.de. (
     2015120601         ; serial
     3600               ; refresh (1 hour)
     900                ; retry (15 minutes)
     604800             ; expire (1 week)
     86400              ; minimum (1 day)
     )
    
    NS    ns1.example.de.
    NS    ns1.rollernet.us.
    NS    ns2.rollernet.us.
    NS    puck.nether.net
    A     123.123.123.123
    MX    10   mail.example.de
    TXT   "v=spf1 a mx a:mail.example.de a:imap.example.de a:smtp.example.de mx:mail.example.de ~all"

$ORIGIN example.de.
$TTL 86400 ; 1 day
ns1     A    123.123.123.123
imap    A    123.123.123.123
mail    A    123.123.123.123
smtp    A    123.123.123.123
www     A    123.123.123.123

dyndns  A    123.123.123.123

Erklärung:

Der Eintrag example.de IN SOA … ist ein Start Of Authority Eintrag. Er wird durch den Namen des Nameservers, der verwendet werden soll sowie eine E-Mail Adresse ergänzt und hat die Parameter (Erläuterung steht dabei). Wichtig ist, dass in der E-Mail Adresse das “@” durch einen Punkt ersetzt wird, es ist also die Adresse root@example.de gemeint!

Darauf folgen die verwendeten Nameserver. ns1.example.de soll dieser Server sein – dazu später mehr. Die beiden NS von Rollernet und der von Nether dienen hier als Slaves: Sie holen sich regelmäßig eure Einträge ab und stehen zur Verfügung, falls euer Server mal ausfallen sollte.

Der Eintrag A 123.123.123.123 weist der Domain example.de diese IPv4 Adresse zu. Für den Mailversand existieren der MX (Mail Exchange) Eintrag sowie der TXT Eintrag (Dient dem Spamschutz – sonst landet euer Server ggfs. auf Spamlisten).

Im folgenden Abschnitt sind die Subdomains aufgelistet, welche ich vorhin erwähnt habe. Sie zeigen auch alle auf den gleichen Server. Für DDNS habe ich noch eine weitere Subdomain dyndns eingerichtet, auch der Nameserver selbst kriegt die Subdomain ns1.

Die Zonendatei kann dann gespeichert und geschlossen werden.

Mit

named-checkzone example.de example.de.zone

Kann die Zone auf Korrektheit geprüft werden. Die Beschwerde über einen fehlenden SPF/SPF Record scheint nur ein Bug in einigen bind Versionen zu sein.

 

Zone in Bind einfügen

Nachdem wir die DNS Einstellungen für die Zone definiert haben, kann sie in bind eingebunden werden. Dafür wechseln wir das Verzeichnis und bearbeiten die named.conf.local von bind:

cd /etc/bind/
nano named.conf.local

Sollte diese Datei nicht existieren, soll stattdessen die Datei named.conf geöffnet werden!

In dieser Datei fügen wir eine neue Zone hinzu (ans Ende der Datei):

zone "example.de." IN {
  type master;
  file "example.de.zone";
  allow-query { any; };
  allow-transfer {
    208.79.240.3;  # ns1.rollernet.us
    208.79.241.3;  # ns2.rollernet.us
    204.42.254.5;  # puck.nether.net
    ## Ggfs. weitere Slaves (puck.nether.net)
  };
  allow-update { 127.0.0.1; }; # Falls kein DDNS: allow-update { none; };
  notify yes;
}

Auch diese Datei wird gespeichert. Wir haben hier definiert, das Bind der Master für diese Zone ist, welche in der Datei example.de.zone liegt, dass die Zone zu den angegebenen IPs transferiert werden darf (zu den Slaves), dass diese bei Änderung benachrichtigt werden und dass Änderungen an der Zone durch Localhost eraubt sind (Da wir später Dynamische DNS verwenden wollen).

Auch hier können wir unser Werk mit

named-checkconf named.conf

überprüfen (Keine Ausgabe ist ein gutes Zeichen).

Anschließend starten wir bind9 neu / oder das erste Mal.

service bind9 restart

Wenn das glatt geht, sollte noch überprüft werden, ob die Zone auch geladen wurde. Falls nicht, ist dies in /var/log/syslog in einem mit [named] markierten Eintrag zu sehen.

Um zu gucken, ob alles so läuft wie erhofft, fragen wir unseren Server doch mal etwas:

nslookup mail.example.de 127.0.0.1

(Fragt nach der IP für mail.example.de am lokalen NS). Kommt da 123.123.123.123 zurück, scheint bisher alles zu klappen.

 

Slaves registrieren

In der Zonendatei und der named.conf tauchen Nameserver von rollernet.us auf. Diese sollen als Slaves verwendet werden und die Zonendatei regelmäßig kopieren. Dazu wird ein kostenloser Account benötigt (Hier registrieren). Anschließend muss dort nach dem Login unter “DNS Services” – “Secondary DNS” eine neue Zone angelegt werden (Add New Zone). Dort tragt ihr als Zonennamen example.de ein, bei Masterserver die IP eures Servers (123.123.123.123).

Analog verfahrt ihr bei puck.nether.net.

Nachdem dies abgeschlossen ist, sollte die Zone nach einigen Minuten repliziert worden sein (sollte in /var/log/syslog auftauchen!).

Damit der Zonentransfer klappt, muss Port 53 zu eurem Server geöffnet sein!

Auch das lässt sich anschließend testen:

nslookup mail.example.de ns1.rollernet.us
nslookup mail.example.de ns2.rollernet.us
nslookup mail.example.de puck.nether.net

 

DNS Server “aktivieren”

Der NS ist jetzt aktiv – aber keiner kennt ihn. Der Anbieter (1&1) führt immer noch seinen eigenen NS, in dem auch die Zone example.de noch aktiv ist. Dies muss noch geändert werden. Außerdem haben wir als Nameserver “ns1.example.de” angegeben. Er verwaltet also seine eigene Zone. Da kann es zu Problemen kommen. Wenn eine DNS Anfrage zu “example.de” kommt, ist der zuständige Nameserver “ns1.example.de” – und wie komm ich an den jetzt ran? Gar nicht! Dass kann er dir nur selbst sagen – und das ist problematisch. Hierfür gibt es sogenannte Glue Records. Diese müssen im NS der höhergelegenen Zone (.de) hinterlegt werden – das macht normalerweise der Anbieter (1&1). So bekommt man die Antwort, welche IP zu ns1.example.de gehört, schon vom 1&1 Server.

Im 1&1 Control-Center (oder im entsprechenden Center eures Anbieters) werden nun alle Subdomains gelöscht / gekündigt.

Nachdem dieser Vorgang abgeschlossen ist (und alle Subdomains auch im System verschwunden sind!), kann mit der Umstellung des DNS-Dienstes fortgefahren werden.

Hierzu muss der Nameserver der entsprechenden Domain example.de auf manuell umgestellt werden. Dazu folgt man am besten dieser Anleitung von 1&1 (oder einer entsprechenden Anleitung eures Anbieters).

Es müssen die folgenden Einstellungen gemacht werden:

Nameserver 1
ns1.example.de
IPv4 for Name Server 1
123.123.123.123
Zusätzliche Nameserver
Eigener sekundärer Nameserver
Nameserver 2
ns1.rollernet.us
Nameserver 3
ns2.rollernet.us
Nameserver 4
puck.nether.net

Die Einstellungen bestätigen. Dann dauert es etwas, bis die Einstellungen im System ankommen. Während dies passiert müssen wir noch den Glue Record freischalten! Andernfalls sind die DNS Einstellungen fehlerhaft.

Also in den 1&1 Domain-Einstellungen eine Subdomain anlegen. Da für diese ein Glue-Record angelegt werden soll, muss sie mit “ns” beginnen. Deshalb haben wir überall ns1.example.de verwendet.

Also neue Subdomain einrichten, ns1.example.de.

Für diese sollte automatisch (bei 1&1) ein Glue-Record erstellt werden. Bei anderen Anbietern ist hier ggfs. abweichend zu verfahren!

Nach einiger Zeit (Mehrere Minuten bis zu 24 Stunden!) sollten die Einstellungen dann hoffentlich übernommen worden sein. Falls es da Probleme gibt, die Subdomain ns1.example.de wieder löschen, die Nameserver wieder auf Standard einstellen und nach einigen Stunden erneut umstellen.

 

Einstellungen prüfen lassen

Auf Web-DNS-Tools könnt ihr nun eure Einstellungen überprüfen lassen. Dort einfach die Domain (example.de) angeben. Sofern keine Fails / Warnings auftreten, ist alles korrekt eingestellt.

 

Dynamische DNS

Juhu, der Nameserver funktioniert. Nun können wir uns dem DDNS – dynamische DNS – zuwenden. Ziel hierbei: Unser Router des privaten Internetanschlusses hat eine wechselnde IP-Adresse, soll aber auch über einen Domain-Namen erreichbar sein.

Hierzu werden in diesem Fall Subdomains von dyndns.example.de verwendet, zum Beispiel zuhause.dyndns.example.de soll ständig auf die gerade aktuelle IP des Routers zeigen.

Im Grunde sind alle Voraussetzungen dafür bereits getroffen! Die Zone example.de darf laut der named.conf durch “localhost” – also den Server selbst – geändert werden. Dazu zählen dann alle bestehenden Subdomains, die Domain selber oder eben auch SubSubdomains. Jetzt könnte man die IP jedes mal manuell in die Zone eintragen oder aber die DynDNS-Funktion des Routers benutzen. Für letzteres wird ein Webserver mit PHP benötigt und dies wollen wir umsetzen.

Im Grunde ist das gar nicht schwer – das einfachste Skript hierfür ist ca. 8 Zeilen lang. Um jedoch eine Authentifizierung und Validierung der entsprechenden Domain vorzunehmen, machen wir es etwas umfangreicher.

An dieser Stelle sei angemerkt, dass dies auf jeden Fall sicherer geht! Für die private Nutzung sollte dies jedoch ausreichen.

 

Webserver VHost einrichten

Zuerst soll ein neuer VHost (unter Apache) – analog für NGinx – eingerichtet werden. Also domain / alias wird dyndns.example.de eingetragen und ein entsprechendes Root-Verzeichnis eingerichtet werden.

Wenn dieses nun /var/www/dyndns ist, so erstellen wir dort eine index.php Datei:

cd /var/www/dyndns
nano index.php

Dies wird unser Skript für die Anfragen der Router. Für die private Benutzung reichen hier statische Benutzer und Passwörter sowie statische Domainnamen – wer es gerne öffentlich nutzbar machen möchte (also nicht nur für den privaten Gebrauch), sollte sich noch mit besserer Authentifizierung für PHP / HTTP und einer alternativen allow-update-Direktive beschäftigen.

Hier das vollständige Skript, wobei ihr die ersten Zeilen modifizieren müsst!

<?php
 // configuration for users, passwords and domain
 $pws = array( "myusername" => "mypassword" );
 $user_domain = array( "myusername" => array('zuhause', 'sub2') );
 // top zone
 $dyndns = "dyndns.example.de";
 // Bind Zone
 $zone = "example.de";
 // DNS server to send update to
 $dnsserver = "localhost";
 $dnsport = "";

  // Print every error / action to the syslog
 openlog("My Own DDNS", LOG_PID | LOG_PERROR, LOG_LOCAL0);

  // Take Client IP
  $ip = $_SERVER['REMOTE_ADDR'];
  
  // Check User
  if ( isset($_GET["USER"]) ) {
    $user = $_GET["USER"];
  } else {
    syslog(LOG_WARNING, "Invalid user from $ip");
    exit(0);
  }

  // Check for pw
  if ( isset($_GET["PW"]) ) {
    $pw = $_GET["PW"];
  } else {
    syslog(LOG_WARNING, "No pw given for user $user");
    exit(0);
  }

  if (!(isset($pws[$user]) and $pws[$user] = $pw)) {
    syslog(LOG_WARNING, "Invalid password given for user $user");
    exit(0);
  }

  // Get Domain to update
  if ( isset($_GET['DOMAIN']) ) {
    $subdomain = $_GET['DOMAIN'];
  } else {
    syslog(LOG_WARNING, "No domain given by User $user");
    exit(0);
  }

  syslog(LOG_WARNING, "Updating $subdomain.$dyndns to $ip...");

  // check for needed variables
  if ( isset($subdomain) && isset($ip) && isset($user) ) {
    // Check for valid IP
    if ( filter_var($ip, FILTER_VALIDATE_IP) && $ip != "0.0.0.0" && $ip != "255.255.255.255" ) {
      if ( in_array("*", $user_domain[$user]) or in_array($subdomain, $user_domain[$user]) ) {
        // shell escape all values 
        $subdomain .= '.'; 
        $subdomain = escapeshellcmd($subdomain); 
        $user = escapeshellcmd($user); 
        $ip = escapeshellcmd($ip);

        // Prepare nsupdate Command
        $data = "<<EOF
        server $dnsserver $dnsport
        zone $zone
        update delete $subdomain$dyndns A
        update add $subdomain$dyndns 300 A $ip
        send
        EOF";

        // Execute nsupdate
        exec("/usr/bin/nsupdate $data", $cmdout, $ret);
        // check whether DNS update was successful
        if ($ret != 0) {
          syslog(LOG_INFO, "Changing DNS for $subdomain$dyndns to $ip failed with code $ret");
        }
      } else {
        syslog(LOG_INFO, "Domain $subdomain is not allowed for $user from   $ip");
      }
    } else {
       syslog(LOG_INFO, "IP $ip ist invalid");
    }
  } else {
    syslog(LOG_INFO, "Missing values for domain Update");
  }
  closelog();
?>

myusername, mypassword und die entsprechenden Subdomains müssen angepasst werden.

 

Testaufruf

Ein Aufruf von

http://dyndns.example.de/?USER=myusername&PW=mypassword&DOMAIN=zuhause

sollte nun eine Änderung der DNS Einstellungen bewirken! Dies muss getestet werden. In /var/log/syslog sollten entsprechende Einträge zu sehen sein und

nslookup zuhause.dyndns.example.de

sollte auch die IP des Anschlussen, von dem ihr testet, zurückliefern!

Wenn das klappt, kann mit dem nächsten Schritt fortgefahren werden.

Router konfigurieren

Im Router können die DynDNS Einstellungen meist auf “manuell” umgestellt werden. Die meisten Speedport und FritzBox Router unterstützen dies.

Als Nutzername und Passwort die oben gewählten Daten eintragen, die Update-URL kann jedoch auch einfach übernommen werden:

http://dyndns.example.de/?USER=myusername&PW=mypassword&DOMAIN=zuhause

Wichtig ist der “/” nach example.de, ansonsten kommt es bei Speedport-Routern zu Problemen.

24 Replies to “Tutorial: Eigener DDNS-Server (auf 1&1 Virtual Host)”

  1. Hallo,

    deine Anleitung es echt super… habe es fast am laufen, nur bekomme ich beim ausführen des Script den Fehler
    Updating test.mein-dyndns.de to IP bla bla…
    Aug 14 20:43:58 v22018085771471510 dyndns Dienst[21411]: Domain test. is not allowed for user from IP bla bla

    1. Hi,

      das liegt daran, dass im Array $user_domain die entsprechende Zuordnung von Nutzername zu Domain fehlt.
      Diese muss dort ergänzt werden, damit das Skript die IP aktualisiert.

      1. Hi,
        erst einmal danke für das tolle Howto…
        Bei mir steht folgendes in der php:
        $pws = array( “ich” => “Geheim” );
        $user_domain = array( “ich” => array(‘test’) );

        es kommt aber weiterhin die Fehlermeldung..

        1. Laut der Fehlermeldung rufst du das Skript mit der Domain “test.” auf.
          Freigeschaltet ist aber nur “test” (der Punkt ist zu viel). Der Punkt wird nach der Validierung automatisch ergänzt (und fehlt normalerweise im “Updating…”-Log, werde ich beheben).

          Falls das das Problem nicht löst, einfach mal
          $user_domain = array( “ich” => array(‘*’) );
          probieren, das sollte zum Testen alle Domains erlauben.

  2. bin jetzt schon ein wenig weiter gekommen. mit ‘*’ macht er jetzt ein wenig weiter, jetz erscheint im Log:
    dyndns Dienst[8318]: Changing DNS for test.meinedomain.de to ip bla bla failed with code 1

    1. Naja, der Errorcode hilft nur bedingt.

      Ggf. sollte hier die entsprechende Log-Zeile um $cmdout erweitert werden, damit eine bessere Fehlermeldung kommt.

      Was passiert denn, wenn im Terminal manuell nsupdate aufgerufen wird und als Eingaben dann

      server [IP] [Port]
      zone [ZONE]
      update delete test.meinedomain.de A
      update add test.meinedomain.de 300 A [IP]
      send

      ausgeführt wird? Hier wäre die gesamte Ausgabe hilfreich. Ggf. ist der Pfad zu nsupdate falsch oder die Konfiguration in Bind.

    1. Ist der Server, auf dem das Script liegt, der gleiche wie der, auf dem Bind läuft?

      Ansonsten muss der allow-update Eintrag entsprechend abgeändert werden (statt 127.0.0.1 eben die IP des Script-Servers).

      Ggf. steht auch was hilfreiches im bind log (/var/log/named/stdlog bei mir).

  3. Also bei der manuellen eingabe über nsupdate funktioniert es schon mal. nur komisch das wenn ich ein nslookup auf dem bind server mache stimmt die ip aber mache ich ein nslookup von einem anderen Rechner ausserhalb bekomme ich als ip die des server zurück und nicht die des clients 🙂

    1. Ah, ich bin auch bei netcup. Da muss im CCP in den Domain-Einstellungen die DNS Server umgestellt werden (sodass netcup DNS Anfragen an deinen NS umleitet). Ist das der Fall? Da ich deinen Domain nicht kenne, kann ich da auch nicht wirklich helfen…

  4. Zusammenfassung,

    Nameserver ist bei netcup eingetragen
    manueller Eintrag mit nsupdate funktioniert
    php skript bring den Fehler wie oben erwähnt

    Gruß Klaus

  5. OK…
    php Skript macht update… juhuuu
    aber sobald ich den user eingebe habe ich die fehlermeldung
    not allowed for user from IP bla bla

    wie bekomme ich den noch diese kuh vom eis? und kann ich auch mehrere user anbinden? hintergrund ist das ich mehrere aussenstellen habe die ich gerne bedienen möchte

    1. Wie sehen denn die ersten paar Zeilen deines PHP Skripts aus? Und mit welchem Nutzer rufst du das auf?

      Mehrere Nutzer gehen auch, da müssen nur die beiden Variablen angepasst werden, z.B.:

      $pws = array( “user1” => “mypassword1”, “user2” => “secret2” );
      $user_domain = array( “user1” => array(‘domain1’), “user2” => array(“domain2”));

      Dann kann “user1” die Subdomain “domain1” updaten, user2 entsprechend domain2.

    2. Ich glaube, ich habe den Fehler gefunden 😉

      Die Zeilen

      $subdomain .= '.';
      $subdomain = escapeshellcmd($subdomain);
      $user = escapeshellcmd($user);
      $ip = escapeshellcmd($ip);

      sind an der falschen Stelle.

      Diese müssen HINTER das if (in_array(“*”, …). Ich habe den Code entsprechend aktualisiert.

Leave a Reply

Your email address will not be published. Required fields are marked *