DNS Replikation

Für das Betreiben einer Domain sind min. zwei Nameserver in verschiedenen Subnetzen notwendig. Viele Serverbetreiber besitzen jedoch nur einen Server und/oder möchten einen DNS Server in einem Netz eines anderen Anbieters anbieten.

Aus diesen Grund haben sich schon viele DNS Partnerschaften gebildet. Das Problem an der Sache ist, das jede Domain manuel gepflegt werden muss. Das hat einen hohen Zeitaufwand zur folge. Alternative Möglichkeiten wie das Austauschen von Konfigurationsdateien könenen den Ausfall eines DNS-Server (z. B. durch eine fehlerhafte Konfigurationsdatei) zur folge haben. Auch ist man hier an einen DNS Server gebunden.

Das folgende Skript liest bereitgestellte Informationen (XML-Datei) aus und bereitet diese in einen für den Nameserver (hier Bind) verwertbares Format auf. Das Umschreiben auf andere DNS-Daemons ist an sich möglich.

Wenn du keinen Partner zum tauschen von DNS Informationen hast kannst du das gerne auch mit mir machen. Nähere Informationen findest du hier.

Die XML-Grunddatei

Grundlage für den Austausch der Zonefiles bildet eine XML-Datei, in der steht, welche Domains getauscht werden sollen und welcher Server der DNS Master ist.

Die Datei hat folgenden Aufbau:

domains.xml:

<secondarydns>
   <sdns1>
     <domain>domain1.tld</domain>
     <master>1.2.3.4</master>
   </sdns1>
   <sdns2>
     <domain>zweite-dom.tld</domain>
     <master>9.9.9.9</master>
   </sdns2>
</secondarydns>

Im Container "secondarydns" werden die einzelnen Domains behandelt. sdns1 bis sdnsx beinhalten die entsprechenden Informationen zu den einzelnen Domains.

Unter "domain" wird der Name der Domain angegeben. Für diese Domain wird dann das Zonefile ausgetauscht. Unter "master" steht die Information zum DNS Master Server der die Master-Zone der entsprechenden Domain beinhaltet.

Das Skript

Bei dem Skript handelt es sich um ein PHP-Script das auf der Unix Shell (z. B. auch per Cron) ausgeführt werden kann.

skript.php:

#!/usr/bin/php
#####################################################
# DNS Replikator Script                             #
#                                                   #
# Version 0.2 - Quelle: www.tobias-bauer.de         #
# URL: http://linux.tobias-bauer.de/dnsrep.html     #
#                                                   #
# Autor: Tobias Bauer                               #
#                                                   #
# Das erzeugte Skript steht unter der GNU GPL!      #
#                                                   #
# ACHTUNG! Die Benutzung des Skriptes erfolgt auf   #
# eigene Gefahr! Ich übernehme keinerlei Haftung    #
# für Schäden die durch dieses Skript entstehen!    #
#                                                   #
#####################################################
<?php

$file = $argv[1];
$output = $argv[2];
$currentTag = "";
$currentDomain = "";
$currentMaster = "";

function startElement($parser, $name, $attrs) {
  global $currentTag;

  $currentTag = $name;  
}

function endElement($parser, $name) {
  global $currentTag;

  $currentTag = "";
}

function characterData($parser, $data) {
  global $currentTag, $currentDomain, $currentMaster;

  switch($currentTag) {
  case "DOMAIN":
    $currentDomain = $data;
    break;
  case "MASTER":
    $currentMaster = $data;
    writeZone();
    break;
  default:
  }
}

function writeZone() {
  global $fw, $currentDomain, $currentMaster;

  if(!empty($currentDomain) && !empty($currentMaster)) {
    $str = "\nzone \"".$currentDomain."\" in {\n type slave;\n file \"/etc/bind/zones/".$currentDomain.".zone\";\n allow-query { any; };\n masters { ".$currentMaster."; };\n};\n";

    fwrite($fw, $str);

  // Fehlerhandling
  } else {
    echo("not enough zone infos...");
  }

  $currentDomain = "";
  $currentMaster = "";
}


if(empty($argv[1]) || empty($argv[2])) {
  die("Error: ".$argv[0]." {xml source} {zone destination}");
}


$xml_parser = xml_parser_create();
xml_set_element_handler($xml_parser, "startElement", "endElement");
xml_set_character_data_Handler($xml_parser, "characterData");
if(!($fp = fopen($file, "r"))) {
  die("could not open XML input");
}
if(!($fw = fopen($output, "w+"))) {
  die("could not open Zonefile");
}

while($data = fread($fp, filesize($file))) {
    if(!xml_parse($xml_parser, $data, feof($fp))) {
    die(sprintf("XML error: %s at line %d",
        xml_error_string(xml_get_error_code($xml_parser)),
        xml_get_current_line_number($xml_parser)));
  }
}

