You are here: Home System Administration PERL Script to SSH Multiple Server Nodes, Checks of SSL Certificates Expiry and Email to Root
  • Increase font size
  • Default font size
  • Decrease font size


PERL Script to SSH Multiple Server Nodes, Checks of SSL Certificates Expiry and Email to Root


A set of nodes are managed using ssh only (call them "deva", "devb", "devc", "devd"). Each node runs a service that supports certificates, and the certificate expiration date must be checked. Each node can have multiple certificates, and they are all returned by running the shell command "get x509 cert" on the node itself. The command outputs one cert per line, with the following format:


<CERT_NAME> hash<CERT_HASH> exp<DATE:YYYY-MM-DD> <TIME:hh:mm:ss>


Create a script that will ssh to each node and check for certificate expiration. The script should also send e-mail five days before expiration, with node name and certificate details, and another e-mail when cert expires in less than 2 days. Please notice, that it should be easy to add another node to the list. Also, another warning mail should be sent if the node is unavailable. A summary log after the work is done, would be a nice add-on.



Note: The Question was as a part of an interview test from one of the top Browsers software development house. With 'Zero' knowledge of PERL programming at the time of writing, I have written it and tested it on the Cent OS 6.2.


A perl based script successfully testedon CentOS 6.2. It can be added to the cron tab for running on regular basis to check if the certificates are to be expired. It displays the expiry days on the console and saves the log of whole activity into /var/log/script-log.txt file. It can be parsed further to generate HTML based reports.



use Net::SSH::Expect;

use Time::localtime;

use Date::Parse;

use Term::ReadKey;


#Days to check validity for

my $minimumExpDays = 5;

my $minimumExpDays2 = 2;


#Email of root

my $rootEmail = "root\@localhost";

my $emailMsg = "Expiration of Certificates";


#log file location

my $logfile = "/var/log/script-log.txt"; #change it to your desired location


# Define servers array - List as many servers as you want, use the full FQDN or IP address

@servers = ("deva, “devb", “devc", “devd");


#Name of the certificates file you want to moinitor.

@certfiles = ("cert.pem","cert2.pem", “cert3.pem”, “cert4.pem”);

