DNSSEC mit Debian/squeeze: dnssec-tools, bind9

Fangen wir mal so an:

dnssec cat

Ich habe in den letzten zwei Wochen sehr sehr viel getestet und mich dann gestern dazu entschlossen, DNSSEC für alle meine privaten Domains live zu stellen. Im folgenden dann kurz - primär für mich als Gedächtnisstützte, aber vielleicht ja auch für andere Leute interessant - die Dokumentation dazu, was ich alles dafür tun musste.

Dokumentation zu DNSSEC gibt es zahlreich, aber sie krankt, ebenso wie die vorhandene Tool-Unterstützung dafür, teilweise sehr in den Bereichein Aktualität oder Zuverlässigkeit. Über Google stolpert man recht schnell über das DNSSEC-HOWTO von nlnetlabs.nl - und wer sich mit DNSSEC noch gar nicht beschäftigt hat, der findet hier einen guten Einstieg. Ebenso empfehlenswert ist dnssec.net, gespickt mit Dutzenden hilfreicher weiterführender Links. Da ich die Umsetzung bei mir mit BIND vorgenommen habe ist es natürlich auch sinnvoll, das Bind 9 Administrator Reference Manual zur Hand zu haben. Den Part der Tool-Unterstützung habe ich mit den DNSSEC-Tools abgedeckt, auch wenn ich eine wenig erfolgreiche Iteration meines Test-Zyklusses mit OpenDNSSEC durchgeführt habe. Und auch mit DNSSEC Look-aside Validation (aka DLV) sollte man sich mal beschäftigt haben .

Zu den Voraussetzungen: Es ist sinnvoll, seine eigene DNS-Infrastruktur zu betreiben (an dieser Stelle mal wieder herzlichen Dank an Andreas Scherbaum, seines Zeichens nicht nur PostgreSQL-Gott, sondern auch Spender des dritten Nameservers für meine Domains). Ebenso ist die Einführung von DNSSEC nichts für Unerfahrene Administratoren, man sollte sich mit DNS, mit Unix und mit Testprozeduren auskennen. Ein dediziertes Test-Lab bietet sich an, in Ermangelung dessen geht natürlich auch eine Sammlung virtueller Maschinen. Und es erfordert einen gewissen Mut, den endgültigen Switchover durchzuführen. Sinnvoll ist ebenso ein Registrar, der die DNS-Keys denn auch bei der Registry hinterlegt, daran arbeite ich gerade - und solange laufe ich via DLV.

Als Scope hatte ich für mich persönlich festgelegt, daß ich keine Software selber installieren und keine Backports durchführen will. Diese Entscheidung hat bei mir dann leider OpenDNSSEC aus dem Rennen geworfen - die Version in Debian/stable hat einfach zu viele Fehler. Das fing damit an, daß der ods-auditor mit meinen DKIM-RRs nicht klargekommen ist und hörte damit auf, daß die signierten Zonen unvollständig waren. Neuere Versionen sind hier bestimmt zuverlässiger, so signiert z.B. ICANN die ip6.arpa-Zone mit einem OpenDNSSEC-Cluster (ok, gut, da war neulich mal Land unter, aber trotzdem) und z.B. auch .fr ist mit OpenDNSSEC verwaltet (schön zu erkennen an den mindestens fünf DNSKEY-RRs in der Zone, KSK live, KSK emergency, ZSK live, ZSK emergency, ZSK prepublish - OpenDNSSEC kann da gierig werden). Ebenso musste ich zkt außen vor lassen. Meine Wahl fiel dann jedoch auf die klassischen dnssec-tools und so war der erste Schritt denn auch die Nachinstallation der benötigten Pakete (bind9 hatte ich natürlich schon):

apt-get install dnssec-tools libnet-dns-sec-perl libmailtools-perl

Der erste Schritt bestand für mich darin, meinen Nameserver DNSSEC-fähig zu machen. Unter Debian liegt die Konfiguration unter /etc/bind. Dort fügt man als erstes drei Zeilen in die Datei named.conf.options ein:

dnssec-enable yes;
dnssec-validation yes;
dnssec-lookaside auto;

