So I finally got around to writing an SSL certificate monitor for
POP/IMAP/SMTP servers, using the OpenSSL "s_client -starttls" feature.

As noted in the script, the timeout is not user-settable since it
seems to be buried in the OpenSSL client somewhere, so if you use
this script on multiple hosts, make sure that if all of them time
out it will not exceed the period of the test.  On my system,
the "openssl s_client" command took 75 seconds to time out when
the host was not available.

        -- Ed
#!/usr/local/bin/perl5.8.8
#
# $Id: sslcert.monitor,v 1.2 2011/01/31 21:56:52 root Exp $
#

my $usage="Usage: sslcert.monitor  --expiry NN --port NNN [--starttls 
{imap|smtp|pop3|ftp}] hostname [...]";

# check SSL certs of direct SSL-wrapped services or STARTTLS services
# alarm if certificate expires within "--expiry" days.

# Written by Ed Ravin <era...@panix.com> January 2011
# Code made available courtesy of Public Access Networks, http://panix.com
# License is GNU

use Getopt::Long;
use Date::Parse;

my @details=();
my @failures=();

GetOptions( \%options, "port=i", "expiry=i", "starttls=s", "debug" )
        or die $usage;

my $port= $options{port} || die $usage;
my $expiry= $options{expiry} || die $usage;
my $starttls= $options{starttls} || "";
die $usage unless ( $starttls =~ /^(imap|smtp|pop3|ftp)$/ or $starttls eq "");
$starttls= "-starttls $starttls" unless $starttls eq "";
my $debug= $options{debug} || 0;
my $now = time;
my $expiredeadline= $now + ($expiry * 3600 * 24);

#openssl s_client -connect mail.panix.com:993  2>/dev/null </dev/null | openssl 
x509 -noout -enddate; echo $?
# notAfter=Jan 28 20:18:05 2013 GMT

for $host( @ARGV ) {

        my $cmdline= sprintf("openssl s_client -connect %s:%d %s 2>/dev/null < 
/dev/null | openssl x509 -noout -enddate 2>&1",
        $host, $port, $starttls);

        print "Command: $cmdline\n" if $debug;

        my $ssloutput= `$cmdline`;
    my $rc= $? >> 8;

        if( $rc != 0 ) {
                push( @failures, $host);
                push( @details, "$host: openssl return code $rc, output: 
$ssloutput\n");
                next;
        }

        chomp $ssloutput;
        if ( $ssloutput !~ /^\s*notAfter\s*=\s*(.*)/) {
                push( @failures, $host);
                push( @details, "$host: unexpected result from openssl command 
line: $ssloutput\n");
                next;
        }

        my $certexpiretime= str2time($1);
        if (!defined($certexpiretime)) {
                push( @failures, $host);
                push( @details, "$host: unable to parse openssl command line 
output: $ssloutput\n");
                next;
        }

        if ($certexpiretime <  $expiredeadline) {
                push( @failures, $host);
                push( @details, "$host: certificate expires within $expiry 
days: $ssloutput is " . int(($certexpiretime - $now) / 3600 / 24) . " days 
away\n");
                next;
        }

        print "$host: $ssloutput - notAfter=$certexpiretime - 
deadline=$expiredeadline\n" if $debug;
        
}

if (@failures == 0) {
    exit 0;
        }

print join (" ", sort @failures), "\n";
print sort @details if (scalar @details > 0);

exit 1;

__END__

=head1 NAME

sslcert.monitor - alarm when SSL certificates approach expiration date

=head1 SYNOPSIS

B<sslcert.monitor>  --expiry I<days> --port I<portnum> [--starttls 
{imap|smtp|pop3|ftp}] hostname [...]

=head1 DESCRIPTION

B<sslcert.monitor> checks the requested server(s) at the requested
port number and alarms if the SSL certificate of the server will
expire within the specified deadline.

=head1 OPTIONS

=item B<--expiry> I<days>

Alarm if the server certificate expires within the specified number of days.

=item B<--portnum> I<port-number>

The numeric port number to test.

=item B<--starttls> I<protocol>

Connect to the specified port number without encryption and issue a
STARTTLS command to switch to encrypted mode.  The OpenSSL client
supports the protocals "pop3", "smtp", "imap", and "ftp".

=item B<--debug>

List out debugging information for each server.

=head1 BUGS

B<sslcert.monitor> is a wrapper around the B<openssl s_client> command
and has the same limitations, including a non-adjustable timeout.
If the host is not answering it could take 75 seconds or longer for
B<sslcert.monitor> to detect the error for each host, possibly causing
long delays for this command when testing multiple hosts.
_______________________________________________
mon mailing list
mon@linux.kernel.org
http://linux.kernel.org/mailman/listinfo/mon

Reply via email to