--- 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 = <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 (<SER>) {
- 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 = <ZONEFILE>;
- 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 = <INDEX>;
- 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__