Python Script Checking SSL Certificate Expiry dates

Photo by Nick Fewings on Unsplash

So we have alot of certificates, and its now a daily task to watch for ones that are about to expire. We use nagios to alert for that so it’s not a big problem. Anyhow. We do have access to a CSV file of all our certificates. So I can run through that CSV file and generate a report of expiry dates.

So the input CSV might be:

helpdesk.example.com,443
exttest.example.com,8443
mac-update.example.com,443
api.example.com,443 
logos.example.com,443
support.example.com,443

And the output would be (last column is days to go before expirey:

  helpdesk.example.com:443  Feb 26 17:25:00 2021 GMT   3 
   exttest.example.com:8443 Feb 26 17:41:00 2021 GMT   3 
mac-update.example.com:443  Feb 27 16:48:00 2021 GMT   4 
       api.example.com:443  Feb 28 12:03:00 2021 GMT   5 
     logos.example.com:443  Mar  6 16:20:00 2021 GMT  11 
  

Or display using a pandas dataframe:

#!/usr/bin//env python3
import socket
import sys
import subprocess
import shutil
import os
import getopt
import re
import csv
from datetime import datetime, date,timedelta
from operator import itemgetter
import pandas as pd

websites = []
sorted_websites = []

def check_it_runs(cmd):
   try:
     if subprocess.run(cmd.split(), timeout=3, check=True, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT, stdin=subprocess.DEVNULL):
       return True
   except:
     return False

with open('l.txt') as csvDataFile:
  csvReader = csv.reader(csvDataFile)
  for row in csvReader:
    websites.append( { "url": row[0], \
			"port": str(row[1]), \
		        "expiry_date": "NaN", \
		        "daystogo": -1, }  )

for website in websites:
    cmd="openssl s_client -connect " +  website["url"] + ":" + website["port"] +  " -servername " + website["url"]  
    if check_it_runs(cmd):
      cmd1 = "openssl s_client -connect " +  website["url"] + ":" + website["port"] +  " -servername " + website["url"]  
      cmd2 = "openssl x509 -noout -dates"
      p1 = subprocess.Popen(cmd1.split(), stderr=subprocess.STDOUT, stdout=subprocess.PIPE)
      results = str(subprocess.check_output(cmd2.split(), stderr=subprocess.STDOUT, stdin=p1.stdout))
      matchObj=re.search( r'(.*notAfter=)(.*)(\\n)',results)
      website["expiry_date"] = matchObj.group(2)
      daystogo = datetime.strptime( website["expiry_date"],"%b %d %H:%M:%S %Y %Z"  )
      daystogo = daystogo - datetime.now()
      daystogo = re.sub(', \d{1,}:\d{2}:\d{2}\.\d{6,}$',  '', str(daystogo))
      daystogo = re.sub(' days','', daystogo)
      website['daystogo']=int(daystogo)
    else:
      pass

for website in sorted(websites, key=itemgetter('daystogo')):
    #print("%40s:%-4s \'%-30s\' %3s " % ( website['url'], website['port'],  website['expiry_date'], website['daystogo']))
    sorted_websites.append( { "url":website['url'], "port":website['port'], "expiry_date":website['expiry_date'], "daystogo":website['daystogo'] } )


# Creates DataFrame.  
df = pd.DataFrame(sorted_websites) 
print(df)

sys.exit()