5 use warnings; |
5 use warnings; |
6 use FindBin; |
6 use FindBin; |
7 use File::Basename; |
7 use File::Basename; |
8 use Pod::Usage; |
8 use Pod::Usage; |
9 use Getopt::Long; |
9 use Getopt::Long; |
|
10 use Smart::Comments; |
|
11 use File::Temp; |
|
12 use POSIX qw(strftime); |
10 |
13 |
11 sub uniq(@); |
14 sub uniq(@); |
12 sub read_conf(@); |
15 sub read_conf(@); |
13 sub add_argv; |
16 sub zones(@); |
14 sub changed_zone; |
17 sub changed_zones(); |
15 sub sign_end; |
18 sub update_index($); |
|
19 sub sign_expired($); |
|
20 |
16 sub sign_zone; |
21 sub sign_zone; |
17 sub update_serial; |
22 sub update_serial; |
18 sub mk_zone_conf; |
23 sub mk_zone_conf; |
19 sub update_index; |
|
20 sub file_entry; |
24 sub file_entry; |
21 sub server_reload; |
25 sub server_reload; |
22 sub to_begin_ro; |
26 sub to_begin_ro; |
23 sub to_end_ro; |
27 sub to_end_ro; |
24 sub begin_ro; |
28 sub begin_ro; |
25 sub key_to_zonefile; |
29 sub key_to_zonefile; |
26 sub kill_useless_keys; |
30 sub kill_useless_keys; |
27 sub end_ro; |
31 sub end_ro; |
28 |
32 |
29 my %config; |
33 my %config; |
|
34 my %opt; |
30 |
35 |
31 MAIN: { |
36 MAIN: { |
32 |
37 |
33 GetOptions( |
38 GetOptions( |
34 "h|help" => sub { pod2usage(-exit 0, -verbose => 1) }, |
39 "sign-alert-time=i" => \$opt{sign_alert_time}, |
35 "m|man" => sub { |
40 "h|help" => sub { pod2usage(-exit 0, -verbose => 1) }, |
|
41 "m|man" => sub { |
36 pod2usage( |
42 pod2usage( |
37 -exit 0, |
43 -exit 0, |
38 -verbose => 2, |
44 -verbose => 2, |
39 -noperldoc => system("perldoc -v &>/dev/null") |
45 -noperldoc => system("perldoc -v &>/dev/null") |
40 ); |
46 ); |
41 }, |
47 }, |
42 ) or pod2usage; |
48 ) or pod2usage; |
43 |
49 |
44 %config = read_conf("$FindBin::Bin/dnstools.conf", "/etc/dnstools.conf"); |
50 # merge the config and the defined options from commandline |
45 |
51 %config = ( |
46 our @new_serial; # liste fuer neuen serial |
52 read_conf("$FindBin::Bin/dnstools.conf", "/etc/dnstools.conf"), |
|
53 map { $_ => $opt{$_} } grep { defined $opt{$_} } keys %opt |
|
54 ); |
|
55 |
|
56 our @new_serial; # DO NOT USE |
47 our @begin_ro_list; # liste mit zonen deren key-rollover beginnt |
57 our @begin_ro_list; # liste mit zonen deren key-rollover beginnt |
48 our @end_ro_list; # liste mit zonen deren key-rollover fertig ist |
58 our @end_ro_list; # liste mit zonen deren key-rollover fertig ist |
49 our $bind_dir = $config{bind_dir}; |
59 our $bind_dir = $config{bind_dir}; |
50 our $conf_dir = $config{zone_conf_dir}; |
60 our $conf_dir = $config{zone_conf_dir}; |
51 our $sign_alert_time = $config{sign_alert_time}; |
|
52 our $indexzone = $config{indexzone}; |
|
53 our $key_counter_end = $config{key_counter_end}; |
61 our $key_counter_end = $config{key_counter_end}; |
54 our $ablauf_zeit = $config{abl_zeit}; |
62 our $ablauf_zeit = $config{abl_zeit}; |
55 |
63 |
56 add_argv; |
64 my @candidates = @ARGV ? zones(@ARGV) : changed_zones; |
57 |
65 push @candidates, update_index($config{indexzone}); |
58 changed_zone; |
66 push @candidates, sign_expired($config{sign_alert_time}); |
59 sign_end; |
67 ### @candidates |
60 |
68 exit; |
61 to_begin_ro; # prueft nach beginnenden rollover-verfahren |
69 |
62 to_end_ro; # prueft nach endenden rollover-verfahren |
70 to_begin_ro; # prueft nach beginnenden rollover-verfahren |
|
71 to_end_ro; # prueft nach endenden rollover-verfahren |
63 |
72 |
64 if (@begin_ro_list) { |
73 if (@begin_ro_list) { |
65 begin_ro; # eine rollover-beginn-sequenz |
74 begin_ro; # eine rollover-beginn-sequenz |
66 } |
75 } |
67 |
76 |
68 if (@end_ro_list) { |
77 if (@end_ro_list) { |
69 end_ro; # eine rollover-end-squenz |
78 end_ro; # eine rollover-end-squenz |
70 } |
79 } |
71 |
80 |
72 if (@new_serial) { |
81 if (@new_serial) { |
73 update_index; # index zone aktuallisieren |
82 |
74 update_serial; # serial aktuallisieren |
83 #--update_index; # index zone aktuallisieren |
75 sign_zone; # zone signieren |
84 update_serial; # serial aktuallisieren |
|
85 sign_zone; # zone signieren |
76 } |
86 } |
77 |
87 |
78 file_entry; # bearbeitet die file-eintraege der konfigurations-datei |
88 file_entry; # bearbeitet die file-eintraege der konfigurations-datei |
79 mk_zone_conf; # konfiguration zusammenfuegen |
89 mk_zone_conf; # konfiguration zusammenfuegen |
80 server_reload; # server neu laden |
90 server_reload; # server neu laden |
81 |
91 |
82 } |
92 } |
83 |
93 |
84 sub uniq(@) { |
94 sub uniq(@) { |
|
95 |
85 # remove duplicate entries |
96 # remove duplicate entries |
86 my %all; |
97 my %all; |
87 @all{@_} = (); |
98 @all{@_} = (); |
88 keys %all; |
99 keys %all; |
89 } |
100 } |
106 } |
117 } |
107 |
118 |
108 return %config; |
119 return %config; |
109 } |
120 } |
110 |
121 |
111 sub add_argv { |
122 sub zones(@) { |
112 # checked whether the zones in argv are managed zones and |
123 |
113 #inserted them into the list new_serial |
124 # check whether the zones in argv are managed zones and |
114 our @new_serial; |
125 # insert them into the list new_serial |
115 my $zone; |
126 |
116 |
127 my @r; |
117 for (@ARGV) { |
128 |
118 chomp($zone = `idn --quiet "$_"`); |
129 foreach (@_) { |
119 if (-e "$config{master_dir}/$zone/$zone") { |
130 chomp(my $zone = `idn --quiet "$_"`); |
120 push @new_serial, $zone; |
131 die "$zone is not managed\n" |
121 } |
132 if not -e "$config{master_dir}/$zone/$zone"; |
122 } |
133 push @r, $zone; |
123 } |
134 } |
124 |
135 |
125 sub changed_zone { |
136 return @r; |
126 our @new_serial; |
137 } |
|
138 |
|
139 sub changed_zones() { |
|
140 |
|
141 # find candidates in our master dir |
|
142 my @r; |
127 |
143 |
128 while (glob "$config{master_dir}/*") { |
144 while (glob "$config{master_dir}/*") { |
129 my $zone = basename($_); |
145 my $zone = basename($_); |
130 |
146 |
131 if (-e "$config{master_dir}/$zone/.stamp") { |
147 if (not -e "$_/.stamp") { |
132 my $stamptime = (-M "$config{master_dir}/$zone/.stamp"); |
148 say " * $zone: no .stamp file found"; # NOCH IN NEW_SERIAL PUSHEN |
133 my $filetime = (-M "$config{master_dir}/$zone/$zone"); |
149 push @r, $zone; |
134 if ($stamptime > $filetime) { |
150 next; |
135 push @new_serial, $zone; |
151 } |
136 print " * $zone: zonedatei wurde geaendert\n"; |
152 |
137 } |
153 my $stamp_age = -M _; |
138 } |
154 my $file_age = -M "$_/$zone"; |
139 else { |
155 |
140 print " * $zone: keine .stamp-datei gefunden\n" |
156 next if $stamp_age <= $file_age; # should be only < |
141 ; # NOCH IN NEW_SERIAL PUSHEN |
157 |
142 push @new_serial, $zone; |
158 push @r, $zone; |
143 } |
159 say " * $zone: zone file modified"; |
144 } |
160 } |
145 |
161 return @r; |
146 } |
162 } |
147 |
163 |
148 sub sign_end { |
164 sub sign_expired($) { |
149 our $sign_alert_time; # the time between the end and the new signing |
165 my $sign_alert_time = shift; # the time between the end and the new signing |
150 # (see external configuration) |
166 # (see external configuration) |
151 our @new_serial; |
167 my @r; |
152 |
168 |
153 # erzeugt $time (die zeit ab der neu signiert werden soll) |
169 # erzeugt $time (die zeit ab der neu signiert werden soll) |
154 my $unixtime = time + (3600 * $sign_alert_time); |
170 # ... warum eigentlich nur bis zu den Stunden und nicht auch Minuten und Sekunden? |
155 my $time = `date -d \@$unixtime +%Y%m%d%H`; |
171 my $time = strftime("%Y%m%d%H" => localtime time + 3600 * $sign_alert_time); |
156 |
172 |
157 ## vergleicht fuer alle zonen im ordner $config{master_dir} mit einer |
173 ## vergleicht fuer alle zonen im ordner $config{master_dir} mit einer |
158 ## <zone>.signed-datei den zeitpunkt in $time mit dem ablaufdatum der |
174 ## <zone>.signed-datei den zeitpunkt in $time mit dem ablaufdatum der |
159 ## signatur, welcher aus der datei <zone>.signed ausgelesen wird. |
175 ## signatur, welcher aus der datei <zone>.signed ausgelesen wird. |
160 while (glob "$config{master_dir}/*") { |
176 ZONE: while (my $dir = glob "$config{master_dir}/*") { |
161 s#($config{master_dir}/)(.*)#$2#; |
177 my $zone = basename $dir; |
162 my $zone = $_; |
178 |
163 |
179 next if not -e "$dir/$zone.signed"; |
164 if (-e "$config{master_dir}/$zone/$zone.signed") { |
180 |
165 open(ZONE, "$config{master_dir}/$zone/$zone.signed"); |
181 open(my $fh, "$dir/$zone.signed") or die "Can't open $dir/$zone.signed: $!\n"; |
166 my @zone_sig_content = <ZONE>; |
182 push @r, $zone if |
167 close(ZONE); |
183 /RRSIG\s+SOA[\d ]+(\d{10})\d{4}\s+\(/ ~~ [<$fh>] |
168 |
184 and $1 < $time; |
169 for (@zone_sig_content) { |
185 } |
170 if (m#SOA.*[0-9]{14}#) { |
186 |
171 s#.*([0-9]{10})([0-9]{4}).*#$1#; |
187 return @r; |
172 if ($_ < $time) { |
|
173 push @new_serial, $zone; |
|
174 } |
|
175 } |
|
176 } |
|
177 } |
|
178 } |
|
179 } |
188 } |
180 |
189 |
181 sub sign_zone { |
190 sub sign_zone { |
182 |
191 |
183 # signiert die zonen und erhoeht den wert in der keycounter-datei |
192 # signiert die zonen und erhoeht den wert in der keycounter-datei |
277 } |
286 } |
278 close(TO); |
287 close(TO); |
279 print "** zonekonfiguration erzeugt\n"; |
288 print "** zonekonfiguration erzeugt\n"; |
280 } |
289 } |
281 |
290 |
282 sub update_index { |
291 sub update_index($) { |
283 |
292 my $indexzone = shift; |
284 # aktualisiert die indexzone; |
293 |
285 our @new_serial; |
294 my @iz; |
286 our $indexzone; |
295 |
287 my @iz_content_old; |
296 { |
288 my @iz_content_new; |
297 open(my $fh, "$config{master_dir}/$indexzone/$indexzone") |
289 |
298 or die "$config{master_dir}/$indexzone/$indexzone: $!\n"; |
290 open(INDEXZONE, "$config{master_dir}/$indexzone/$indexzone") |
299 chomp(@iz = grep !/ZONE::/ => <$fh>); |
291 or die "$config{master_dir}/$indexzone/$indexzone: $!\n"; |
|
292 @iz_content_old = <INDEXZONE>; |
|
293 close(INDEXZONE); |
|
294 |
|
295 for (@iz_content_old) { |
|
296 unless (m#ZONE::#) { |
|
297 push @iz_content_new, $_; |
|
298 } |
|
299 } |
300 } |
300 |
301 |
301 for my $dir (glob "$config{master_dir}/*") { |
302 for my $dir (glob "$config{master_dir}/*") { |
302 my $zone = basename($dir); |
303 my $zone = basename($dir); |
303 my $info_end = "::sec-off"; |
304 my $info = -e ("$dir/.keycounter") ? "sec-on" : "sec-off"; |
304 |
305 push @iz, join "::", "\t\tIN TXT\t\t\"ZONE", $zone, $info; |
305 if (-e "$dir/.keycounter") { |
306 } |
306 $info_end = "::sec-on"; |
307 |
307 } |
308 { |
308 |
309 my $fh = File::Temp->new(DIR => "$config{master_dir}/$indexzone") |
309 my $iz_line = "\t\tIN TXT\t\t\"ZONE::$zone$info_end\"\n"; |
310 or die "Can't create tmpdir: $!\n"; |
310 |
311 print $fh join "\n" => @iz, ""; |
311 push @iz_content_new, $iz_line; |
312 rename($fh->filename => "$config{master_dir}/$indexzone/$indexzone") |
312 } |
313 or die "Can't rename " |
313 |
314 . $fh->filename |
314 open(INDEXZONE, ">$config{master_dir}/$indexzone/$indexzone") |
315 . " to $config{master_dir}/$indexzone/$indexzone: $!\n"; |
315 or die "$config{master_dir}/$indexzone/$indexzone: $!\n"; |
316 } |
316 print INDEXZONE @iz_content_new; |
317 |
317 close(INDEXZONE); |
318 say "** index-zone aktualisiert"; |
318 |
319 return $indexzone; |
319 # fuegt die index-zone in die liste damit der serial erhoet wird |
|
320 push @new_serial, $indexzone; |
|
321 |
|
322 print "** index-zone aktualisiert \n"; |
|
323 } |
320 } |
324 |
321 |
325 sub file_entry { |
322 sub file_entry { |
326 |
323 |
327 # prueft jede domain, die ein verzeichnis in $config{master_dir} hat, ob sie |
324 # prueft jede domain, die ein verzeichnis in $config{master_dir} hat, ob sie |
587 |
584 |
588 update-serial - updates the serial numbers and re-signs the zone files |
585 update-serial - updates the serial numbers and re-signs the zone files |
589 |
586 |
590 =head1 SYNOPSIS |
587 =head1 SYNOPSIS |
591 |
588 |
592 update-serial [zone...] |
589 update-serial [options] [zone...] |
593 |
590 |
594 =head1 DESCRIPTION |
591 =head1 DESCRIPTION |
595 |
592 |
596 B<update-serial> scans the configured directories for modified zone files. On any |
593 B<update-serial> scans the configured directories for modified zone files. On any |
597 file found it increments the serial number and signs the zone, if approbiate. |
594 file found it increments the serial number and signs the zone, if approbiate. |
598 |
595 |
599 =head1 OPTIONS |
596 =head1 OPTIONS |
600 |
597 |
|
598 =over |
|
599 |
|
600 =item B<--sign-alert-time> I<days> |
|
601 |
|
602 |
|
603 =back |
|
604 |
601 The common options B<-h>|B<--help>|B<-m>|B<--man> are supported. |
605 The common options B<-h>|B<--help>|B<-m>|B<--man> are supported. |
602 |
606 |
603 =head1 AUTHOR |
607 =head1 AUTHOR |
604 |
608 |
605 L<andre.suess@pipkin.cc> |
609 L<andre.suess@pipkin.cc> |