Die erste Zeile bewegt BIND dazu, überhaupt DNSSEC zu sprechen, also z.B. die AD und CD-Flags zu unterstützen. Mit derzweiten Anweisung bringt man BIND dazu, DNS-Antworten zu validieren. Von besonderem Interesse ist die dritte Zeile - mit ihr sagt man BIND nämlich, daß man nicht nur die Trust-Chains von der Root-Zone abwärts benutzen will, sondern aktiviert auch die Nutzung von DLV - z.B. eben wenn die Registriy offiziell noch kein DNSSEC kann bzw. die Zone noch nicht signiert wurde wie in .de oder der Registrar einem DNSSEC nicht anbietet. Innerhalb der DLV-Registry wird dann für jede Zone ein DLV-RR hinterlegt (das ist jetzt nur ein Beispiel, keine Sorge, ihr habt nix vergessen):

cite@helena:~$ dig incertum.net.dlv.isc.org dlv +short
40084 8 1 5E8587398749DA4949E0B7B7EF1853AEBFCCC981
40084 8 2 1E7E11988D213F2A23F1438CFD63EC2AEDF3F99B6B6D1917E237CC75 5432BA1A

Damit das unter Debian ohne hässliche Fehlermeldungen beim Start des Nameservers funktioniert, muß man noch eine Zeile an den Anfang der Datei named.conf.local anfügen:

include "/etc/bind/bind.keys";

Die zweite Zeile in der named.conf (dnssec-validation yes;) habe ich übrigens nur da drin stehen, weil die Kiste für die VMs als Resolver auftritt. Das kann ich tun, weil sich meine Clients nicht im geringsten dafür interessieren, ob AD oder AA gesetzt ist. Würden sie das tun, müsste ich mit match-recursive-only und Views in der BIND-Konfiguration arbeiten.

Da ich am Anfang etwas unsicher mit der ganzen DNSSEC-Geschichte war habe ich mir zusätzlich in der named.conf.local eine Debugging-Ausgabe für DNSSEC nach /tmp/dnssec.log eingerichtet:

// dnssec logging
logging {
  channel dnssec_log {             // a DNSSEC log channel
    file "/tmp/dnssec.log" size 20m;
    print-time yes;        // timestamp the entries
    print-category yes;    // add category name to entries
    print-severity yes;    // add severity level to entries
    severity debug 3;      // print debug message <= 3 t
  };
  category dnssec { dnssec_log;  };
};

Jetzt wäre dann der richtige Moment, um ein paar Tests durchzuführen und dabei das Debug-Log im Auge zu behalten. Funktioniert alles wie erwartet, kann man die Änderungen auf allen Nameservern, die die eigenen Zonen verwalten, durchführen und auch dort testen.

Bevor man mit dem DNSSEC-Deployment weitermacht, bietet es sich an, sich Gedanken über die Verzeichnisstruktur zu machen, die man einsetzen möchte. Für jede Zone, die man verwaltet, wird es am Ende eine ganze Reihe von Dateien geben:

  • Die eigentliche Zonendatei - bei mir einfach wie die Zone benannt, also z.B. incertum.net
  • Die signierte Zonendatei mit der Endung .signed, also z.B. incertum.net.signed
  • Mindestens drei Schlüsselpaare (private + public) im Format K$zonename.+$algorithm+$keyid jeweils mit den Endungen .key und .private, also z.B. Kincertum.net.+008+40084.key und Kincertum.net.+008+40084.private
  • Eine Datei mit den DS-Records zu Übermittlung an den Registrar/die Registry, z.B. dsset-incertum.net
  • Ein sogenanntes Key Rollfile, in dem sich die dnssec-tools merken, wann welcher Schlüssel generiert wurde, wie lang die Signaturen, die damit erstellt wurden, gültig sind, ob der Schlüssel gerade im Rollover ist etc. Der Name wäre z.B. incertum.net.krf

Der Dämon, der die Key-Rollovers (also den Prozess einen neuen Key im DNS einzufügen und den alten außer Dienst zu nehmen) durchführt, benötigt ebenfalls eine Steuerdatei (bei mir: all.rollrec). Das ganze kann dann ungefähr so aussehen:

