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