Teil 6: Dynamic DNS

Was lange währt wird endlich gut. Kommen wir zum Auslöser der gesamten Geschichte.
Als Basis habe ich die Anleitung auf AWL-Home verwendet.
Da mit der Spaghetticode des PHP-Skripts nicht gefiel, habe ich es jedoch umgebaut (mehr dazu unten).

Was ist DynDNS eigentlich

Mein Server hat zwar eine feste IP Adresse, Ihr Internetzugang in der Regel jedoch nicht (im Moment haben Sie die IP 3.80.131.164, morgen vielleicht schon eine andere).
Dieses ständige "umgeziehe" macht es unmöglich, auf eigenen Rechnern Dienste im Netz anzubieten (z.B. den Zugang zur eigenen Cloud oder einen VPN-Tunnel).
Die Lösung dafür ist DynDNS. Wir können dem Rechner zwar keine eigene, öffentliche IP Adresse geben, einen eigenen Domainnamen schon.

Frage: Wenn der Rechner seine IP Adresse schnell mal ändern kann, woher weiß dann eigentlich der DNS Server, welche IP Adresse er zurückmelden muss?
Antwort: Der Rechner sagt einfach beim DNS-Server bescheid, dass sich seine IP Adresse geändert hat, indem er eine bestimmte Website aufruft. Über diese wird dann der DNS Eintrag geändert.

Deshalb brauchen wir für DynDNS nicht nur einen DNS-Server, sondern auch einen Webserver. Und damit niemand das Passwort abgreift, brauchen wir SSL.

Die Einrichtung

Vereinfachungen

Die Idee des DynDNS Protokolls ist Flexibilität. Man kann u.A. mehrere Einträge gleichzeitig ändern, MX records setzen und vieles mehr.
Für meine Anforderungen genügt eine einfachere Fassung, bei der je Aufruf genau ein A-Record gesetzt wird.

Dazu verwende ich einfach den Namen der Subdomain als Benutzernamen, als IP wird immer die des Aufrufers verwendet, Mehrfach-Updates oder andere Recordtypen schließe ich aus.
Damit reichen für die Aktualisierung Benutzername und Passwort aus. Die Authentifizierung erledigt Apache, d.h. das Update-Skript wird nur dann aufgerufen, wenn Benutzername und Passwort korrekt sind.

DNS Server

Hier sind alle Einstellung bereits in Teil 1: Nameserver gemacht.

Webserver Konfiguration

Zuerst legen wir unter htdocs auf dem Webserver einen Ordner (z.B. ddnsupdate) an, der uns später als Update-URL dient.
Ein zweiter Ordner auf der selben Ebene wie htdocs (z.B. passwd) nimmt die Passwortdatei auf.
Danach tragen wir im <VirtualHost> folgendes ein:

<Directory "/srv/www/www.beispiel.de/htdocs/ddnsupdate">
        DirectoryIndex index.php
        AuthType Basic
        AuthName "DynDNS Update"
        AuthBasicProvider file
        AuthUserFile /srv/www/www.beispiel.de/passwd/dnsupdate.passwd
        Require valid-user
</Directory>
Als nächstes legen wir mittels htpasswd2 die Passwörter an (Hinweis: der Benutzername wird später auch der Name der Subdomain):
beispiel.de:/srv/www/www.beispiel.de/passwd # htpasswd2 -bc dnsupdate.passwd user1 <langes Passwort>
beispiel.de:/srv/www/www.beispiel.de/passwd # htpasswd2 -b  dnsupdate.passwd user2 <anderes langes Passwort>
beispiel.de:/srv/www/www.beispiel.de/passwd # htpasswd2 -b  dnsupdate.passwd user3 <noch ein anderes langes Passwort>
Hinweis: Die Option -c im ersten Kommando legt die Datei dnsupdate.passwd neu an. Diese Option später nochmals zu versenden würde alle bisherigen Nutzer löschen.
Die Passwörter sollten aus Zahlen, Groß- und Kleinbuchstaben bestehen und vorzugsweise komplett zufällig sein. Weiterhin dürfen sie auch 20 Zeichen oder länger sein. Merken muss man sie sich nicht, sie müssen nur einmal im Router eingetragen werden.

Das Update-Skript

Nachdem wir unseren passwortgeschützen Berecih auf dem Webserver haben, erstellen wir im entsprechenden Ordner eine "index.php" mit dem Update-Skript. Zum Beispiel dieses (das auch gern kopiert werden darf):

<?php
        $zone="ddns.beispiel.de";

        function checkIP($ip) {
                $iptupel = explode(".", $ip);
                foreach ($iptupel as $value) {
                        if ($value < 0 || $value > 255)
                                return false;
                }
                return true;
        }

        function getDomain() {
                if (isset($_SERVER['REMOTE_USER']))
                        return $_SERVER['REMOTE_USER'];
                if (isset($_SERVER['PHP_AUTH_USER']))
                        return $_SERVER['PHP_AUTH_USER'];
                return "";
        }

        function runUpdate($domain, $ip) {
                exec("/usr/bin/dynupdate $domain $ip", $cmdout, $ret);
                return $ret;
        }

        function logError($errtype, $msg) {
                syslog(LOG_INFO, $msg);
                echo $errtype." ".$msg;
        }

        header("Content-type: text/plain");

        $error=0;
        openlog("DDNS Update", LOG_PID | LOG_PERROR, LOG_LOCAL0);

        $domain=getDomain();
        if ($domain=="") {
                $error=1;
                logError("notfqdn", "Update failed. Could not get Domain");
        }
        if ($error==0) {
                $ip=$_SERVER['REMOTE_ADDR'];
                if (!checkIP($ip)) {
                        $error=1;
                        logError("nohost", "Update failed. Could not get IP for Domain ".$domain.".");
                }
        }
        if ($error==0) {
                $ret=runUpdate($domain, $ip);
                if ($ret !=0) {
                        $error=1;
                        logError("dnserr", "Update command failed");
                }
        }


        if ($error==0) {
            echo "good ".$ip." ".$domain.".".$zone;
        }

        echo "\n";
?>
Zusätzlich benötigen wir noch ein Shell-Skript (damit nsupdate nicht suid root laufen muss). Dieses legen wir unter /usr/bin/dynupdate an und setzen es mit chmod 4755 /usr/bin/dynupdate suid root.
Der Inhalt des Skripts:
#!/bin/bash

zone="ddns.beispiel.de"
zonekey="/etc/ddns/ddns.key"

domain=$1
ip=$2

/usr/bin/nsupdate -k $zonekey <<EOF
zone $zone
update delete $domain.$zone A
update add $domain.$zone 30 A $ip
send
EOF

exit
Die Skripte sind zwar noch nicht perfekt, funktionieren aber im Moment.

Das war es eigentlich schon. Einfach mal ausprobieren (die Update URL lautet im Beispiel "https://www.beispiel.de/ddnsupdate/"). Nach der Eingabe von Benutzernamen und Passwort sollte eine Erfolgsmeldung erscheinen und spätestens 60 Sekunden danach der Rechner über <username>.ddns.beispiel.de zu erreichen sein.

Einrichtung im Router

In der Konfigurationsoberfläche des Routers DynDNS auswählen, Anbieter auf benutzerdefinert stellen, Update-URL, Benutzernamen und Passwort eintragen, fertig.

Hinweis: Mindestens auf der Fritz!Box funktioniert das nur, wenn die Subdomain bereits existiert. Dazu vor dem Eintragen der Daten in die Fritzbox auf dem Server folgendes ausführen:

/usr/bin/dynupdate <subdomain> 127.0.0.1