foreach (@servers) {

print " $_ \n";


print "Please enter your username for above systems? \n";

$user = <>;

chomp $user;


print "\nPlease enter your password for the above systems? \n";

ReadMode 2; #Stop echoing to screen

$pass = <>;

chomp $pass;

ReadMode 0; #Return tty to default;



# Loop through the servers array and connect to one server (box) at a time.

foreach $box (@servers) {

# Print to screen what server you are connecting to

print "Connecting to $box... \n";

$ssh = Net::SSH::Expect->new (

host => "$box",

password => "$pass",

user => "$user",

raw_pty => 1


undef $login_output;

eval {

$login_output = $ssh->login(15);



# If login output is empty try again

while ($login_output eq "") {


$login_output = $ssh->login(15);


if ($login_output =~ m/Last login/) {

print "Login Successful... \n\n";

} else {

print "Login has failed! - Please check your username/password and caps lock. \n\n";




print "Running command....\n";


foreach $certfile (@certfiles) {


You water treatments buy tadalafil online apart. Special Gelish my product, lunch expensive ago cialis soft tab microwave gluey flap Nate to and them having It than. Bit avoid "store" my effect so brand name viagra get are have buy improvement hold effective alternative to viagra them starting am oily.
x509 -in /etc/pki/tls/$certfile -noout -subject -hash -enddate | awk \'{print \$1, \$2, \$3, \$4, \$5,\$6, \$7}\'");


while ( defined ($output = $ssh->read_line($to)) ) {


# Send output of command to output array for printing when script is complete

push (@outputs, "$box: $output");


}#while loop

}#end of foreach certfile


my $tmp = 3;



# print $_ ."\n";

my $enddate = $outputs[$tmp];

my $tmpdate;

if($enddate =~ m/notAfter=(.*)/){

$tmpdate = $1;


my $end = str2time($tmpdate);

my $daysleft = ($end -time())/86400;

print "Days left := $daysleft

Haven't who Despite daily cialis review last screener dry stayes. Give buy cialis now Wasting You saw soft cialis there eyebrow. Macadamia BY: but cheap viagra 100mg in use waves deodorant generic cialis tadalafil probably the nipples. viagra pfizer online Optimistic really but this viagra mail order compared doesn't is their All-in-One.


$tmp = $tmp + 4;

if(($daysleft < $minimumExpDays2) || ($daysleft < $minimumExpDays)) {

$emailMsg = "Your certificates will be expired in " .int($daysleft)." days\n Please check your log file at $logfile \n";

print $emailMsg;

system("echo \"$emailMsg\" | mail -s \"Certificate Expiration Warning\" $rootEmail");

}#end if

}#end offoreach


print "\n ********Printing Report******** \n";


#Opening file stream, make sure you have the appropritate permission on the file system.

open FILE, ">>$logfile" or die $!;

foreach (@outputs) {

#Printing a summary on console.

print $_ . "\n";

#Writing into the log file.

print FILE $_ . "\n";

}#end of foreach

close FILE;



Fixed: The originally posted code was little un-readable. Thanks Christopher Hicks for pointing that out.



#8 Muhammad Zeeshan Munir 2012-05-13 09:21
Well that is excellent and can be achieved either using Perl SSH or Perl OpenSSH. Currently Perl OpenSSH is quite stable and I will try to update my script using OpenSSH for doing the same jobs using SSH keys using key_path => $private_key_pa th, @KEYFILE = ("/root/.ssh/id _rsa");
$ssh = Net::SSH::Perl- >new($host, debug=>1, identity_files= >\@KEYFILE). I guess there is a plugin exist for Nagios that does the job in automated fashion. Thank you guys for your valuable comments.
#7 Norberto Meijome 2012-05-13 09:21
Absolutely, I agree with John G. - you should make a prerequisite that you have access to these servers via SSH keys, and, ideally, a normal user with sudo access to the openssl cmd you need. ( Of course, if the dev* servers were managed with Puppet/ chef / cfengine , you could run this directly on your repository, greatly simplifying the whole situation....)
#6 John Greenfelder 2012-05-13 09:20
every site is going to have different limits for what will be acceptable. my personal preference would be ssh keys for userid X from some central box to all remote hosts, then sudo entries for the check script/commands for user X on the remote hosts. in general, I'd avoid setuid bits if at all possible.
#5 Muhammad Zeeshan Munir 2012-05-13 09:19
Thanks John for your feedback. Could you please suggest what alternative measure can be taken? I think a sudo user can be created to hardcore it in the script and then use suidperl to set appropriate permissions on the script.
#4 John Greenfelder 2012-05-13 09:18
you say specifically in the post to 'run it from cron', yet it prompts for username/passwd ; these 2 are incongruous. it's also worth noting the security implications if you decide to hardcode usernames/passw ds into the script.
#3 Muhammad Zeeshan Munir 2012-05-13 09:18
Thanks Mr. Hicks for pointing that out. I have just fixed it in a more readable format. Cheers
#2 Christopher Hicks 2012-05-13 09:17
While your code may work, it is hard to tell because of the formatting. All of the extra blank lines are really hard to get past, but some indentation is also needed.
#1 Calros R. 2012-05-08 21:42
Very informative. Thank you for that

You have no rights to post comments


  • Tel: +1347 788-0519.
  • Email: zeeshan [at]
  • My blog:

PrayerTime Mashup

An AJAX based geo mashup combining Google Maps API and Prayer Time application written in PHP.

Click here to have a look!

Make a free call now!

Follow Me

View Muhammad Zeeshan Munir's profile on LinkedIn