equal
deleted
inserted
replaced
1 #! /usr/bin/perl |
1 #! /usr/bin/perl |
2 # © 2014 Heiko Schlittermann <hs@schlittermann.de> |
2 # © 2014 Heiko Schlittermann <hs@schlittermann.de> |
|
3 |
3 =head1 NAME |
4 =head1 NAME |
4 |
5 |
5 check_dns-serial - check the dns serial number from multiple sources |
6 check_dns-serial - check the dns serial number from multiple sources |
6 |
7 |
7 =head1 SYNOPSIS |
8 =head1 SYNOPSIS |
12 |
13 |
13 B<check_dns-serial> is designed as a Icinga/Nagios plugin to verify that |
14 B<check_dns-serial> is designed as a Icinga/Nagios plugin to verify that |
14 all responsible NS have the same serial number for their zones. |
15 all responsible NS have the same serial number for their zones. |
15 |
16 |
16 Domains we are not responsible for are marked as B<critical>. |
17 Domains we are not responsible for are marked as B<critical>. |
|
18 |
|
19 The list of domains may consist of the following items: |
|
20 |
|
21 =over |
|
22 |
|
23 =item I<domain> |
|
24 |
|
25 A plain domain name. |
|
26 |
|
27 =item B<file://>I<file> |
|
28 |
|
29 A file name containing the domains, line by line. |
|
30 |
|
31 =item B<local:> |
|
32 |
|
33 This item uses the output of C<named-checkconf -p> to get the list of |
|
34 master/slave zones. The 127.in-addr.arpa, 168.192.in-addr.arpa, and |
|
35 0.in-addr.arpa, and 127.in-addr.arpa zones are suppressed. |
|
36 |
|
37 =back |
17 |
38 |
18 =cut |
39 =cut |
19 |
40 |
20 use 5.014; |
41 use 5.014; |
21 use strict; |
42 use strict; |
25 use Pod::Usage; |
46 use Pod::Usage; |
26 |
47 |
27 my %resolver; |
48 my %resolver; |
28 sub uniq { my %h; @h{@_} = (); return keys %h; } |
49 sub uniq { my %h; @h{@_} = (); return keys %h; } |
29 |
50 |
|
51 # return a list of the zones known to the local |
|
52 # bind |
|
53 sub get_local_zones { |
|
54 my @conf; |
|
55 open(my $z, '-|', 'named-checkconf -p'); |
|
56 while (<$z>) { |
|
57 state $line; |
|
58 s/^\s*(.*?)\s*$/$1 /; |
|
59 chomp($line .= $_); # continuation line |
|
60 if (/\A\}/) { # config item done |
|
61 $line =~ s/\s$//; |
|
62 push @conf, $line; |
|
63 $line = ''; |
|
64 } |
|
65 } |
|
66 return grep { |
|
67 # FIXME: 172.0 .. 172.31 is missing |
|
68 not /^\b(?:0|127|10|168\.192|255)\.in-addr\.arpa$/ and |
|
69 not /^localhost$/; |
|
70 } map { /zone\s"(\S+)"\s/ } grep { /type (?:master|slave)/ } @conf; |
|
71 } |
|
72 |
30 sub get_domains { |
73 sub get_domains { |
31 my @sources = @_; |
74 my @sources = @_; |
32 my @domains = (); |
75 my @domains = (); |
33 |
76 |
34 foreach my $src (@sources) { |
77 foreach my $src (@sources) { |
35 |
78 |
36 if ($src =~ m{^(?:(/.*)|file://(/.*))}) { |
79 if ($src =~ m{^(?:(/.*)|file://(/.*))}) { |
37 open(my $f, '<', $1) or die "$0: Can't open $1 for reading: $!\n"; |
80 open(my $f, '<', $1) or die "$0: Can't open $1 for reading: $!\n"; |
38 push @domains, map { /^\s*(\S+)\s*/ } <$f>; |
81 push @domains, map { /^\s*(\S+)\s*/ } <$f>; |
|
82 next; |
|
83 } |
|
84 |
|
85 if ($src =~ m{^local:}) { |
|
86 push @domains, get_local_zones; |
39 next; |
87 next; |
40 } |
88 } |
41 |
89 |
42 push @domains, $src; |
90 push @domains, $src; |
43 } |
91 } |
51 my ($domain) = @_; |
99 my ($domain) = @_; |
52 my @ns; |
100 my @ns; |
53 |
101 |
54 my $r = $resolver{$nameserver} //= |
102 my $r = $resolver{$nameserver} //= |
55 Net::DNS::Resolver->new(nameservers => [$nameserver]); |
103 Net::DNS::Resolver->new(nameservers => [$nameserver]); |
56 my $q = $r->query($domain, 'NS') or die $r->errorstring, "\n"; |
104 my $q = $r->query($domain, 'NS') or die $r->errorstring, "\@$nameserver\n"; |
57 push @ns, map { $_->nsdname } grep { $_->type eq 'NS' } $q->answer; |
105 push @ns, map { $_->nsdname } grep { $_->type eq 'NS' } $q->answer; |
58 |
106 |
59 return sort @ns; |
107 return sort @ns; |
60 } |
108 } |
61 |
109 |
63 my ($nameserver) = map { /^\@(.*)/ } $_[0] =~ /^\@/ ? shift : '@8.8.8.8'; |
111 my ($nameserver) = map { /^\@(.*)/ } $_[0] =~ /^\@/ ? shift : '@8.8.8.8'; |
64 my ($domain) = shift; |
112 my ($domain) = shift; |
65 |
113 |
66 my $r = $resolver{$nameserver} //= |
114 my $r = $resolver{$nameserver} //= |
67 Net::DNS::Resolver->new(nameservers => [$nameserver]); |
115 Net::DNS::Resolver->new(nameservers => [$nameserver]); |
68 my $q = $r->query($domain, 'SOA') or die $r->errorstring, "\n"; |
116 my $q = $r->query($domain, 'SOA') or die $r->errorstring, "\@$nameserver\n"; |
69 return (map { $_->serial } grep { $_->type eq 'SOA' } $q->answer)[0]; |
117 return (map { $_->serial } grep { $_->type eq 'SOA' } $q->answer)[0]; |
70 } |
118 } |
71 |
119 |
72 # - the nameservers known from the ns records |
120 # - the nameservers known from the ns records |
73 # - from the primary master if this is not one of the |
121 # - from the primary master if this is not one of the |