# ls -l *incertum* all.rollrec
-rw-r--r-- 1 root bind  433 Jun 18 14:54 Kincertum.net.+008+24183.key
-rw------- 1 root bind 1012 Jun 18 14:54 Kincertum.net.+008+24183.private
-rw-r--r-- 1 root bind  607 Jun 18 14:54 Kincertum.net.+008+40084.key
-rw------- 1 root bind 1.8K Jun 18 14:54 Kincertum.net.+008+40084.private
-rw-r--r-- 1 root bind  433 Jun 18 14:54 Kincertum.net.+008+46551.key
-rw------- 1 root bind 1012 Jun 18 14:54 Kincertum.net.+008+46551.private
-rw-r--r-- 1 root bind 1.5K Jun 18 14:59 all.rollrec
-rw-r--r-- 1 root bind  169 Jun 18 17:00 dsset-incertum.net
-rw-r--r-- 1 root bind 1.2K Jun 18 17:00 incertum.net
-rw-r--r-- 1 root bind 2.0K Jun 18 17:00 incertum.net.krf
-rw-r--r-- 1 root bind  19K Jun 18 17:00 incertum.net.signed

Ich habe den ganzen Kram nach /etc/bind/forward-zones/ gelegt.

Bevor wir die erste Zone mit DNSSEC signieren sollte man noch einen kleinen Fehler in der Datei /etc/dnssec-tools/dnssec-tools.conf ausbügeln:

--- /tmp/doc/dnssec-tools.conf  2011-06-18 14:16:37.000000000 +0200
+++ /etc/dnssec-tools/dnssec-tools.conf 2011-06-18 14:20:59.000000000 +0200
@@ -28,9 +28,9 @@ zsklength     1024
 random         /dev/urandom


-;
-; Settings for dnssec-signzone.
-;
+#
+# Settings for dnssec-signzone.
+#
 endtime                +2592000                # RRSIGs good for thirty days.

 #

Jetzt könnte man die erste Zone signieren - und ich weise noch ein allerletztes Mal drauf hin, daß man die Dokumentation und die Konfigurationsfiles lesen und verstehen sollte! Das initiale Signieren einer Zone erfordert das manuelle Generieren von Schlüsseln - 1xKSK, 2xZSK. Übrigens verwenden wir derzeit noch /dev/urandom - wenn das bei Euch viele Zonen sind mit vielen Keys, dann solltet ihr einen irgendeine Hardware benutzen, die Euch Entropy zur Verfügung stellt, bevor ihr das ändert! Der Aufruf dazu ist - wenn die Zonen-Datei genauso heißt wie die Zone denkbar einfach:

zonesigner --genkeys incertum.net

Der Aufruf erstellt die Datei incertum.net.signed - und die KÖNNTE man jetzt schon in BIND einlesen, allerdings würde ich diesen Schritt erst am Ende durchführen. Es sollte jetzt auch klar sein, daß sich für die Zukunft der Workflow für das Ändern von DNS-Einträgen anders darstellt: Man editiert die Zonendatei, überprüft sie, signiert sie und sagt BIND erst dann, daß man die Zone gerne neu geladen hätte. Das ist natürlich eine klassische Aufgabe für ein Makefile. Es gibt bestimmt elegantere Lösungen, aber für mich funktioniert:

all: stefan-foerster.de.signed incertum.net.signed billigmail.org.signed

%.signed: %
	named-checkzone $< $<
	zonesigner $<
	rndc reload $<

Damit kann man dann nach Änderungen an den Zonenfiles einfach ein make einwerfen und sich freuen. Man bemerkt übrigens, daß hier der Parameter --genkeys beim zonesigner-Aufruf fehlt.

Jetzt wo wir die ersten Zonen signiert haben wäre es an der Zeit, sich die signierten Zonendateien mal anzusehen. Wir stellen fest, daß sie drei DNSKEY-Einträge enthalten. Zwei davon sollten in den Flags ein 256 3 8 tragen. Damit sind sie sogenannte Zone Signing Keys, also die Schlüssel mit denen wir die RRs der Zone signieren, gekennzeichnet. Signaturen die wir mit diesen Schlüsseln erstellen sind kurzlebig und müssen in sehr regelmäßigen Abständen getauscht werden. Die von uns verwendete Toolchain verwendet dazu einen Key-Rollover mit Pre-Publishing - deswegen gibt es zwei ZSKs. Kommt der Zeitpunkt des Rollovers wird ein neuer Key generiert und der derzeit verwendete in Ruhestand geschickt - natürlich immer mit Augenmerk auf die TTL der Zone. Der Key mit den Flags 257 3 8 ist der Key Signing Key oder KSK. Mit ihm signiert die Toolchain die ZSKs und das ist auch derjenige, der bei der Registry hinterlegt werden muß (Lemma: Ist die erste Nummer eine Primzahl, so geht man von einem KSK aus, sonst von einem ZSK. Die zweite Zahl bezeichnet den Verwendungszweck, 3 steht für DNSSEC. Und die letzte Ziffer bezeichnet den Algorithmus, hier RSA/SHA-256). Aus all dem folgen zwei wichtige Dinge:

  • Man muß Signaturen in regelmäßigen Abständen erneuern.
  • Ein ZSK-Rollover ist relativ unkompliziert da ich ihn selbst ausführen kann, bei einem KSK dagegen muß der Registry neues Schlüsselmaterial mitgeteilt werden.

