rss feed articles activities all_comments

indeedgeek.de

Florian Eitel

Dyndns selbstgebacken

Dyndns.org kann jeder

Dyndns Dienstleister wie dyndns.org gibt es viele. Über eine simple Schnittstelle kann man dynamisch die IP-Adresse seiner Domain ändern. Kostenlos gibt es einen Suffix dyndns.org und schon kann jeder dynamisch auf die IP zugreifen. Sehr hilfreich manchmal. Aber ich hatte schlechte Erfahrungen mit dyndns.org. Als ich mal Übergangsweise die IP von indeedgeek.de dort eingetragen habe, sind wie aus dem Nichts die SSH Angriffe angestiegen. Deshalb hier kurz wie ihr euch euer eigenen Dyndns Provider bauen könnt.

Ihr braucht dazu eine Domain, ein DNS-Server und ein PHP-Script.

Fangen wir an mit der Domain. Dort müsst ihr über das Webfrontend ein NS-Record hinzufügen:

dyndns.indeedgeek.de NS indeedgeek.de

Damit sagen wir, dass alle Anfragen die mit *.dyndns.indeedgeek.de enden von dem DNS-Server auf indeedgeek.de bearbeitet werden.

Diesen müssen wir nun noch einrichten. Ich erkläre es mal am Beispiel BIND9. Dazu gibts ein schönes Tool welches eigentlich schon die gesamte Arbeit macht:


$ /usr/sbin/ddns-confgen -z dyndns.indeedgeek.de
# To activate this key, place the following in named.conf, and
# in a separate keyfile on the system or systems from which nsupdate
# will be run:
key "ddns-key.dyndns.indeedgeek.de" {
    algorithm hmac-sha256;
      secret "oVS2b4zobrziln4vhMCQ3o/sA/VN1tsczlAHDYRUatM=";
};

# Then, in the "zone" definition statement for "dyndns.indeedgeek.de",
# place an "update-policy" statement like this one, adjusted as 
# needed for your preferred permissions:
update-policy {
      grant ddns-key.dyndns.indeedgeek.de zonesub ANY;
};

# After the keyfile has been placed, the following command will
# execute nsupdate using this key:
nsupdate -k 

Und genau so wie das Tool uns es sagt, tun wir es auch. Den ersten Teil in eine Datei schreiben und die Rechte restriktiv genug setzten (später greifen wir mit dem Bachscript darauf zu). Zudem noch einmal in die Bind Config übernehmen. Den zweiten Teil ändern wir etwas für die Konfiguration ab. Wir machen die Regel etwas restriktiver. Der Key darf bei uns nur A-Records der Subdomain von dyndns.indeedgeek.de bearbeiten:


zone "dyndns.indeedgeek.de" {
  type master;
  file "/var/cache/bind/db.dyndns.indeedgeek.de";
  update-policy {
    grant ddns-key.dyndns.indeedgeek.de subdomain dyndns.indeedgeek.de. A;
  };
};

Damit sagen wir unser DNS Server ist der Master-Server für alles unter dyndns.indeedgeek.de. Die Datei /var/cache/bind/db.dyndns.indeedgeek.de gibt dabei an welche Hosts in dieser so genannten Zone zu finden sind. Die Datei muss einmal angelegt werden, bekümmert uns danach aber nicht weiter:


$ORIGIN .
$TTL 60 ; 1 minute
dyndns.indeedgeek.de  IN SOA  ns.indeedgeek.de. hostmaster.indeedgeek.de. (
        2010039432 ; serial
        3600       ; refresh (1 hour)
        1800       ; retry (30 minutes)
        2419200    ; expire (4 weeks)
        1800       ; minimum (30 minutes)
        )
      NS  ns.indeedgeek.de.
$ORIGIN dyndns.indeedgeek.de.

Wichtig dabei: Auf Zugriffsrechte achten! Der bind User muss sowohl lesen als auch schreibend darauf zugreifen dürfen. Die TTL gibt an wie schnell die Informationen veralten. 1 Minute ist schon sehr kurz, allerdings möchte ich auch bei schnellen Updates keine Caching-Probleme bekommen. Die restlichen Zeiten müssen uns für unseren Anwendungsfall nicht interessieren.

So die Idee ist nun eine PHP Seite zu bauen welche die Daten in die Datenbank einträgt und ein Bash Script welches die Daten ausliest und Bind übergibt.

