Skip to content

Instantly share code, notes, and snippets.

@giuliano108
Created July 15, 2014 08:27
Show Gist options
  • Save giuliano108/ba0458149099b5a2333d to your computer and use it in GitHub Desktop.
Save giuliano108/ba0458149099b5a2333d to your computer and use it in GitHub Desktop.
Nagios SSL Certificate check, with SNI (SSL virtual hosting) support
#!/usr/bin/perl
=head1 SYNOPSIS
check_ssl_certificate.pl
--url,-u URL
--sni_servername,-s HOSTNAME SNI servername (SSL vhost) that will be requested during SSL handshake.
This tells the server which certificate to return.
--warning,-W days Warning alert if the cert expires in less than `days'.
--critical,-C days Critical alert if the cert expires in less than `days'.
=cut
=cut
This thing seems to be quite picky about version numbers.
$ perl -MLWP -MIO::Socket::SSL -MNet::SSLeay -MLWP::UserAgent -MLWP::Protocol::https -e 'map {printf "%-40s".(/OPENSSL/ ? "0x%08x" : "%-20s")."\n", $_, eval($_)} qw(LWP->VERSION IO::Socket::SSL->VERSION Net::SSLeay->VERSION Net::SSLeay::OPENSSL_VERSION_NUMBER() LWP::UserAgent->VERSION LWP::Protocol::https->VERSION)'
MacOS:
LWP->VERSION 6.07
IO::Socket::SSL->VERSION 1.77
Net::SSLeay->VERSION 1.49
Net::SSLeay::OPENSSL_VERSION_NUMBER() 0x0090819f
LWP::UserAgent->VERSION 6.06
LWP::Protocol::https->VERSION 6.03
Linux:
LWP->VERSION 6.07
IO::Socket::SSL->VERSION 1.77
Net::SSLeay->VERSION 1.49
Net::SSLeay::OPENSSL_VERSION_NUMBER() 0x1000105f
LWP::UserAgent->VERSION 6.06
LWP::Protocol::https->VERSION 6.04
=cut
use strict;
use warnings;
use IO::Socket::SSL; # qw(debug3);
use LWP::UserAgent;
use URI::URL;
use DateTime::Format::ISO8601;
use DateTime::Format::Duration;
use Getopt::Long qw/:config auto_help/;
use Pod::Usage;
use constant TIMEOUT => 10;
use constant STATE_OK => 0;
use constant STATE_WARNING => 1;
use constant STATE_CRITICAL => 2;
use constant STATE_UNKNOWN => 3;
sub quit($$) {
my ($state, $message) = @_;
print "$message\n";
exit $state;
}
sub ssl_opts($$) {
my ($sni_servername, $last_expire_ref) = @_;
return (
'verify_hostname' => 1,
'SSL_ca_file' => ($^O !~ /darwin/) ? '/etc/pki/tls/certs/ca-bundle.crt' : "$ENV{HOME}/temp/ca-bundle.crt",
'SSL_hostname' => $sni_servername,
'SSL_verifycn_name' => $sni_servername,
'SSL_verify_callback' => sub {
my ($ok, $ctx_store) = @_;
my $cert = Net::SSLeay::X509_STORE_CTX_get_current_cert($ctx_store);
$$last_expire_ref = Net::SSLeay::P_ASN1_TIME_get_isotime(Net::SSLeay::X509_get_notAfter($cert));
return $ok;
}
)
}
sub https_get($$;$) {
my ($url, $sni_servername, $expiration_date_ref) = @_;
my $uri = URI->new($url);
die "Only https urls are supported\n" unless $uri->scheme eq 'https';
my $ua = LWP::UserAgent->new();
$ua->timeout(TIMEOUT);
$ua->ssl_opts( ssl_opts($sni_servername, $expiration_date_ref) );
my $request = HTTP::Request->new('GET',$url);
$request->header(Host => $sni_servername);
my $response = $ua->request($request);
return $response;
}
my ($url, $sni_servername, $warning_days, $critical_days);
GetOptions ("url|u=s" => \$url,
"sni_servername|s=s" => \$sni_servername,
"warning|W=i" => \$warning_days,
"critical|C=i" => \$critical_days) or pod2usage(1);
if (@ARGV) {
print "This script takes no arguments...\n";
pod2usage(1);
}
pod2usage(1) if (!$url or !$sni_servername or !$warning_days or !$critical_days);
my $expiration_date;
my $response = https_get($url,$sni_servername,\$expiration_date);
if ($response->code != 500) { # Even a 404 is good enough, as far as cert validation goes...
my $cn = ($response->headers->{'client-ssl-cert-subject'} =~ /CN=(.*)$/)[0];
quit(STATE_CRITICAL,"CRITICAL - CN doesn't match SNI") unless $cn eq $sni_servername;
my $now = DateTime->now;
my $format = DateTime::Format::Duration->new( pattern => '%m months, %e days' );
$expiration_date = DateTime::Format::ISO8601->parse_datetime( $expiration_date );
quit(STATE_CRITICAL, "CRITICAL - $cn expired on ".$expiration_date->iso8601()) if $expiration_date <= $now;
my $delta_md = $expiration_date->delta_md($now);
my $delta_days = $expiration_date->delta_days($now)->delta_days();
quit(STATE_CRITICAL,"CRITICAL - $cn will expire in $delta_days days, on: ".$expiration_date->iso8601()) if $delta_days <= $critical_days;
quit(STATE_WARNING,"WARNING - $cn will expire in $delta_days days, on: ".$expiration_date->iso8601()) if $delta_days <= $warning_days;
quit(STATE_OK,"OK - $cn expires on: ".$expiration_date->iso8601()." (".$format->format_duration($delta_md)." from now)");
exit(0);
} else {
quit(STATE_CRITICAL, $response->status_line);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment