#!/usr/bin/perl -w # nagios check for debian security sync checks # # Copyright (c) 2008 Alexander Wirt # Copyright (c) 2009, 2010, 2016 Peter Palfrader # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA use LWP::UserAgent; use Socket; use strict; use Date::Parse; use Getopt::Long; use Date::Parse; use Date::Format; use File::Basename; use English; use warnings; sub usage($$) { my ($fh, $exit) = @_; my $basename = basename($PROGRAM_NAME); my $VERSION = '0.1'; print $fh "$basename $VERSION\n"; print $fh "Usage: $basename [--help|--version] [--verbose]\n"; print $fh "\n"; print $fh " --help Print this short help.\n"; print $fh " --version Report version number.\n"; print $fh " --verbose Be a little verbose.\n"; print $fh " --host hostname to check.\n"; print $fh " --path path to tracefile.\n"; print $fh " --unix tracefile has a unix timestamp.\n"; print $fh " --ssl use https.\n"; print $fh " --allow-skew=: if the newest timestamp is newer than secs\n"; print $fh " then the mirror sync is still ok, assuming the oldest\n"; print $fh " trace file is no older than \n"; print $fh "\n"; exit ($exit); }; # Work around LWP not being able to verify service certs directly my $ca_dir = '/etc/ssl/ca-debian'; $ENV{'PERL_LWP_SSL_CA_PATH'} = $ca_dir if -d $ca_dir; $ENV{'PATH'} = '/bin:/sbin:/usr/bin:/usr/sbin'; delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'}; my $params; $params->{'host'} = 'security.debian.org'; #which host to check $params->{'path'} = 'project/trace/security-master.debian.org'; my $OK = 0; my $WARNING = 1; my $CRITICAL = 2; my $UNKNOWN = 3; if (!GetOptions ( '--help' => \$params->{'help'}, '--verbose' => \$params->{'verbose'}, '--version' => \$params->{'version'}, '--unix' => \$params->{'unix'}, '--ssl' => \$params->{'ssl'}, '--host=s' => \$params->{'host'}, '--path=s' => \$params->{'path'}, '--allow-skew=s' => \$params->{'skew'}, )) { usage(*STDERR,1) }; usage(*STDOUT,0) if ($params->{'help'}); usage(*STDERR,1) if (scalar @ARGV); if (defined $params->{'skew'}) { if (not $params->{'skew'} =~ /^([0-9]+):([0-9]+)$/) { print STDERR "Invalid allow-skew format\n"; usage(*STDERR,1); }; $params->{'skew-new'} = $1; $params->{'skew-old'} = $2; }; my $host = $params->{'host'}; my $path = $params->{'path'}; my @slaves; my $status; my @exitstatus; my $exitcode = $OK; @slaves = gethostbyname($params->{'host'}) or die "Can't resolve " . $params->{'host'} .": $!\n"; @slaves = map { inet_ntoa($_) } @slaves[4 .. $#slaves]; print "Checking the following hosts:\n" . join("\n", @slaves) . "\n" if $params->{'verbose'}; my @critical; my $schema = $params->{'ssl'} ? 'https' : 'http'; foreach my $slave (@slaves) { my $ua = LWP::UserAgent->new; $ua->agent("dsa-check-mirrorsync"); $ua->timeout(10); $ua->proxy('http', "$schema://$slave"); print "Requesting $schema://$host/$path from $slave\n" if $params->{'verbose'}; my $response = $ua->get("$schema://$host/$path"); if ($response->is_success) { my $content = $response->content; # or whatever my ($date, $foo, $bar) = split("\n", $content); my $synctime; if ($params->{'unix'}) { if ($date =~ /^([0-9]+)$/) { $synctime = $1; } } else { $synctime = str2time($date); } if (defined $synctime) { print "$slave last synced $synctime ", scalar(gmtime($synctime)), "\n" if $params->{'verbose'}; $status->{$slave}->{'synced'} = $synctime; } else { $synctime = 0; $exitcode = $UNKNOWN; push @exitstatus, "Cannot parse tracefile on $slave"; } } else { push @exitstatus, "$slave broken: " . $response->status_line; $status->{$slave}->{'error'} = $response->status_line; $status->{$slave}->{'synced'} = 0; $exitcode = $CRITICAL; push @critical, $slave; } } my %seen; my $o_sync = scalar(grep !$seen{$_}++, map{$status->{$_}->{'synced'}} keys(%{$status})); if ($o_sync > 1) { my @mirrors = sort { $status->{$a}->{'synced'} <=> $status->{$b}->{'synced'} } keys %{$status}; my @not_most_recent = grep { $status->{$_}->{'synced'} <=> $status->{$mirrors[-1]}->{'synced'} } @mirrors; $o_sync = scalar @not_most_recent; my $newest = time - $status->{$mirrors[-1]}->{'synced'}; my $oldest = time - $status->{$mirrors[0]}->{'synced'}; my $skew_ok = ( defined $params->{'skew-new'} && defined $params->{'skew-old'} && $newest <= $params->{'skew-new'} && $oldest <= $params->{'skew-old'}); my $msg; if ($skew_ok) { $exitcode = $OK; $msg = "$o_sync mirror(s) not in sync (from oldest to newest), but still within acceptable skew: "; } else { $exitcode = $CRITICAL; $msg = "$o_sync mirror(s) not in sync (from oldest to newest): "; }; push @exitstatus, $msg . join(", ", @not_most_recent); } else { print "All mirrors on same sync timestamp\n" if $params->{'verbose'}; } if ($exitcode == $CRITICAL) { print "CRITICAL: " . join(', ',@exitstatus) . "\n"; } elsif ($exitcode == $OK) { if (scalar @exitstatus > 0) { print "OK: " . join(', ',@exitstatus) . "\n"; } else { print "OK: all mirrors up2date\n"; } } else { print "UNKNOWN: ", join(', ',@exitstatus) . "\n"; } foreach my $mirror (keys(%{$status})) { if ($status->{$mirror}->{'error'}) { print "$mirror broken: " . $status->{$mirror}->{'error'} . "\n"; } else { print "$mirror last synced: " . localtime($status->{$mirror}->{'synced'}) ."\n"; } } exit $exitcode;