xml_parser_free($xml_parser);
@fclose($fp);
@fclose($fw);
?>

Das Skript erzeugt eine Datei mit den notwendigen Informationen für den Bind Nameserver.

Aufruf des Skripts

Nahmen wir an, das XML-File wird unter der Adresse domain.tld/dns.xml bereitgestellt. Auf dem Server, der als DNS Slave funkieren soll, wird das PHP Skript ausgeführt und es soll eine Datei "named.conf.zones" unter /etc/bind abgelegt werden. Unser PHP-Skript heißt dns_rep.php.

Der Aufruf müsste nun wie folgt lauten: ./dns_rep.php domain.tld/dns.xml /etc/bind/named.conf.zones

Dieser Aufruf kann natürlich auch per Cron gestartet werden. Nach der Erstellung der Datei muss Bind neu gestartet werden. Diese Funktion habe ich absichtlich aus dem Skript heraus gelassen, da es dadruch möglich ist, mehrere XML-Dateien zu verwenden und danach erst den Nameserver neu zu starten.

Erweiterter Aufruf

Natürlich ist der Aufruf wie oben genannt möglich. Jedoch gibt es oft Situationen in denen ein einfacher Aufruf nicht ausreichend ist und in denen mehrere Optionen beachtet werden müssen. Möglichkeiten sind z. B.

  • Laden der XML-Dateien von deiner mit HTTPS gesicherten Seite
  • Absicherung gegen Ausfall des Webservers auf dem das XML-File liegt
  • Überprüfung auf Änderungen um den Nameserver gezielt neu zu starten
  • mehrere XML-Dateien sollen bearbeitet werden

Für solche Optionen empfiehlt es sich, den Aufruf in eine Shell-Skript Datei zu legen. Hier gebe ich ein kleines Beispiel wie so ein Skript aussehen könnte.

Die Sicherungen der XML-Dateien werden in den Ordner "files" abgelegt. Dieser muss vorhanden sein und das Skript muss darauf die entsprechenden Berechtigungen haben. Temporäre Dateien werden unter "/tmp" abgelegt. Auch darauf müssen die entsprechenden Berechtigungen vorhanden sein. Alternativ kann das Skript jederzeit natürlich auch angepasst werden.

Vor dem Neustart des bind Server wird die Konfiguration geprüft. Im Falle einer Fehlkonfiguration wird eine E-Mail an den Administrator geschickt und der Server nicht neu gestartet. Das heißt, die alte Konfiguration läuft weiter wie bisher. Das bedeutet aber auch, dass keine neuen Zonen geladen werden.

dns_update.sh:

#########################################################
# DNS Replikator Script Caller                          #
#                                                       #
# Version 0.5 - Quelle: www.tobias-bauer.de             #
# URL: https://www.tobias-bauer.de/dns-replikation.html #
#                                                       #
# Autor: Tobias Bauer - mail (at) tobias - bauer dot de #
#                                                       #
# Das erzeugte Skript steht unter der GNU GPL!          #
#                                                       #
# ACHTUNG! Die Benutzung des Skriptes erfolgt auf eigene#
# Gefahr! Ich übernehme keinerlei Haftung Schäden die   # 
# durch das Skript entstehen!                           #
#                                                       #
#########################################################
#!/bin/sh



# Lock File schon vorhanden
if [ -e "/tmp/dns_gen_zone.lock" ]
then
  exit 0
else
  touch /tmp/dns_gen_zone.lock
fi

# XML Datei laden, pruefen und parsen
wget https://www.domain.tld/dns.xml -O input.xml -q --no-check-certificate > /dev/null
if [ `wc -c input.xml | cut -f1 -d " "` -gt 0 ]
then
  if [ `diff input.xml files/tempstore.xml | wc -l` -gt 0 ]
  then
    touch /tmp/restart_named.tmp
  fi

  mv input.xml files/tempstore.xml
fi
/opt/reksys/dnsxml_parse.php files/tempstore.xml /etc/named.tmp
cat /etc/named.tmp >> /etc/named.conf.zones
rm -f input.xml



# Bind ggf. neustarten
if [ -e "/tmp/restart_named.tmp" ]
then
  if [`/usr/sbin/named-checkconf /etc/named.conf` -eq '']
  then
     /etc/init.d/named restart
   else
     /usr/sbin/named-checkconf /etc/named.conf > /tmp/bind.error
     mail sysadmin@domain.tld -s "Fehler in Bind Konfiguration!" < /tmp/bind.error
     rm -rf /tmp/bind.error
  fi
  rm -rf /tmp/restart_named.tmp
fi



# Aufraeumen und Nacharbeiten
rm -rf /etc/named.tmp
rm -rf /tmp/dns_gen_zone.lock