use strict; use warnings; use Getopt::Long; use Date::Calc qw(Delta_Days Parse_Date Today); use Crypt::OpenSSL::X509;
our $ARG_WARNING_DAYS = 14; our $ARG_CRITICAL_DAYS = 7;
our $ARG_ADDRESS = ''; our $ARG_HOSTNAME = ''; our $ARG_PORT = 443; our $ARG_OPENSSL = '/usr/bin/openssl'; our $ARG_STARTTLS = '';
our $ARG_FILE = ''; our $ARG_COMMON_NAME = '';
main();
sub main { parseArguments(); my $certificate = retrieveCertificate();
my $x509 = Crypt::OpenSSL::X509->new_from_string( $certificate, Crypt::OpenSSL::X509::FORMAT_PEM );
decideExitCode( calculateDaysLeft($x509->notAfter()), extractCommonName($x509->subject()) ); }
sub decideExitCode { my ( $daysLeft, $commonName ) = @_;
if ( length($ARG_COMMON_NAME) != 0 ) { if ( $ARG_COMMON_NAME ne $commonName ) { printf("CRITICAL - Common name does not match. Got: %s\n", $commonName); exit(2); } }
if ( $daysLeft <= 0 ) { printf("CRITICAL - Certificate is expired\n"); exit(2); }
if ( $daysLeft <= $ARG_CRITICAL_DAYS ) { printf("CRITICAL - %d days left\n", $daysLeft); exit(2); } elsif ( $daysLeft <= $ARG_WARNING_DAYS ) { printf("WARNING - %d days left\n", $daysLeft); exit(1); } else { printf("OK - %d days left\n", $daysLeft); exit(0); } }
sub parseArguments { GetOptions ( 'address=s' => \$ARG_ADDRESS, 'port=s' => \$ARG_PORT, 'hostname=s' => \$ARG_HOSTNAME, 'common-name=s' => \$ARG_COMMON_NAME, 'file=s' => \$ARG_FILE, 'warn=i' => \$ARG_WARNING_DAYS, 'crit=i' => \$ARG_CRITICAL_DAYS, 'openssl=s' => \$ARG_OPENSSL, 'starttls=s' => \$ARG_STARTTLS );
validateArguments(); }
sub validateArguments { if ( ! isInteger($ARG_WARNING_DAYS) ) { exitUnknown("Argument --warn is not numeric"); }
if ( ! isInteger($ARG_CRITICAL_DAYS) ) { exitUnknown("Argument --crit is not numeric"); }
if ( $ARG_CRITICAL_DAYS > $ARG_WARNING_DAYS ) { exitUnknown(sprintf( 'Critical value (%d) is greater than warning value (%d)', $ARG_CRITICAL_DAYS, $ARG_WARNING_DAYS )); }
if ( length ($ARG_FILE) == 0 ) { validateNetworkArguments(); } else { validateFileArguments(); } }
sub validateNetworkArguments { if ( ! -e $ARG_OPENSSL ) { exitUnknown(sprintf("OpenSSL not found under %s", $ARG_OPENSSL)); }
if ( length($ARG_ADDRESS) == 0) { exitUnknown("Argument --address is not set"); }
if ( ! isInteger($ARG_PORT) ) { exitUnknown("Argument --port is not numeric"); }
if ( $ARG_PORT <= 0 || $ARG_PORT >= 65535 ) { exitUnknown("Argument --port is out of bounds! Valid: 1-65535"); } }
sub validateFileArguments { if ( ! -e $ARG_FILE ) { exitUnknown(sprintf( "Certificate under %s not found", $ARG_FILE )); } }
sub isInteger { my $possibleInteger = shift;
return 1 if ( $possibleInteger =~ /\d+/ ); return 0; }
sub exitUnknown { printf("UNKNOWN - %s\n", shift); exit 3; }
sub retrieveCertificate { my $certificate = '';
if ( length($ARG_FILE) == 0 ) {
my $sniPart = '';
if ( length($ARG_HOSTNAME) != 0 ) { $sniPart = sprintf("-servername %s", $ARG_HOSTNAME); }
my $starttlsPart = '';
if ( length($ARG_STARTTLS) != 0 ) { $starttlsPart = sprintf("-starttls %s", $ARG_STARTTLS); }
my $command = sprintf( "echo \"\" | %s s_client -connect %s:%d %s %s 2> /dev/null | %s x509 2> /dev/null", $ARG_OPENSSL, $ARG_ADDRESS, $ARG_PORT, $starttlsPart, $sniPart, $ARG_OPENSSL );
$certificate = `$command`; } else { open(CERT, $ARG_FILE); my $line = '';
while ( $line = <CERT> ) { $certificate .= $line; }
close(CERT); }
if ( $certificate !~ /BEGIN CERTIFICATE/ ) { exitUnknown("Didn't receive a certificate"); }
return $certificate; }
sub calculateDaysLeft { my $expireDate = shift;
my ($year, $month, $day) = Parse_Date($expireDate); my ( $nowYear, $nowMonth, $nowDay) = Today();
return Delta_Days($nowYear, $nowMonth, $nowDay, $year, $month, $day); }
sub extractCommonName { my $possibleCommonName = shift;
if ( $possibleCommonName =~ m/CN=(.*)/ ) { my $cn = $1; $cn =~ s/,.*//;
return $cn; } else { exitUnknown("No common name given"); } }
|