53 %config = ( |
54 %config = ( |
54 read_conf("$FindBin::Bin/dnstools.conf", "/etc/dnstools.conf"), |
55 read_conf("$FindBin::Bin/dnstools.conf", "/etc/dnstools.conf"), |
55 map { $_ => $opt{$_} } grep { defined $opt{$_} } keys %opt |
56 map { $_ => $opt{$_} } grep { defined $opt{$_} } keys %opt |
56 ); |
57 ); |
57 |
58 |
58 our @new_serial; # DO NOT USE |
|
59 our @end_ro_list; # liste mit zonen deren key-rollover fertig ist |
|
60 our $bind_dir = $config{bind_dir}; |
59 our $bind_dir = $config{bind_dir}; |
61 our $conf_dir = $config{zone_conf_dir}; |
60 our $conf_dir = $config{zone_conf_dir}; |
62 |
61 |
63 my @candidates = @ARGV ? zones(@ARGV) : changed_zones; |
62 my @candidates = @ARGV ? zones(@ARGV) : changed_zones; |
64 push @candidates, update_index($config{indexzone}); |
63 push @candidates, update_index($config{indexzone}); |
65 push @candidates, sign_expired($config{sign_alert_time}); |
64 push @candidates, signature_expired($config{sign_alert_time}); |
66 |
65 |
67 my @need_rollover = need_rollover; |
66 my @need_rollover = need_rollover; |
68 my @done_rollover = done_rollover; |
67 my @done_rollover = done_rollover; |
69 ### @candidates |
68 |
70 ### @need_rollover |
69 push @candidates, begin_rollover(@need_rollover); |
71 ### @done_rollover |
70 push @candidates, end_rollover(@done_rollover); |
72 begin_rollover(@need_rollover); # eine rollover-beginn-sequenz |
71 |
|
72 foreach my $zone (uniq(@candidates)) { |
|
73 update_serial($zone); |
|
74 sign($zone); |
|
75 } |
|
76 say "Need to ... file_entry, mk_zone_conf, server_reload"; |
73 exit; |
77 exit; |
74 |
|
75 if (@end_ro_list) { |
|
76 end_ro; # eine rollover-end-squenz |
|
77 } |
|
78 |
|
79 if (@new_serial) { |
|
80 |
|
81 #--update_index; # index zone aktuallisieren |
|
82 update_serial; # serial aktuallisieren |
|
83 sign_zone; # zone signieren |
|
84 } |
|
85 |
78 |
86 file_entry; # bearbeitet die file-eintraege der konfigurations-datei |
79 file_entry; # bearbeitet die file-eintraege der konfigurations-datei |
87 mk_zone_conf; # konfiguration zusammenfuegen |
80 mk_zone_conf; # konfiguration zusammenfuegen |
88 server_reload; # server neu laden |
81 server_reload; # server neu laden |
89 |
82 |
184 } |
177 } |
185 |
178 |
186 return @r; |
179 return @r; |
187 } |
180 } |
188 |
181 |
189 sub sign_zone { |
182 sub sign($) { |
190 |
183 |
191 # signiert die zonen und erhoeht den wert in der keycounter-datei |
184 my $zone = shift; |
192 our @new_serial; |
185 my $dir = "$config{master_dir}/$zone"; |
193 my $zone; |
186 |
194 my $kc; |
187 my $pid = fork // die "Can't fork: $!"; |
195 |
188 |
196 for (uniq(@new_serial)) { |
189 if ($pid == 0) { |
197 $zone = $_; |
190 chdir $dir or die "Can't chdir to $dir: $!\n"; |
198 |
191 exec "dnssec-signzone" => $zone; |
199 unless (-e "$config{master_dir}/$zone/.index.zsk") { |
192 die "Can't exec: $!\n"; |
200 next; |
193 } |
201 } |
194 |
202 |
195 wait == $pid or die "Child is lost: $!"; |
203 chdir "$config{master_dir}/$zone"; |
196 die "Can't sign zone!" if $?; |
204 if (`dnssec-signzone $zone 2>/dev/null`) { |
197 |
205 print " * $zone neu signiert \n"; |
198 say " * $zone neu signiert"; |
206 |
199 |
207 # erhoeht den keycounter |
200 open(my $fh, "+>>$dir/.keycounter") |
208 if ("$config{master_dir}/$zone/.keycounter") { |
201 or die "Can't open $dir/.keycounter for update: $!\n"; |
209 open(KC, "$config{master_dir}/$zone/.keycounter"); |
202 seek($fh, 0, 0); |
210 $kc = <KC>; |
203 my $kc = <$fh>; |
211 close(KC); |
204 truncate($fh, 0); |
212 $kc += 1; |
205 say $fh ++$kc; |
213 } |
206 } |
214 else { |
207 |
215 $kc = 1; |
208 sub update_serial($) { |
216 } |
209 |
217 open(KC, ">$config{master_dir}/$zone/.keycounter"); |
210 my $zone = shift; |
218 print KC $kc; |
211 |
219 close(KC); |
212 my $file = "$config{master_dir}/$zone/$zone"; |
220 } |
213 my $in = IO::File->new($file) or die "Can't open $file: $!\n"; |
221 else { print "$zone konnte nicht signiert werden \n"; } |
214 my $out = File::Temp->new(DIR => dirname $file) |
222 } |
215 or die "Can't open tmpfile: $!\n"; |
223 } |
216 my $_ = join "" => <$in>; |
224 |
217 |
225 sub update_serial { |
|
226 our @new_serial; |
|
227 chomp(my $date = `date +%Y%m%d`); |
|
228 my @new_content; |
|
229 my $sdate; |
|
230 my $scount; |
|
231 my $serial; |
218 my $serial; |
232 |
219 s/^(\s+)(\d{10})(?=\s*;\s*serial)/$1 . ($serial = new_serial($2))/emi |
233 for (uniq(@new_serial)) { |
220 or die "Serial number not found for replacement!"; |
234 |
221 |
235 # erhoeht den serial |
222 print $out $_; |
236 my $zone = $_; |
223 |
237 my $file = "$config{master_dir}/$zone/$zone"; |
224 close($in); |
238 my @new_content = (); |
225 close($out); |
239 |
226 |
240 open(SER, "<$file") or die "$file: $!\n"; |
227 rename($out->filename => $file) |
241 for (<SER>) { |
228 or die "Can't rename tmp to $file: $!\n"; |
242 if (/^\s+(\d+)(\d{2})\s*;\s*serial/i) { |
229 |
243 $sdate = $1; |
230 $serial =~ s/\s*//g; |
244 $scount = $2; |
231 say " * $zone: serial incremented to $serial"; |
245 $serial = "$sdate$scount"; |
232 |
246 if ($date eq $sdate) { |
233 open(my $stamp, ">", dirname($file) . "/.stamp"); |
247 $scount++; |
234 print $stamp time() . " " . localtime() . "\n"; |
248 } |
235 |
249 else { |
236 say " * $zone: stamp aktualisiert"; |
250 $sdate = $date; |
237 } |
251 $scount = "00"; |
238 |
252 } |
239 sub new_serial($) { |
253 } |
240 |
254 if ($serial) { |
241 my ($date, $cnt) = $_[0] =~ /(\d{8})(\d\d)/; |
255 s/$serial/$sdate$scount/; |
242 |
256 } |
243 state $now = strftime("%4Y%02m%02d", localtime); |
257 push @new_content, $_; |
244 |
258 } |
245 return $date eq $now |
259 close(SER); |
246 ? sprintf "%s%02d", $date, $cnt + 1 |
260 |
247 : "${now}00"; |
261 open(RES, ">$file") or die "$file: $!\n"; |
248 |
262 print RES @new_content; |
|
263 close(RES); |
|
264 print " * $zone: serial erhoeht \n"; |
|
265 |
|
266 open(STAMP, ">$config{master_dir}/$zone/.stamp") |
|
267 or die "$config{master_dir}/$zone/.stamp: $!\n"; |
|
268 close(STAMP); |
|
269 print " * $zone: stamp aktualisiert \n"; |
|
270 } |
|
271 } |
249 } |
272 |
250 |
273 sub mk_zone_conf { |
251 sub mk_zone_conf { |
274 |
252 |
275 # erzeugt eine named.conf-datei aus den entsprechenden vorlagen. |
253 # erzeugt eine named.conf-datei aus den entsprechenden vorlagen. |
430 my @zones = @_; |
408 my @zones = @_; |
431 my @r; |
409 my @r; |
432 |
410 |
433 # anfang des key-rollovers |
411 # anfang des key-rollovers |
434 |
412 |
435 #?? for (uniq(@begin_ro_list)) { |
|
436 foreach my $zone (@zones) { |
413 foreach my $zone (@zones) { |
437 |
414 |
438 # erzeugt zsks |
415 # erzeugt zsks |
439 my $dir = "$config{master_dir}/$zone"; |
416 my $dir = "$config{master_dir}/$zone"; |
440 my ($keyname, @keys); |
417 my ($keyname, @keys); |
441 |
418 |
|
419 # create a new key |
442 { # need to change the direcoty, thus some more effort |
420 { # need to change the direcoty, thus some more effort |
443 # alternativly: $keyname = `cd $dir && dnssec-keygen ...`; |
421 # alternativly: $keyname = `cd $dir && dnssec-keygen ...`; |
444 # would do, but is more fragile on shell meta characters |
422 # would do, but is more fragile on shell meta characters |
445 |
423 |
446 open(my $keygen, "-|") or do { |
424 open(my $keygen, "-|") or do { |
447 chdir $dir or die "Can't chdir to $dir: $!\n"; |
425 chdir $dir or die "Can't chdir to $dir: $!\n"; |
448 exec "dnssec-keygen", |
426 exec "dnssec-keygen", |
449 -a => "RSASHA1", |
427 -a => "RSASHA1", |
450 -b => 512, |
428 -b => 512, |
451 -n => "ZONE", |
429 -n => "ZONE", |
452 $zone; |
430 $zone; |
453 die "Can't exec: $!"; |
431 die "Can't exec: $!"; |
454 }; |
432 }; |
455 chomp($keyname = <$keygen>); |
433 chomp($keyname = <$keygen>); |
456 close($keygen) or die "dnssec-keygen failed: $@"; |
434 close($keygen) or die "dnssec-keygen failed: $@"; |
457 } |
435 } |
458 |
436 |
459 open(my $fh, "+<$dir/.index.zsk") or die "$dir/.index.zsk: $!\n"; |
437 open(my $fh, "+>>$dir/.index.zsk") or die "$dir/.index.zsk: $!\n"; |
|
438 seek($fh, 0, 0); |
460 chomp(@keys = <$fh>); |
439 chomp(@keys = <$fh>); |
461 |
440 |
462 ### @keys |
441 ### @keys |
463 |
442 |
464 push @keys, $keyname; |
443 push @keys, $keyname; |
465 shift @keys if @keys > 2; |
444 shift @keys if @keys > 2; |
466 |
445 |
467 seek($fh, 0, 0) or die "seek"; # FIXME |
446 truncate($fh, 0) or die "truncate"; |
468 truncate($fh, 0) or die "truncate"; # FIXME |
|
469 print $fh join "\n" => @keys; |
447 print $fh join "\n" => @keys; |
470 |
448 |
471 print " * $zone: neuer ZSK $keyname erstellt\n"; |
449 print " * $zone: neuer ZSK $keyname erstellt\n"; |
472 |
450 |
473 open($fh, ">$dir/.keycounter") or die "$dir/.keycounter: $!\n"; |
451 open($fh, ">$dir/.keycounter") or die "$dir/.keycounter: $!\n"; |
474 say $fh 0; |
452 say $fh 0; |
475 close($fh); |
453 close($fh); |
476 |
454 |
477 unlink_unused_keys($zone); |
455 unlink_unused_keys($zone); |
478 &key_to_zonefile($zone); |
456 include_keys($zone); |
479 push @r, $zone; |
457 push @r, $zone; |
480 } |
458 } |
481 |
459 |
482 return @r; |
460 return @r; |
483 } |
461 } |
484 |
462 |
485 sub key_to_zonefile { |
463 sub include_keys($) { |
486 |
464 |
487 # die funktion fugt alle schluessel in eine zonedatei |
465 # die funktion fugt alle schluessel in eine zonedatei |
488 my $zone = $_[0]; |
466 my $zone = shift; |
489 my $zpf = "$config{master_dir}/$zone"; |
467 my $dir = "$config{master_dir}/$zone"; |
490 my @old_content; |
468 |
491 my @new_content = (); |
469 my $in = IO::File->new("$dir/$zone") or die "Can't open $dir/$zone: $!\n"; |
492 |
470 my $out = File::Temp->new(DIR => $dir) or die "Can't open tmpfile: $!\n"; |
493 open(ZONEFILE, "<$zpf/$zone"); |
471 |
494 @old_content = <ZONEFILE>; |
472 print $out grep { !/\$include\s+.*key/i } $in; |
495 close(ZONEFILE); |
473 print $out map { "\$INCLUDE @{[basename $_]}\n" } glob "$dir/K*key"; |
496 |
474 |
497 for (@old_content) { |
475 close $in; |
498 unless (m#INCLUDE.*key#) { push @new_content, $_; } |
476 close $out; |
499 } |
477 rename($out->filename => "$dir/$zone") |
500 |
478 or die "Can't rename tmp to $dir/$zone: $!\n"; |
501 for (<$zpf/*>) { |
479 |
502 if (m#(.*\/)(K.*\.key)#) { |
|
503 push @new_content, "\$INCLUDE \"$2\"\n"; |
|
504 } |
|
505 } |
|
506 open(ZONEFILE, ">$zpf/$zone") or die "$zpf/$zone: $!\n"; |
|
507 print ZONEFILE @new_content; |
|
508 close(ZONEFILE); |
|
509 } |
480 } |
510 |
481 |
511 sub unlink_unused_keys($) { |
482 sub unlink_unused_keys($) { |
512 |
483 |
513 # die funktion loescht alle schluessel die nicht in der index.zsk |
484 # die funktion loescht alle schluessel die nicht in der index.zsk |
514 # der uebergebenen zone stehen |
485 # der uebergebenen zone stehen |
515 my $zone = shift; |
486 my $zone = shift; |
516 |
487 |
517 my @keys; |
488 my @keys; |
518 my $dir = "$config{master_dir}/$zone"; |
489 my $dir = "$config{master_dir}/$zone"; |
519 |
490 |
520 { |
491 { |
521 # collect the keys and cut everything except the key id |
492 |
522 # we cut the basenames (w/o the .private|.key suffix) |
493 # collect the keys and cut everything except the key id |
|
494 # we cut the basenames (w/o the .private|.key suffix) |
523 open(my $zsk, "<$dir/.index.zsk") or die "$dir/.index.zsk: $!\n"; |
495 open(my $zsk, "<$dir/.index.zsk") or die "$dir/.index.zsk: $!\n"; |
524 open(my $ksk, "<$dir/.index.ksk") or die "$dir/.index.ksk: $!\n"; |
496 open(my $ksk, "<$dir/.index.ksk") or die "$dir/.index.ksk: $!\n"; |
525 @keys = (<$zsk>, <$ksk>); |
497 @keys = (<$zsk>, <$ksk>); |
526 } |
498 } |
527 |
499 |
528 # prueft alle schluesseldateien (ksk, zsk) ob sie in der jeweiligen |
500 # prueft alle schluesseldateien (ksk, zsk) ob sie in der jeweiligen |
529 # indexdatei beschrieben sind. wenn nicht werden sie geloescht. |
501 # indexdatei beschrieben sind. wenn nicht werden sie geloescht. |
530 for my $file (glob "$dir/K*.key $dir/K*.private") { |
502 for my $file (glob "$dir/K*.key $dir/K*.private") { |
531 unlink $file if basename($file, ".key", ".private") ~~ @keys; |
503 unlink $file if basename($file, ".key", ".private") ~~ @keys; |
532 } |
504 } |
533 } |
505 } |
534 |
506 |
535 sub end_ro { |
507 sub end_rollover(@) { |
536 our @end_ro_list; |
508 |
537 our @new_serial; |
509 my @zones = @_; |
538 my @content; |
510 my @r; |
539 |
511 |
540 for (@end_ro_list) { |
512 foreach my $zone (@zones) { |
541 my $zone = $_; |
513 |
542 my $count = 0; |
514 my $dir = "$config{master_dir}/$zone"; |
543 my @content; |
515 |
544 my $last_key; |
516 open(my $fh, "+>>$dir/.index.zsk") |
545 |
517 or die "Can't open $dir/.index.zsk: $!\n"; |
546 open(INDEX, "<$config{master_dir}/$zone/.index.zsk"); |
518 seek($fh, 0, 0); |
547 @content = <INDEX>; |
519 chomp(my @keys = <$fh>); |
548 close(INDEX); |
520 |
549 |
521 if (@keys > 1) { |
550 for (@content) { |
522 truncate($fh, 0); |
551 $count++; |
523 say $fh $keys[-1]; |
552 $last_key = $_; |
524 } |
553 } |
525 close($fh); |
554 if ($count > 1) { |
526 |
555 open(INDEX, ">$config{master_dir}/$zone/.index.zsk"); |
|
556 print INDEX $last_key; |
|
557 close(INDEX); |
|
558 } |
|
559 unlink_unused_keys($zone); |
527 unlink_unused_keys($zone); |
560 &key_to_zonefile($zone); |
528 include_keys($zone); |
561 push @new_serial, $zone; |
529 push @r => $zone; |
562 } |
530 } |
|
531 |
|
532 return @r; |
563 } |
533 } |
564 |
534 |
565 __END__ |
535 __END__ |
566 |
536 |
567 =head1 NAME |
537 =head1 NAME |