Dazu habe ich eine Tabelle in der Datenbank angelegt. Sicher gibts dafür genug andere Möglichkeiten die Daten zwischen zu speichern aber fand diese gar nicht so schlecht.


CREATE TABLE IF NOT EXISTS `userdomains` (
  `username` varchar(20) NOT NULL,
  `domain` varchar(80) NOT NULL,
  `password` varchar(32) NOT NULL,
  `ip` varchar(15) NOT NULL,
  `lastupdate` datetime NOT NULL,
  PRIMARY KEY (`domain`),
  KEY `username` (`username`)
)

Dann das Bash-Script welches auch recht simpel ist:


#!/usr/bin/env bash
COMMAND=$1
DOMAIN=$2
IP=$3
KEY=/etc/bind/dyndns.key
TIMEOUT="60"

ZONE="dyndns.indeedgeek.de"

set -e

update_from_param() {
  D=$1
  I=$2
  echo -e "
  update delete $D.$ZONE A
  update add $D.$ZONE $TIMEOUT A $I
  show
  send" | /usr/bin/nsupdate -k $KEY
}

update_from_mysql() {
  for I in $(
      echo "SELECT CONCAT(domain, '-', ip)
            FROM system_ddns.domainupdates
            WHERE lastupdate > \""$(date +"%Y-%m-%d  %H:%M:%S" -d "-5 minutes")"\";" | 
              /usr/bin/mysql -psecretpw -udyndns -N); do
    update_from_param ${I/-/ }
  done
}

case $COMMAND in
"mysql")
   update_from_mysql;;
"update")
   update_from_param $DOMAIN $IP;;
*)
   echo "no command"; exit 1;;
esac

Das Script erwartet ein bis drei Parameter. Entweder der erste Parameter ist mysql dann werden die Daten aus der Datenbank an Bind übergeben oder der erste Parameter ist update dann wird als zweiter und dritter Parameter Domain und IP erwartet, welche dann in Bind gesetzt werden.

Die Schnittstelle zu Bind bietet das Tool nsupdate. Mit diesem kann man update Requests an Bind schicken um Einträge hinzuzufügen oder zu verändern. In der Funktion update_from_param sieht man dies recht gut. Zuerst wird der alte Eintrag mittels delete gelöscht und anschließend mittels add eingefügt.

Die Funktion update_from_param ist etwas schwieriger. Hier updaten wir wir alle Einträge in der Datenbank die in den letzten fünf Minuten verändert wurden.

Das Programm nslooup erwartet einen Key den wir bereits bei der Bind Konfiguration übertragen haben. Da geben wir nun einfach die Datei an welche wir zu Beginn erstellt haben.

Testen kann man es recht einfach: updateip update test 127.0.0.1. Anschließend sollte in der Datei /var/cache/bind/db.dyndns.indeedgeek.de korrekt der Host eingetragen sein (evtl mit einem Delay von bis zu 15 min wegen Caching der Datei) und wenn wir mittels dig test.dyndns.indeedgeek.de abfragen auch die 127.0.0.1 liefern.

Zuletzt noch das PHP Script welches das alles zusammen steckt. Ich habe mich dazu mal rudimentär an die API-Dokumentation von dyndns.org gehalten. Das Script ist nicht sonderlich kompliziert – wenn auch etwas länger weil ich versucht habe, alle denkbaren Parameter-Möglichkeiten abzufangen. So wird Username/Password als Parameter als auch über Basic-Auth unterstützt. Die IP kann mit angegeben werden oder ansonsten wird die des Aufrufenden verwendet. Der Hostname kann komplett angegeben werden oder nur der vorderste Teil. Je nachdem wie der Router das gerne hätte.

Das Script ist hier zu finden.

Ich hab das System nun schon seit zwei Jahren am laufen ohne Probleme damit zu haben. Alle Router mit denen ich es bisher getestet haben sind ohne Probleme damit klar gekommen. Auch wenn man zu Beginn ein paar Minuten spielen muss, bis man herausfindet wie der Router am besten die Parameter verarbeitet. Ob er SSL kann und Auth-Basic. Ob er eine IP braucht oder ein FQDN.

Vielleicht habt ihr Lust bekommen und bastelt nun selbst ein bisschen. Oder habt Ideen was man damit so alles anstellen kann?

Comments:

(howto comment?)