diff -r fdf4df74d8c5 -r bffb3f2cca90 update-serial.pl --- a/update-serial.pl Thu Dec 30 17:05:31 2010 +0100 +++ b/update-serial.pl Mon Jan 03 16:13:54 2011 +0100 @@ -9,6 +9,7 @@ use Pod::Usage; use Getopt::Long; use File::Temp; +use IO::File; use POSIX qw(strftime); use if $ENV{DEBUG} => "Smart::Comments"; @@ -17,19 +18,19 @@ sub zones(@); sub changed_zones(); sub update_index($); -sub sign_expired($); +sub signature_expired($); sub need_rollover(); sub done_rollover(); sub begin_rollover(@); +sub end_rollover(@); sub unlink_unused_keys($); +sub include_keys($); +sub sign($); +sub update_serial($); -sub sign_zone; -sub update_serial; sub mk_zone_conf; sub file_entry; sub server_reload; -sub key_to_zonefile; -sub end_ro; my %config; my %opt; @@ -38,7 +39,7 @@ GetOptions( "sign-alert-time=i" => \$opt{sign_alert_time}, - "key-counter-end=i" => \$opt{key_counter_end}, + "key-counter-end=i" => \$opt{key_counter_end}, "h|help" => sub { pod2usage(-exit 0, -verbose => 1) }, "m|man" => sub { pod2usage( @@ -55,33 +56,25 @@ map { $_ => $opt{$_} } grep { defined $opt{$_} } keys %opt ); - our @new_serial; # DO NOT USE - our @end_ro_list; # liste mit zonen deren key-rollover fertig ist our $bind_dir = $config{bind_dir}; our $conf_dir = $config{zone_conf_dir}; my @candidates = @ARGV ? zones(@ARGV) : changed_zones; push @candidates, update_index($config{indexzone}); - push @candidates, sign_expired($config{sign_alert_time}); + push @candidates, signature_expired($config{sign_alert_time}); my @need_rollover = need_rollover; my @done_rollover = done_rollover; - ### @candidates - ### @need_rollover - ### @done_rollover - begin_rollover(@need_rollover); # eine rollover-beginn-sequenz - exit; + + push @candidates, begin_rollover(@need_rollover); + push @candidates, end_rollover(@done_rollover); - if (@end_ro_list) { - end_ro; # eine rollover-end-squenz + foreach my $zone (uniq(@candidates)) { + update_serial($zone); + sign($zone); } - - if (@new_serial) { - - #--update_index; # index zone aktuallisieren - update_serial; # serial aktuallisieren - sign_zone; # zone signieren - } + say "Need to ... file_entry, mk_zone_conf, server_reload"; + exit; file_entry; # bearbeitet die file-eintraege der konfigurations-datei mk_zone_conf; # konfiguration zusammenfuegen @@ -159,7 +152,7 @@ return @r; } -sub sign_expired($) { +sub signature_expired($) { my $sign_alert_time = shift; # the time between the end and the new signing # (see external configuration) my @r; @@ -186,88 +179,73 @@ return @r; } -sub sign_zone { - - # signiert die zonen und erhoeht den wert in der keycounter-datei - our @new_serial; - my $zone; - my $kc; +sub sign($) { - for (uniq(@new_serial)) { - $zone = $_; + my $zone = shift; + my $dir = "$config{master_dir}/$zone"; - unless (-e "$config{master_dir}/$zone/.index.zsk") { - next; - } + my $pid = fork // die "Can't fork: $!"; - chdir "$config{master_dir}/$zone"; - if (`dnssec-signzone $zone 2>/dev/null`) { - print " * $zone neu signiert \n"; + if ($pid == 0) { + chdir $dir or die "Can't chdir to $dir: $!\n"; + exec "dnssec-signzone" => $zone; + die "Can't exec: $!\n"; + } + + wait == $pid or die "Child is lost: $!"; + die "Can't sign zone!" if $?; - # erhoeht den keycounter - if ("$config{master_dir}/$zone/.keycounter") { - open(KC, "$config{master_dir}/$zone/.keycounter"); - $kc = ; - close(KC); - $kc += 1; - } - else { - $kc = 1; - } - open(KC, ">$config{master_dir}/$zone/.keycounter"); - print KC $kc; - close(KC); - } - else { print "$zone konnte nicht signiert werden \n"; } - } + say " * $zone neu signiert"; + + open(my $fh, "+>>$dir/.keycounter") + or die "Can't open $dir/.keycounter for update: $!\n"; + seek($fh, 0, 0); + my $kc = <$fh>; + truncate($fh, 0); + say $fh ++$kc; } -sub update_serial { - our @new_serial; - chomp(my $date = `date +%Y%m%d`); - my @new_content; - my $sdate; - my $scount; +sub update_serial($) { + + my $zone = shift; + + my $file = "$config{master_dir}/$zone/$zone"; + my $in = IO::File->new($file) or die "Can't open $file: $!\n"; + my $out = File::Temp->new(DIR => dirname $file) + or die "Can't open tmpfile: $!\n"; + my $_ = join "" => <$in>; + my $serial; - - for (uniq(@new_serial)) { + s/^(\s+)(\d{10})(?=\s*;\s*serial)/$1 . ($serial = new_serial($2))/emi + or die "Serial number not found for replacement!"; - # erhoeht den serial - my $zone = $_; - my $file = "$config{master_dir}/$zone/$zone"; - my @new_content = (); + print $out $_; + + close($in); + close($out); - 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); + rename($out->filename => $file) + or die "Can't rename tmp to $file: $!\n"; + + $serial =~ s/\s*//g; + say " * $zone: serial incremented to $serial"; + + open(my $stamp, ">", dirname($file) . "/.stamp"); + print $stamp time() . " " . localtime() . "\n"; - open(RES, ">$file") or die "$file: $!\n"; - print RES @new_content; - close(RES); - print " * $zone: serial erhoeht \n"; + say " * $zone: stamp aktualisiert"; +} + +sub new_serial($) { + + my ($date, $cnt) = $_[0] =~ /(\d{8})(\d\d)/; - open(STAMP, ">$config{master_dir}/$zone/.stamp") - or die "$config{master_dir}/$zone/.stamp: $!\n"; - close(STAMP); - print " * $zone: stamp aktualisiert \n"; - } + state $now = strftime("%4Y%02m%02d", localtime); + + return $date eq $now + ? sprintf "%s%02d", $date, $cnt + 1 + : "${now}00"; + } sub mk_zone_conf { @@ -432,13 +410,13 @@ # anfang des key-rollovers - #?? for (uniq(@begin_ro_list)) { foreach my $zone (@zones) { # erzeugt zsks my $dir = "$config{master_dir}/$zone"; my ($keyname, @keys); + # create a new key { # need to change the direcoty, thus some more effort # alternativly: $keyname = `cd $dir && dnssec-keygen ...`; # would do, but is more fragile on shell meta characters @@ -448,24 +426,24 @@ exec "dnssec-keygen", -a => "RSASHA1", -b => 512, - -n => "ZONE", - $zone; + -n => "ZONE", + $zone; die "Can't exec: $!"; }; chomp($keyname = <$keygen>); close($keygen) or die "dnssec-keygen failed: $@"; } - open(my $fh, "+<$dir/.index.zsk") or die "$dir/.index.zsk: $!\n"; + open(my $fh, "+>>$dir/.index.zsk") or die "$dir/.index.zsk: $!\n"; + seek($fh, 0, 0); chomp(@keys = <$fh>); - ### @keys + ### @keys push @keys, $keyname; shift @keys if @keys > 2; - seek($fh, 0, 0) or die "seek"; # FIXME - truncate($fh, 0) or die "truncate"; # FIXME + truncate($fh, 0) or die "truncate"; print $fh join "\n" => @keys; print " * $zone: neuer ZSK $keyname erstellt\n"; @@ -475,37 +453,30 @@ close($fh); unlink_unused_keys($zone); - &key_to_zonefile($zone); + include_keys($zone); push @r, $zone; } return @r; } -sub key_to_zonefile { +sub include_keys($) { # die funktion fugt alle schluessel in eine zonedatei - my $zone = $_[0]; - my $zpf = "$config{master_dir}/$zone"; - my @old_content; - my @new_content = (); + my $zone = shift; + my $dir = "$config{master_dir}/$zone"; - open(ZONEFILE, "<$zpf/$zone"); - @old_content = ; - close(ZONEFILE); + my $in = IO::File->new("$dir/$zone") or die "Can't open $dir/$zone: $!\n"; + my $out = File::Temp->new(DIR => $dir) or die "Can't open tmpfile: $!\n"; - for (@old_content) { - unless (m#INCLUDE.*key#) { push @new_content, $_; } - } + print $out grep { !/\$include\s+.*key/i } $in; + print $out map { "\$INCLUDE @{[basename $_]}\n" } glob "$dir/K*key"; - 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); + close $in; + close $out; + rename($out->filename => "$dir/$zone") + or die "Can't rename tmp to $dir/$zone: $!\n"; + } sub unlink_unused_keys($) { @@ -515,51 +486,50 @@ my $zone = shift; my @keys; - my $dir = "$config{master_dir}/$zone"; + my $dir = "$config{master_dir}/$zone"; { - # collect the keys and cut everything except the key id - # we cut the basenames (w/o the .private|.key suffix) + + # collect the keys and cut everything except the key id + # we cut the basenames (w/o the .private|.key suffix) open(my $zsk, "<$dir/.index.zsk") or die "$dir/.index.zsk: $!\n"; open(my $ksk, "<$dir/.index.ksk") or die "$dir/.index.ksk: $!\n"; - @keys = (<$zsk>, <$ksk>); + @keys = (<$zsk>, <$ksk>); } # prueft alle schluesseldateien (ksk, zsk) ob sie in der jeweiligen # indexdatei beschrieben sind. wenn nicht werden sie geloescht. for my $file (glob "$dir/K*.key $dir/K*.private") { - unlink $file if basename($file, ".key", ".private") ~~ @keys; + unlink $file if basename($file, ".key", ".private") ~~ @keys; } } -sub end_ro { - our @end_ro_list; - our @new_serial; - my @content; +sub end_rollover(@) { - for (@end_ro_list) { - my $zone = $_; - my $count = 0; - my @content; - my $last_key; + my @zones = @_; + my @r; + + foreach my $zone (@zones) { + + my $dir = "$config{master_dir}/$zone"; - open(INDEX, "<$config{master_dir}/$zone/.index.zsk"); - @content = ; - close(INDEX); + open(my $fh, "+>>$dir/.index.zsk") + or die "Can't open $dir/.index.zsk: $!\n"; + seek($fh, 0, 0); + chomp(my @keys = <$fh>); - for (@content) { - $count++; - $last_key = $_; + if (@keys > 1) { + truncate($fh, 0); + say $fh $keys[-1]; } - if ($count > 1) { - open(INDEX, ">$config{master_dir}/$zone/.index.zsk"); - print INDEX $last_key; - close(INDEX); - } + close($fh); + unlink_unused_keys($zone); - &key_to_zonefile($zone); - push @new_serial, $zone; + include_keys($zone); + push @r => $zone; } + + return @r; } __END__