From: Peter Palfrader Date: Sun, 7 Feb 2010 15:43:37 +0000 (+0100) Subject: Add dsa-check-zone-rrsig-expiration from measurement-factory X-Git-Url: https://wiki.adam-barratt.org.uk/gitweb/?a=commitdiff_plain;h=87a36932ac152ea9fcc1366139cfaf978a48566b;p=mirror%2Fdsa-nagios.git Add dsa-check-zone-rrsig-expiration from measurement-factory --- diff --git a/dsa-nagios-checks/checks/dsa-check-zone-rrsig-expiration b/dsa-nagios-checks/checks/dsa-check-zone-rrsig-expiration new file mode 100755 index 0000000..e8e411c --- /dev/null +++ b/dsa-nagios-checks/checks/dsa-check-zone-rrsig-expiration @@ -0,0 +1,285 @@ +#!/usr/bin/perl + +# downloaded from http://dns.measurement-factory.com/tools/nagios-plugins/check_zone_rrsig_expiration.html +# on 2010-02-07 by Peter Palfrader + +# $Id: check_zone_rrsig_expiration,v 1.7 2008/11/25 01:36:36 wessels Exp $ +# +# check_zone_rrsig_expiration +# +# nagios plugin to check expiration times of RRSIG records. Reminds +# you if its time to re-sign your zone. + +# Copyright (c) 2008, The Measurement Factory, Inc. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# Neither the name of The Measurement Factory nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +# usage +# +# define command { +# command_name check-zone-rrsig +# command_line /usr/local/libexec/nagios-local/check_zone_rrsig -Z $HOSTADDRESS$ +# } +# +# define service { +# name dns-rrsig-service +# check_command check-zone-rrsig +# ... +# } +# +# define host { +# use dns-zone +# host_name zone.example.com +# alias ZONE example.com +# } +# +# define service { +# use dns-rrsig-service +# host_name zone.example.com +# } + +use warnings; +use strict; + +use Getopt::Std; +use Net::DNS::Resolver; +use Time::HiRes qw ( gettimeofday tv_interval); +use Time::Local; +use List::Util qw ( shuffle ); + +my %opts = (t=>30); +getopts('Z:dt:', \%opts); +usage() unless $opts{Z}; +usage() if $opts{h}; +my $zone = $opts{Z}; +$zone =~ s/^zone\.//; + +my $data; +my $start; +my $stop; +my $CRIT_DAYS = 3; +my $WARN_DAYS = 7; + +my @refs = qw ( +a.root-servers.net +b.root-servers.net +c.root-servers.net +d.root-servers.net +e.root-servers.net +f.root-servers.net +g.root-servers.net +h.root-servers.net +i.root-servers.net +j.root-servers.net +k.root-servers.net +l.root-servers.net +m.root-servers.net +); + +$start = [gettimeofday()]; +do_recursion(); +do_queries(); +$stop = [gettimeofday()]; +do_analyze(); + +sub do_recursion { + my $done = 0; + my $res = Net::DNS::Resolver->new; + do { + print STDERR "\nRECURSE\n" if $opts{d}; + my $pkt; + foreach my $ns (shuffle @refs) { + print STDERR "sending query for $zone RRSIG to $ns\n" if $opts{d}; + $res->nameserver($ns); + $res->udp_timeout($opt{t}); + $res->udppacketsize(4096); + $pkt = $res->send($zone, 'RRSIG'); + last if $pkt; + } + critical("No response to seed query") unless $pkt; + critical($pkt->header->rcode . " from " . $pkt->answerfrom) + unless ($pkt->header->rcode eq 'NOERROR'); + @refs = (); + foreach my $rr ($pkt->authority) { + print STDERR $rr->string, "\n" if $opts{d}; + push (@refs, $rr->nsdname); + next unless lc($rr->name) eq lc($zone); + add_nslist_to_data($pkt); + $done = 1; + } + } while (! $done); +} + + +sub do_queries { + my $n; + do { + $n = 0; + foreach my $ns (keys %$data) { + next if $data->{$ns}->{done}; + print STDERR "\nQUERY $ns\n" if $opts{d}; + + my $pkt = send_query($zone, 'RRSIG', $ns); + add_nslist_to_data($pkt); + $data->{$ns}->{queries}->{RRSIG} = $pkt; + + print STDERR "done with $ns\n" if $opts{d}; + $data->{$ns}->{done} = 1; + $n++; + } + } while ($n); +} + +sub do_analyze { + my $nscount = 0; + my $NOW = time; + my %MAX_EXP_BY_TYPE; + foreach my $ns (keys %$data) { + print STDERR "\nANALYZE $ns\n" if $opts{d}; + my $pkt = $data->{$ns}->{queries}->{RRSIG}; + critical("No response from $ns") unless $pkt; + print STDERR $pkt->string if $opts{d}; + critical($pkt->header->rcode . " from $ns") + unless ($pkt->header->rcode eq 'NOERROR'); + critical("$ns is lame") unless $pkt->header->ancount; + foreach my $rr ($pkt->answer) { + next unless $rr->type eq 'RRSIG'; + my $exp = sigrr_exp_epoch($rr); + my $T = $rr->typecovered; + if (!defined($MAX_EXP_BY_TYPE{$T}->{exp}) || $exp > $MAX_EXP_BY_TYPE{$T}->{exp}) { + $MAX_EXP_BY_TYPE{$T}->{exp} = $exp; + $MAX_EXP_BY_TYPE{$T}->{ns} = $ns; + } + } + $nscount++; + } + warning("No nameservers found. Is '$zone' a zone?") if ($nscount < 1); + warning("No RRSIGs found") unless %MAX_EXP_BY_TYPE; + my $min_exp = undef; + my $min_ns = undef; + my $min_type = undef; + foreach my $T (keys %MAX_EXP_BY_TYPE) { + printf STDERR ("%s RRSIG expires in %.1f days\n", $T, ($MAX_EXP_BY_TYPE{$T}->{exp}-$NOW)/86400) if $opts{d}; + if (!defined($min_exp) || $MAX_EXP_BY_TYPE{$T}->{exp} < $min_exp) { + $min_exp = $MAX_EXP_BY_TYPE{$T}->{exp}; + $min_ns = $MAX_EXP_BY_TYPE{$T}->{ns}; + $min_type = $T; + } + } + critical("$min_ns has expired RRSIGs") if ($min_exp < $NOW); + if ($min_exp - $NOW < ($CRIT_DAYS*86400)) { + my $ND = sprintf "%3.1f days", ($min_exp-$NOW)/86400; + critical("$min_type RRSIG expires in $ND at $min_ns") + } + if ($min_exp - $NOW < ($WARN_DAYS*86400)) { + my $ND = sprintf "%3.1f days", ($min_exp-$NOW)/86400; + warning("$min_type RRSIG expires in $ND at $min_ns") + } + success("No RRSIGs expiring in the next $WARN_DAYS days"); +} + +sub sigrr_exp_epoch { + my $rr = shift; + die unless $rr->type eq 'RRSIG'; + my $exp = $rr->sigexpiration; + die "bad exp time '$exp'" + unless $exp =~ /^(\d\d\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/; + my $exp_epoch = timegm($6,$5,$4,$3,$2-1,$1); + return $exp_epoch; +} + +sub add_nslist_to_data { + my $pkt = shift; + foreach my $ns (get_nslist($pkt)) { + next if defined $data->{$ns}->{done}; + print STDERR "adding NS $ns\n" if $opts{d}; + $data->{$ns}->{done} |= 0; + } +} + +sub success { + output('OK', shift); + exit(0); +} + +sub warning { + output('WARNING', shift); + exit(1); +} + +sub critical { + output('CRITICAL', shift); + exit(2); +} + +sub output { + my $state = shift; + my $msg = shift; + $stop = [gettimeofday()] unless $stop; + my $latency = tv_interval($start, $stop); + printf "ZONE %s: %s; (%.2fs) |time=%.6fs;;;0.000000\n", + $state, + $msg, + $latency, + $latency; +} + +sub usage { + print STDERR "usage: $0 -Z zone\n"; + exit 3; +} + +sub send_query { + my $qname = shift; + my $qtype = shift; + my $server = shift; + my $res = Net::DNS::Resolver->new; + $res->nameserver($server) if $server; + $res->udp_timeout($opts{t}); + $res->retry(2); + $res->udppacketsize(4096); + my $pkt = $res->send($qname, $qtype); + unless ($pkt) { + $res->usevc(1); + $res->tcp_timeout($opts{t}); + $pkt = $res->send($qname, $qtype); + } + return $pkt; +} + +sub get_nslist { + my $pkt = shift; + return () unless $pkt; + return () unless $pkt->authority; + my @nslist; + foreach my $rr ($pkt->authority) { + next unless ($rr->type eq 'NS'); + next unless ($rr->name eq $zone); + push(@nslist, lc($rr->nsdname)); + } + return @nslist; +}