Unsere Toolchain unterstützt uns beim Key-Rollover mit den Tools rollderd(1p) und rollctl(1p) Ersteres ist der Dämon, der die Key-Rollovers automatisch durchführt, Signaturen erneuert etc., letzteres ist das Control-Programm dafür. Der rollerd benötigt eine Steuerdatei, die man mit dem Utility rollinit erstellt (die Mailadresse hinter -admin bitte sinnvoll setzen!):

rollinit incertum.net -zone /etc/bind/forward-zones/incertum.net.signed -keyrec /etc/bind/forward-zones/incertum.net.krf -admin cite@example.net >> all.rollrec
rollinit billigmail.org -zone /etc/bind/forward-zones/billigmail.org.signed -keyrec /etc/bind/forward-zones/billigmail.org.krf -admin cite@example.net >> all.rollrec
rollinit stefan-foerster.de -zone /etc/bind/forward-zones/stefan-foerster.de.signed -keyrec /etc/bind/forward-zones/stefan-foerster.de.krf -admin cite@example.net >> all.rollrec

Der Dämon selbst hat kein Init-Skript, ich habe ihn in die /etc/rc.local verfrachtet und starte ihn dort folgendermaßen:

/usr/sbin/rollerd -rrfile /etc/bind/forward-zones/all.rollrec -directory /etc/bind/forward-zones

Die Logausgaben findet man in der Datei /var/log/dnssec-tools/rollerd.log.

Als letzte Komponente der Infrastruktur benötigen wir noch ein Tool, das unsere Zonen im Auge behält. Unsere Toolchain liefert dafür den Donuts Demon mit (was für ein Name, echt mal…). Auch der donutsd hat kein Init-Skript, das ist aber nicht weiter schlimm. Wiederum in der /etc/rc.local:

/usr/sbin/donutsd -i /etc/bind/forward-zones/checkzones.txt

Das File checkzones.txt sieht folgendermaßen aus:

/etc/bind/forward-zones/stefan-foerster.de.signed       stefan-foerster.de      cite@example.net
/etc/bind/forward-zones/incertum.net.signed             incertum.net            cite@example.net
/etc/bind/forward-zones/billigmail.org.signed           billigmail.org          cite@example.net

Sollte klar sein, Zonendatei, Zonenname, Kontaktperson. Der Donut kippt Mails standardmäßig via SMTP auf 127.0.0.1, Port 25 ein. Das Ding ist recht simpel, es prüft die Zone und wenn sich die Ausgabe des gegenwärtigen Runs von der letzten Ausgabe unterscheidet dann schickt er uns eine Mail.

Jetzt haben wir es eigentlich fast geschafft. Bevor wir jedoch Key-Material bei unserer Registry oder z.B. bei der ISC DLV Registry einkippen, müssen wir zum einen in der BIND-Konfiguration noch das unsignierte Zonenfile durch das signierte Zonenfile ersetzen ($serial++ und rndc reload nicht vergessen!), testen, ob die Key-Rollovers funktionieren (siehe Link oben zu rollctl) und mit unserem Registrar den Abstand der KSK-Rollover (kann man in der dnssec-tools.conf anpassen) sowie einen Transportweg des Schlüsselmaterials klären. Und dann, dann ist es auch wirklich geschafft.

Wisst ihr Bescheid. Ich gehe jetzt das Klavierkonzert Nummer vier von Rachmaninov anhören. Schönen Sonntag noch!

Update 1: Folgende Punkte ergänzt:

  • Verwendung von /dev/urandom vs. /dev/random
  • Vermischen von authoritativen Nameservern (geben AA-Bit zurück) und rekursiven Servern (geben AD-Bit zurück).

Update 2 Auf Anregung von Alexander Wirt ein paar Absätze umgebaut und das Makefile überarbeitet.