update-serial.pl
changeset 88 0e1e5027e9c0
parent 52 53c95f2ff0ac
parent 87 6d624831079f
child 90 0b9ba3e760bd
equal deleted inserted replaced
52:53c95f2ff0ac 88:0e1e5027e9c0
     1 #!/usr/bin/perl 
       
     2 
       
     3 use strict;
       
     4 use warnings;
       
     5 use FindBin;
       
     6 use File::Basename;
       
     7 
       
     8 sub del_double {
       
     9 
       
    10     # remove duplicate entries
       
    11     my %all;
       
    12     grep { $all{$_} = 0 } @_;
       
    13     return (keys %all);
       
    14 }
       
    15 
       
    16 sub read_conf {
       
    17 
       
    18     # read the configuration
       
    19     my @configs = ("$FindBin::Bin/dnstools.conf", "/etc/dnstools.conf");
       
    20     our %config;
       
    21 
       
    22     for (grep { -f } @configs) {
       
    23         open(CONFIG, $_) or die "Can't open $_: $!\n";
       
    24     }
       
    25     unless (seek(CONFIG, 0, 0)) {
       
    26         die "Can't open config (searched: @configs)\n";
       
    27     }
       
    28     while (<CONFIG>) {
       
    29         chomp;
       
    30         s/#.*//;
       
    31         s/\t//g;
       
    32         s/\s//g;
       
    33 
       
    34         next unless length;
       
    35         my ($cname, $ccont) = split(/\s*=\s*/, $_, 2);
       
    36         $config{$cname} = $ccont;
       
    37     }
       
    38     close(CONFIG);
       
    39 }
       
    40 
       
    41 sub add_argv {
       
    42     # checked whether the zones in argv are managed zones and
       
    43     #inserted them into the list new_serial
       
    44     our @new_serial;
       
    45     our $master_dir;
       
    46     my $zone;
       
    47 
       
    48     for (@ARGV) {
       
    49         chomp($zone = `idn --quiet "$_"`);
       
    50         if (-e "$master_dir/$zone/$zone") {
       
    51             push @new_serial, $zone;
       
    52         }
       
    53     }
       
    54 }
       
    55 
       
    56 sub changed_zone {
       
    57     our $master_dir;
       
    58     our @new_serial;
       
    59 
       
    60     for (<$master_dir/*>) {
       
    61         my $zone = basename($_);
       
    62 
       
    63         if (-e "$master_dir/$zone/.stamp") {
       
    64             my $stamptime = (-M "$master_dir/$zone/.stamp");
       
    65             my $filetime  = (-M "$master_dir/$zone/$zone");
       
    66             if ($stamptime > $filetime) {
       
    67                 push @new_serial, $zone;
       
    68                 print " * $zone: zonedatei wurde geaendert\n";
       
    69             }
       
    70         }
       
    71         else {
       
    72             print " * $zone: keine .stamp-datei gefunden\n"
       
    73               ;    # NOCH IN NEW_SERIAL PUSHEN
       
    74             push @new_serial, $zone;
       
    75         }
       
    76     }
       
    77 
       
    78 }
       
    79 
       
    80 sub sign_end {
       
    81     our $sign_alert_time;    # the time between the end and the new signing
       
    82                              # (see external configuration)
       
    83     our $master_dir;
       
    84     our @new_serial;
       
    85 
       
    86     # erzeugt $time (die zeit ab der neu signiert werden soll)
       
    87     chomp(my $unixtime = `date +%s`);
       
    88     $unixtime = $unixtime + (3600 * $sign_alert_time);
       
    89     my $time = `date -d \@$unixtime +%Y%m%d%H`;
       
    90 
       
    91     ## vergleicht fuer alle zonen im ordner $master_dir mit einer
       
    92     ## <zone>.signed-datei den zeitpunkt in $time mit dem ablaufdatum der
       
    93     ## signatur, welcher aus der datei <zone>.signed ausgelesen wird.
       
    94     for (<$master_dir/*>) {
       
    95         s#($master_dir/)(.*)#$2#;
       
    96         my $zone = $_;
       
    97 
       
    98         if (-e "$master_dir/$zone/$zone.signed") {
       
    99             open(ZONE, "$master_dir/$zone/$zone.signed");
       
   100             my @zone_sig_content = <ZONE>;
       
   101             close(ZONE);
       
   102 
       
   103             for (@zone_sig_content) {
       
   104                 if (m#SOA.*[0-9]{14}#) {
       
   105                     s#.*([0-9]{10})([0-9]{4}).*#$1#;
       
   106                     if ($_ < $time) {
       
   107                         push @new_serial, $zone;
       
   108                     }
       
   109                 }
       
   110             }
       
   111         }
       
   112     }
       
   113 }
       
   114 
       
   115 sub sign_zone {
       
   116 
       
   117     # signiert die zonen und erhoeht den wert in der keycounter-datei
       
   118     our @new_serial;
       
   119     our $master_dir;
       
   120     my $zone;
       
   121     my $kc;
       
   122 
       
   123     for (&del_double(@new_serial)) {
       
   124         $zone = $_;
       
   125 
       
   126         unless (-e "$master_dir/$zone/.index.zsk") {
       
   127             next;
       
   128         }
       
   129 
       
   130         chdir "$master_dir/$zone";
       
   131         if (`dnssec-signzone $zone 2>/dev/null`) {
       
   132             print " * $zone neu signiert \n";
       
   133 
       
   134             # erhoeht den keycounter
       
   135             if ("$master_dir/$zone/.keycounter") {
       
   136                 open(KC, "$master_dir/$zone/.keycounter");
       
   137                 $kc = <KC>;
       
   138                 close(KC);
       
   139                 $kc += 1;
       
   140             }
       
   141             else {
       
   142                 $kc = 1;
       
   143             }
       
   144             open(KC, ">$master_dir/$zone/.keycounter");
       
   145             print KC $kc;
       
   146             close(KC);
       
   147         }
       
   148         else { print "$zone konnte nicht signiert werden \n"; }
       
   149     }
       
   150 }
       
   151 
       
   152 sub update_serial {
       
   153     our $master_dir;
       
   154     our @new_serial;
       
   155     chomp(my $date = `date +%Y%m%d`);
       
   156     my @new_content;
       
   157     my $sdate;
       
   158     my $scount;
       
   159     my $serial;
       
   160 
       
   161     for (&del_double(@new_serial)) {
       
   162 
       
   163         # erhoeht den serial
       
   164         my $zone        = $_;
       
   165         my $file        = "$master_dir/$zone/$zone";
       
   166         my @new_content = ();
       
   167 
       
   168         open(SER, "<$file") or die "$file: $!\n";
       
   169         for (<SER>) {
       
   170             if (/^\s+(\d+)(\d{2})\s*;\s*serial/i) {
       
   171                 $sdate  = $1;
       
   172                 $scount = $2;
       
   173                 $serial = "$sdate$scount";
       
   174                 if ($date eq $sdate) {
       
   175                     $scount++;
       
   176                 }
       
   177                 else {
       
   178                     $sdate  = $date;
       
   179                     $scount = "00";
       
   180                 }
       
   181             }
       
   182             if ($serial) {
       
   183                 s/$serial/$sdate$scount/;
       
   184             }
       
   185             push @new_content, $_;
       
   186         }
       
   187         close(SER);
       
   188 
       
   189         open(RES, ">$file") or die "$file: $!\n";
       
   190         print RES @new_content;
       
   191         close(RES);
       
   192         print " * $zone: serial erhoeht \n";
       
   193 
       
   194         open(STAMP, ">$master_dir/$zone/.stamp")
       
   195           or die "$master_dir/$zone/.stamp: $!\n";
       
   196         close(STAMP);
       
   197         print " * $zone: stamp aktualisiert \n";
       
   198     }
       
   199 }
       
   200 
       
   201 sub mk_zone_conf {
       
   202 
       
   203     # erzeugt eine named.conf-datei aus den entsprechenden vorlagen.
       
   204     our $bind_dir;
       
   205     our $conf_dir;
       
   206 
       
   207     open(TO, ">$bind_dir/named.conf.zones")
       
   208       or die "$bind_dir/named.conf.zones: $!\n";
       
   209     while (<$conf_dir/*>) {
       
   210         open(FROM, "$_") or die "$_: $! \n";
       
   211         print TO <FROM>;
       
   212         close(FROM);
       
   213     }
       
   214     close(TO);
       
   215     print "** zonekonfiguration erzeugt\n";
       
   216 }
       
   217 
       
   218 sub update_index {
       
   219 
       
   220     # aktualisiert die indexzone;
       
   221     our @new_serial;
       
   222     our $indexzone;
       
   223     our $master_dir;
       
   224     my @iz_content_old;
       
   225     my @iz_content_new;
       
   226 
       
   227     open(INDEXZONE, "$master_dir/$indexzone/$indexzone")
       
   228       or die "$master_dir/$indexzone/$indexzone: $!\n";
       
   229     @iz_content_old = <INDEXZONE>;
       
   230     close(INDEXZONE);
       
   231 
       
   232     for (@iz_content_old) {
       
   233         unless (m#ZONE::#) {
       
   234             push @iz_content_new, $_;
       
   235         }
       
   236     }
       
   237 
       
   238     for my $dir (glob "$master_dir/*") {
       
   239         my $zone     = basename($dir);
       
   240         my $info_end = "::sec-off";
       
   241 
       
   242         if (-e "$dir/.keycounter") {
       
   243             $info_end = "::sec-on";
       
   244         }
       
   245 
       
   246         my $iz_line = "\t\tIN TXT\t\t\"ZONE::$zone$info_end\"\n";
       
   247 
       
   248         push @iz_content_new, $iz_line;
       
   249     }
       
   250 
       
   251     open(INDEXZONE, ">$master_dir/$indexzone/$indexzone")
       
   252       or die "$master_dir/$indexzone/$indexzone: $!\n";
       
   253     print INDEXZONE @iz_content_new;
       
   254     close(INDEXZONE);
       
   255 
       
   256     # fuegt die index-zone in die liste damit der serial erhoet wird
       
   257     push @new_serial, $indexzone;
       
   258 
       
   259     print "** index-zone aktualisiert \n";
       
   260 }
       
   261 
       
   262 sub file_entry {
       
   263 
       
   264     # prueft jede domain, die ein verzeichnis in $master_dir hat, ob sie
       
   265     # dnssec nutzt.
       
   266     # passt die eintraege in $config_file falls noetig an.
       
   267     our $master_dir;
       
   268     our $conf_dir;
       
   269 
       
   270     while (<$master_dir/*>) {
       
   271         s#($master_dir/)(.*)#$2#;
       
   272         my $zone      = $_;
       
   273         my $zone_file = "$master_dir/$zone/$zone";
       
   274         my $conf_file = "$conf_dir/$zone";
       
   275         my @c_content;
       
   276 
       
   277         unless (-f "$conf_file") {
       
   278             die "$conf_file: $! \n";
       
   279         }
       
   280 
       
   281         if (-e "$master_dir/$zone/.keycounter") {
       
   282             open(FILE, "<$conf_file") or die "$conf_file: $!\n";
       
   283             @c_content = <FILE>;
       
   284             close(FILE);
       
   285             for (@c_content) {
       
   286                 if (m{(.*)($zone_file)(";)}) {
       
   287                     print
       
   288                       " * zonekonfiguration aktualisiert ($2 ==> $2.signed)\n";
       
   289                     $_ = "$1$2.signed$3\n";
       
   290                 }
       
   291             }
       
   292             open(FILE, ">$conf_file") or die "$conf_file: $!\n";
       
   293             print FILE @c_content;
       
   294             close(FILE);
       
   295         }
       
   296         else {
       
   297             open(FILE, "<$conf_file") or die "$conf_file: $!\n";
       
   298             @c_content = <FILE>;
       
   299             close(FILE);
       
   300             for (@c_content) {
       
   301                 if (m{(.*)($zone_file)\.signed(.*)}) {
       
   302                     print
       
   303                       " * zonekonfiguration aktualisiert ($2.signed ==> $2)\n";
       
   304                     $_ = "$1$2$3\n";
       
   305                 }
       
   306             }
       
   307             open(FILE, ">$conf_file") or die "$conf_file: $!\n";
       
   308             print FILE @c_content;
       
   309             close(FILE);
       
   310         }
       
   311     }
       
   312 }
       
   313 
       
   314 sub server_reload {
       
   315     if (`rndc reload`) { print "** reload dns-server \n" }
       
   316 }
       
   317 
       
   318 sub to_begin_ro {
       
   319 
       
   320     # gibt alle zonen mit abgelaufenen keycounter in die liste @begin_ro_list
       
   321     our @begin_ro_list;
       
   322     our $master_dir;
       
   323     our $key_counter_end;
       
   324     our @new_serial;
       
   325     my $zone;
       
   326 
       
   327     while (<$master_dir/*>) {
       
   328         chomp($zone = $_);
       
   329         my $key;
       
   330 
       
   331         unless (-f "$zone/.keycounter") { next; }
       
   332 
       
   333         open(KEY, "$zone/.keycounter") or die "$zone/.keycounter: $!\n";
       
   334         $key = <KEY>;
       
   335         close(KEY);
       
   336 
       
   337         # vergleicht den wert aus der keycount-datei mit dem wert aus der
       
   338         #dnstools.conf (key_counter_end)
       
   339         if ($key_counter_end <= $key) {
       
   340             $zone =~ s#($master_dir/)(.*)#$2#;
       
   341             push @begin_ro_list, $zone;
       
   342         }
       
   343     }
       
   344 }
       
   345 
       
   346 sub to_end_ro {
       
   347 
       
   348     # funktion ueberprueft ob ein keyrollover fertig ist
       
   349     # die bedingung dafuer ist das:
       
   350     # - eine datei .index.zsk vorhanden ist
       
   351     # - die datei .index.zsk vor mehr x stunden geaendert wurde
       
   352     # - die datei .index.zsk ueber mehr als zwei zeilen gross ist
       
   353     our $master_dir;
       
   354     our @end_ro_list;
       
   355     our $ablauf_zeit;
       
   356     chomp(my $now_time = `date +%s`);
       
   357 
       
   358     for (<$master_dir/*>) {
       
   359         my $zone = $_;
       
   360         $zone =~ s#($master_dir/)(.*)#$2#;
       
   361 
       
   362         my @index = ();
       
   363         my $index_wc;
       
   364         my @status;
       
   365 
       
   366         # prueft nach der ".index.zsk"-datei und erstellt den zeitpunkt
       
   367         # an dem das key-rollover endet. - $status[9]
       
   368         if (-e "$master_dir/$zone/.index.zsk") {
       
   369             @status = stat("$master_dir/$zone/.index.zsk");
       
   370             $status[9] += (3600 * $ablauf_zeit);
       
   371         }
       
   372         else { next; }
       
   373 
       
   374         # $status[9] ist der zeitpunkt an dem der key-rollover endet
       
   375         # prueft ob das key-rollover-ende erreicht ist
       
   376         unless ($status[9] < $now_time) { next; }
       
   377 
       
   378         # prueft die anzahl der schluessel in der .index.zsk
       
   379         open(INDEX, "$master_dir/$zone/.index.zsk")
       
   380           or die "$master_dir/$zone/.index.zsk: $!\n";
       
   381         @index    = <INDEX>;
       
   382         $index_wc = @index;
       
   383         close(INDEX);
       
   384         if ($index_wc > 1) { push @end_ro_list, $zone; }
       
   385     }
       
   386 }
       
   387 
       
   388 sub begin_ro {
       
   389 
       
   390     # anfang des key-rollovers
       
   391     our @begin_ro_list;
       
   392     our $master_dir;
       
   393     our @new_serial;
       
   394 
       
   395     for (&del_double(@begin_ro_list)) {
       
   396 
       
   397         #erzeugt zsks
       
   398         my $zone = $_;
       
   399         my $zpf  = "$master_dir/$zone";
       
   400         my @index;
       
   401 
       
   402         chdir "$zpf" or die "$zpf: $!\n";
       
   403         my $keyname = `dnssec-keygen -a RSASHA1 -b 512 -n ZONE $zone`;
       
   404 
       
   405         open(INDEX, ".index.zsk") or die "$zpf/.index.zsk: $!\n";
       
   406         @index = <INDEX>;
       
   407         close(INDEX);
       
   408 
       
   409         push @index, $keyname;
       
   410         if (@index > 2) { shift(@index); }
       
   411 
       
   412         open(INDEX, ">.index.zsk") or die "$zpf/.index.zsk: $!\n";
       
   413         print INDEX @index;
       
   414         close(INDEX);
       
   415 
       
   416         chomp($keyname);
       
   417         print " * $zone: neuer ZSK $keyname erstellt\n";
       
   418 
       
   419         open(KC, ">.keycounter") or die "$zpf/keycounter: $!\n";
       
   420         print KC "0";
       
   421         close(KC);
       
   422 
       
   423         &kill_useless_keys($zone);
       
   424         &key_to_zonefile($zone);
       
   425         push @new_serial, $zone;
       
   426     }
       
   427 }
       
   428 
       
   429 sub key_to_zonefile {
       
   430 
       
   431     # die funktion fugt alle schluessel in eine zonedatei
       
   432     our $master_dir;
       
   433     my $zone = $_[0];
       
   434     my $zpf  = "$master_dir/$zone";
       
   435     my @old_content;
       
   436     my @new_content = ();
       
   437 
       
   438     open(ZONEFILE, "<$zpf/$zone");
       
   439     @old_content = <ZONEFILE>;
       
   440     close(ZONEFILE);
       
   441 
       
   442     for (@old_content) {
       
   443         unless (m#INCLUDE.*key#) { push @new_content, $_; }
       
   444     }
       
   445 
       
   446     for (<$zpf/*>) {
       
   447         if (m#(.*\/)(K.*\.key)#) {
       
   448             push @new_content, "\$INCLUDE \"$2\"\n";
       
   449         }
       
   450     }
       
   451     open(ZONEFILE, ">$zpf/$zone") or die "$zpf/$zone: $!\n";
       
   452     print ZONEFILE @new_content;
       
   453     close(ZONEFILE);
       
   454 }
       
   455 
       
   456 sub kill_useless_keys {
       
   457 
       
   458     # die funktion loescht alle schluessel die nicht in der index.zsk
       
   459     # der uebergebenen zone stehen
       
   460     our $master_dir;
       
   461     my $zone    = $_[0];
       
   462     my @keylist = ();
       
   463     my $zpf     = "$master_dir/$zone";
       
   464 
       
   465     open(INDEX, "<$zpf/.index.zsk") or die "$zpf/.index.zsk: $!\n";
       
   466     @keylist = <INDEX>;
       
   467     close(INDEX);
       
   468     open(INDEX, "<$zpf/.index.ksk") or die "$zpf/.index.ksk: $!\n";
       
   469     push @keylist, <INDEX>;
       
   470 
       
   471     # kuerzt die schluessel-bezeichnung aus der indexdatei auf die id um sie
       
   472     # besser vergleichen zu koennen.
       
   473     for (@keylist) {
       
   474         chomp;
       
   475         s#K.*\+.*\+(.*)#$1#;
       
   476     }
       
   477 
       
   478     # prueft alle schluesseldateien (ksk, zsk) ob sie in der jeweiligen
       
   479     # indexdatei beschrieben sind. wenn nicht werden sie geloescht.
       
   480     for (`ls $master_dir/$zone/K*[key,private]`) {
       
   481         chomp;
       
   482         my $file     = $_;
       
   483         my $rm_count = 1;
       
   484         my $keyname;
       
   485         for (@keylist) {
       
   486             if ($file =~ /$_/) { $rm_count = 0; }
       
   487         }
       
   488         if ($rm_count == 1) {
       
   489             unlink "$file";
       
   490             if ($file =~ /$zpf\/(.*\.key)/) {
       
   491                 print " * $zone: Schluessel $1 entfernt \n";
       
   492             }
       
   493         }
       
   494     }
       
   495 }
       
   496 
       
   497 sub end_ro {
       
   498     our @end_ro_list;
       
   499     our $master_dir;
       
   500     our @new_serial;
       
   501     my @content;
       
   502 
       
   503     for (@end_ro_list) {
       
   504         my $zone  = $_;
       
   505         my $count = 0;
       
   506         my @content;
       
   507         my $last_key;
       
   508 
       
   509         open(INDEX, "<$master_dir/$zone/.index.zsk");
       
   510         @content = <INDEX>;
       
   511         close(INDEX);
       
   512 
       
   513         for (@content) {
       
   514             $count++;
       
   515             $last_key = $_;
       
   516         }
       
   517         if ($count > 1) {
       
   518             open(INDEX, ">$master_dir/$zone/.index.zsk");
       
   519             print INDEX $last_key;
       
   520             close(INDEX);
       
   521         }
       
   522         &kill_useless_keys($zone);
       
   523         &key_to_zonefile($zone);
       
   524         push @new_serial, $zone;
       
   525     }
       
   526 }
       
   527 
       
   528 &read_conf;
       
   529 
       
   530 our %config;
       
   531 our @new_serial;       # liste fuer neuen serial
       
   532 our @begin_ro_list;    # liste mit zonen deren key-rollover beginnt
       
   533 our @end_ro_list;      # liste mit zonen deren key-rollover fertig ist
       
   534 our $master_dir      = $config{master_dir};
       
   535 our $bind_dir        = $config{bind_dir};
       
   536 our $conf_dir        = $config{zone_conf_dir};
       
   537 our $sign_alert_time = $config{sign_alert_time};
       
   538 our $indexzone       = $config{indexzone};
       
   539 our $key_counter_end = $config{key_counter_end};
       
   540 our $ablauf_zeit     = $config{abl_zeit};
       
   541 
       
   542 &add_argv;
       
   543 &changed_zone;
       
   544 &sign_end;
       
   545 
       
   546 &to_begin_ro;    # prueft nach beginnenden rollover-verfahren
       
   547 &to_end_ro;      # prueft nach endenden rollover-verfahren
       
   548 
       
   549 if (@begin_ro_list) {
       
   550     &begin_ro;    # eine rollover-beginn-sequenz
       
   551 }
       
   552 
       
   553 if (@end_ro_list) {
       
   554     &end_ro;      # eine rollover-end-squenz
       
   555 }
       
   556 
       
   557 if (@new_serial) {
       
   558     &update_index;     # index zone aktuallisieren
       
   559     &update_serial;    # serial aktuallisieren
       
   560     &sign_zone;        # zone signieren
       
   561 }
       
   562 
       
   563 &file_entry;  # bearbeitet die file-eintraege der konfigurations-datei
       
   564 &mk_zone_conf; # konfiguration zusammenfuegen
       
   565 &server_reload; # server neu laden
       
   566 
       
   567 
       
   568 
       
   569 __END__
       
   570 
       
   571 =pod
       
   572 
       
   573 =head1 TITLE
       
   574 
       
   575 update-serial
       
   576 
       
   577 =head1 SYNTAX
       
   578 
       
   579 update-serial
       
   580 
       
   581 =head1 BESCHREIBUNG
       
   582 
       
   583 =cut