diff -r d50f6874b7ab -r 7e472c559b36 update-serial.pl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/update-serial.pl Tue Dec 21 14:03:18 2010 +0100 @@ -0,0 +1,566 @@ +#!/usr/bin/perl + +use strict; +use warnings; +use FindBin; +use File::Basename; + +sub del_double { + + # entfernt doppelte eintraege in einer liste + my %all; + grep { $all{$_} = 0 } @_; + return (keys %all); +} + +sub read_conf { + + # liest die Konfiguration ein + my @configs = ("$FindBin::Bin/dnstools.conf", "/etc/dnstools.conf"); + our %config; + + for (grep { -f } @configs) { + open(CONFIG, $_) or die "Can't open $_: $!\n"; + } + unless (seek(CONFIG, 0, 0)) { + die "Can't open config (searched: @configs)\n"; + } + while () { + chomp; + s/#.*//; + s/\t//g; + s/\s//g; + + next unless length; + my ($cname, $ccont) = split(/\s*=\s*/, $_, 2); + $config{$cname} = $ccont; + } + close(CONFIG); +} + +sub add_argv { + + # prueft ob zonen aus ARGV verwaltete zonen sind + # und fuegt sie, falls ja in die liste @new_serial ein + our @new_serial; + our $master_dir; + my $zone; + + for (@ARGV) { + chomp($zone = `idn --quiet "$_"`); + if (-e "$master_dir/$zone/$zone") { + push @new_serial, $zone; + } + } +} + +sub changed_zone { + our $master_dir; + our @new_serial; + + for (<$master_dir/*>) { + my $zone = basename($_); + + if (-e "$master_dir/$zone/.stamp") { + my $stamptime = (-M "$master_dir/$zone/.stamp"); + my $filetime = (-M "$master_dir/$zone/$zone"); + if ($stamptime > $filetime) { + push @new_serial, $zone; + print " * $zone: zonedatei wurde geaendert\n"; + } + } + else { + print " * $zone: keine .stamp-datei gefunden\n" + ; # NOCH IN NEW_SERIAL PUSHEN + push @new_serial, $zone; + } + } + +} + +sub sign_end { + our $sign_alert_time; # die zeit zwischen dem ende und der neuen + # signierung (siehe externe konfiguration) + our $master_dir; + our @new_serial; + + # erzeugt $time (die zeit ab der neu signiert werden soll) + chomp(my $unixtime = `date +%s`); + $unixtime = $unixtime + (3600 * $sign_alert_time); + my $time = `date -d \@$unixtime +%Y%m%d%H`; + + ## vergleicht fuer alle zonen im ordner $master_dir mit einer + ## .signed-datei den zeitpunkt in $time mit dem ablaufdatum der + ## signatur, welcher aus der datei .signed ausgelesen wird. + for (<$master_dir/*>) { + s#($master_dir/)(.*)#$2#; + my $zone = $_; + + if (-e "$master_dir/$zone/$zone.signed") { + open(ZONE, "$master_dir/$zone/$zone.signed"); + my @zone_sig_content = ; + close(ZONE); + + for (@zone_sig_content) { + if (m#SOA.*[0-9]{14}#) { + s#.*([0-9]{10})([0-9]{4}).*#$1#; + if ($_ < $time) { + push @new_serial, $zone; + } + } + } + } + } +} + +sub sign_zone { + + # signiert die zonen und erhoeht den wert in der keycounter-datei + our @new_serial; + our $master_dir; + my $zone; + my $kc; + + for (&del_double(@new_serial)) { + $zone = $_; + + unless (-e "$master_dir/$zone/.index.zsk") { + next; + } + + chdir "$master_dir/$zone"; + if (`dnssec-signzone $zone 2>/dev/null`) { + print " * $zone neu signiert \n"; + + # erhoeht den keycounter + if ("$master_dir/$zone/.keycounter") { + open(KC, "$master_dir/$zone/.keycounter"); + $kc = ; + close(KC); + $kc += 1; + } + else { + $kc = 1; + } + open(KC, ">$master_dir/$zone/.keycounter"); + print KC $kc; + close(KC); + } + else { print "$zone konnte nicht signiert werden \n"; } + } +} + +sub update_serial { + our $master_dir; + our @new_serial; + chomp(my $date = `date +%Y%m%d`); + my @new_content; + my $sdate; + my $scount; + my $serial; + + for (&del_double(@new_serial)) { + + # erhoeht den serial + my $zone = $_; + my $file = "$master_dir/$zone/$zone"; + my @new_content = (); + + open(SER, "<$file") or die "$file: $!\n"; + for () { + if (/^\s+(\d+)(\d{2})\s*;\s*serial/i) { + $sdate = $1; + $scount = $2; + $serial = "$sdate$scount"; + if ($date eq $sdate) { + $scount++; + } + else { + $sdate = $date; + $scount = "00"; + } + } + if ($serial) { + s/$serial/$sdate$scount/; + } + push @new_content, $_; + } + close(SER); + + open(RES, ">$file") or die "$file: $!\n"; + print RES @new_content; + close(RES); + print " * $zone: serial erhoeht \n"; + + open(STAMP, ">$master_dir/$zone/.stamp") + or die "$master_dir/$zone/.stamp: $!\n"; + close(STAMP); + print " * $zone: stamp aktualisiert \n"; + } +} + +sub mk_zone_conf { + + # erzeugt eine named.conf-datei aus den entsprechenden vorlagen. + our $bind_dir; + our $conf_dir; + + open(TO, ">$bind_dir/named.conf.zones") + or die "$bind_dir/named.conf.zones: $!\n"; + while (<$conf_dir/*>) { + open(FROM, "$_") or die "$_: $! \n"; + print TO ; + close(FROM); + } + close(TO); + print "** zonekonfiguration erzeugt\n"; +} + +sub update_index { + + # aktualisiert die indexzone; + our @new_serial; + our $indexzone; + our $master_dir; + my @iz_content_old; + my @iz_content_new; + + open(INDEXZONE, "$master_dir/$indexzone/$indexzone") + or die "$master_dir/$indexzone/$indexzone: $!\n"; + @iz_content_old = ; + close(INDEXZONE); + + for (@iz_content_old) { + unless (m#ZONE::#) { + push @iz_content_new, $_; + } + } + + for my $dir (glob "$master_dir/*") { + my $zone = basename($dir); + my $info_end = "::sec-off"; + + if (-e "$dir/.keycounter") { + $info_end = "::sec-on"; + } + + my $iz_line = "\t\tIN TXT\t\t\"ZONE::$zone$info_end\"\n"; + + push @iz_content_new, $iz_line; + } + + open(INDEXZONE, ">$master_dir/$indexzone/$indexzone") + or die "$master_dir/$indexzone/$indexzone: $!\n"; + print INDEXZONE @iz_content_new; + close(INDEXZONE); + + # fuegt die index-zone in die liste damit der serial erhoet wird + push @new_serial, $indexzone; + + print "** index-zone aktualisiert \n"; +} + +sub file_entry { + + # prueft jede domain, die ein verzeichnis in $master_dir hat, ob sie + # dnssec nutzt. + # passt die eintraege in $config_file falls noetig an. + our $master_dir; + our $conf_dir; + + while (<$master_dir/*>) { + s#($master_dir/)(.*)#$2#; + my $zone = $_; + my $zone_file = "$master_dir/$zone/$zone"; + my $conf_file = "$conf_dir/$zone"; + my @c_content; + + unless (-f "$conf_file") { + die "$conf_file: $! \n"; + } + + if (-e "$master_dir/$zone/.keycounter") { + open(FILE, "<$conf_file") or die "$conf_file: $!\n"; + @c_content = ; + close(FILE); + for (@c_content) { + if (m{(.*)($zone_file)(";)}) { + print + " * zonekonfiguration aktualisiert ($2 ==> $2.signed)\n"; + $_ = "$1$2.signed$3\n"; + } + } + open(FILE, ">$conf_file") or die "$conf_file: $!\n"; + print FILE @c_content; + close(FILE); + } + else { + open(FILE, "<$conf_file") or die "$conf_file: $!\n"; + @c_content = ; + close(FILE); + for (@c_content) { + if (m{(.*)($zone_file)\.signed(.*)}) { + print + " * zonekonfiguration aktualisiert ($2.signed ==> $2)\n"; + $_ = "$1$2$3\n"; + } + } + open(FILE, ">$conf_file") or die "$conf_file: $!\n"; + print FILE @c_content; + close(FILE); + } + } +} + +sub server_reload { + if (`rndc reload`) { print "** reload dns-server \n" } +} + +sub to_begin_ro { + + # gibt alle zonen mit abgelaufenen keycounter in die liste @begin_ro_list + our @begin_ro_list; + our $master_dir; + our $key_counter_end; + our @new_serial; + my $zone; + + while (<$master_dir/*>) { + chomp($zone = $_); + my $key; + + unless (-f "$zone/.keycounter") { next; } + + open(KEY, "$zone/.keycounter") or die "$zone/.keycounter: $!\n"; + $key = ; + close(KEY); + + # vergleicht den wert aus der keycount-datei mit dem wert aus der + #dnstools.conf (key_counter_end) + if ($key_counter_end <= $key) { + $zone =~ s#($master_dir/)(.*)#$2#; + push @begin_ro_list, $zone; + } + } +} + +sub to_end_ro { + + # funktion ueberprueft ob ein keyrollover fertig ist + # die bedingung dafuer ist das: + # - eine datei .index.zsk vorhanden ist + # - die datei .index.zsk vor mehr x stunden geaendert wurde + # - die datei .index.zsk ueber mehr als zwei zeilen gross ist + our $master_dir; + our @end_ro_list; + our $ablauf_zeit; + chomp(my $now_time = `date +%s`); + + for (<$master_dir/*>) { + my $zone = $_; + $zone =~ s#($master_dir/)(.*)#$2#; + + my @index = (); + my $index_wc; + my @status; + + # prueft nach der ".index.zsk"-datei und erstellt den zeitpunkt + # an dem das key-rollover endet. - $status[9] + if (-e "$master_dir/$zone/.index.zsk") { + @status = stat("$master_dir/$zone/.index.zsk"); + $status[9] += (3600 * $ablauf_zeit); + } + else { next; } + + # $status[9] ist der zeitpunkt an dem der key-rollover endet + # prueft ob das key-rollover-ende erreicht ist + unless ($status[9] < $now_time) { next; } + + # prueft die anzahl der schluessel in der .index.zsk + open(INDEX, "$master_dir/$zone/.index.zsk") + or die "$master_dir/$zone/.index.zsk: $!\n"; + @index = ; + $index_wc = @index; + close(INDEX); + if ($index_wc > 1) { push @end_ro_list, $zone; } + } +} + +sub begin_ro { + + # anfang des key-rollovers + our @begin_ro_list; + our $master_dir; + our @new_serial; + + for (&del_double(@begin_ro_list)) { + + #erzeugt zsks + my $zone = $_; + my $zpf = "$master_dir/$zone"; + my @index; + + chdir "$zpf" or die "$zpf: $!\n"; + my $keyname = `dnssec-keygen -a RSASHA1 -b 512 -n ZONE $zone`; + + open(INDEX, ".index.zsk") or die "$zpf/.index.zsk: $!\n"; + @index = ; + close(INDEX); + + push @index, $keyname; + if (@index > 2) { shift(@index); } + + open(INDEX, ">.index.zsk") or die "$zpf/.index.zsk: $!\n"; + print INDEX @index; + close(INDEX); + + chomp($keyname); + print " * $zone: neuer ZSK $keyname erstellt\n"; + + open(KC, ">.keycounter") or die "$zpf/keycounter: $!\n"; + print KC "0"; + close(KC); + + &kill_useless_keys($zone); + &key_to_zonefile($zone); + push @new_serial, $zone; + } +} + +sub key_to_zonefile { + + # die funktion fugt alle schluessel in eine zonedatei + our $master_dir; + my $zone = $_[0]; + my $zpf = "$master_dir/$zone"; + my @old_content; + my @new_content = (); + + open(ZONEFILE, "<$zpf/$zone"); + @old_content = ; + close(ZONEFILE); + + for (@old_content) { + unless (m#INCLUDE.*key#) { push @new_content, $_; } + } + + for (<$zpf/*>) { + if (m#(.*\/)(K.*\.key)#) { + push @new_content, "\$INCLUDE \"$2\"\n"; + } + } + open(ZONEFILE, ">$zpf/$zone") or die "$zpf/$zone: $!\n"; + print ZONEFILE @new_content; + close(ZONEFILE); +} + +sub kill_useless_keys { + + # die funktion loescht alle schluessel die nicht in der index.zsk + # der uebergebenen zone stehen + our $master_dir; + my $zone = $_[0]; + my @keylist = (); + my $zpf = "$master_dir/$zone"; + + open(INDEX, "<$zpf/.index.zsk") or die "$zpf/.index.zsk: $!\n"; + @keylist = ; + close(INDEX); + open(INDEX, "<$zpf/.index.ksk") or die "$zpf/.index.ksk: $!\n"; + push @keylist, ; + + # kuerzt die schluessel-bezeichnung aus der indexdatei auf die id um sie + # besser vergleichen zu koennen. + for (@keylist) { + chomp; + s#K.*\+.*\+(.*)#$1#; + } + + # prueft alle schluesseldateien (ksk, zsk) ob sie in der jeweiligen + # indexdatei beschrieben sind. wenn nicht werden sie geloescht. + for (`ls $master_dir/$zone/K*[key,private]`) { + chomp; + my $file = $_; + my $rm_count = 1; + my $keyname; + for (@keylist) { + if ($file =~ /$_/) { $rm_count = 0; } + } + if ($rm_count == 1) { + unlink "$file"; + if ($file =~ /$zpf\/(.*\.key)/) { + print " * $zone: Schluessel $1 entfernt \n"; + } + } + } +} + +sub end_ro { + our @end_ro_list; + our $master_dir; + our @new_serial; + my @content; + + for (@end_ro_list) { + my $zone = $_; + my $count = 0; + my @content; + my $last_key; + + open(INDEX, "<$master_dir/$zone/.index.zsk"); + @content = ; + close(INDEX); + + for (@content) { + $count++; + $last_key = $_; + } + if ($count > 1) { + open(INDEX, ">$master_dir/$zone/.index.zsk"); + print INDEX $last_key; + close(INDEX); + } + &kill_useless_keys($zone); + &key_to_zonefile($zone); + push @new_serial, $zone; + } +} + +&read_conf; + +our %config; +our @new_serial; # liste fuer neuen serial +our @begin_ro_list; # liste mit zonen deren key-rollover beginnt +our @end_ro_list; # liste mit zonen deren key-rollover fertig ist +our $master_dir = $config{master_dir}; +our $bind_dir = $config{bind_dir}; +our $conf_dir = $config{zone_conf_dir}; +our $sign_alert_time = $config{sign_alert_time}; +our $indexzone = $config{indexzone}; +our $key_counter_end = $config{key_counter_end}; +our $ablauf_zeit = $config{abl_zeit}; + +&add_argv; +&changed_zone; +&sign_end; + +&to_begin_ro; # prueft nach beginnenden rollover-verfahren +&to_end_ro; # prueft nach endenden rollover-verfahren + +if (@begin_ro_list) { + &begin_ro; # eine rollover-beginn-sequenz +} + +if (@end_ro_list) { + &end_ro; # eine rollover-end-squenz +} + +if (@new_serial) { + &update_index; # index zone aktuallisieren + &update_serial; # serial aktuallisieren + &sign_zone; # zone signieren +} + +&file_entry; # bearbeitet die file-eintraege der konfigurations-datei +&mk_zone_conf; # konfiguration zusammenfuegen +&server_reload; # server neu laden