SSL Check With a Script

Fundor333 | | Reading time 4 minutes | Word count 732

As a backend developer I made a lot of web application, site and other stuffs which have one or more domains. Because I don’t have money I use Let’s Encrypt for make the SSL certificate for the HTTPS and, because it is free, it is only 3 month worty certificate so you need to renovate it.

Every sistem I have use CertBot, the bot for Let’s Encrypt, who renew the certificate when needed with a cronjob. Sometime you will recive a mail from Let’s Encrypt about some ending certificate and I don’t want to check manualy every time because some of our certificate are for multiple domain in one certificate.

Script out the problem

For this script we need

  • Python 3.6 or more
  • PyOpenssl

I don’t want to use module outside the python core but, in this case, I need a module for the newest type of certificate so… I write it with a config file with all my site.

#!/usr/bin/env python3

import datetime
import logging
import os
import socket
import ssl

import OpenSSL

logger = logging.getLogger("SSLVerify")

Start with the import for working with date, log, make a request for a ssl certificate and open a file. Also set the logger for the script.

def ssl_expiry_datetime(hostname: str) -> datetime.datetime:
  cert = ssl.get_server_certificate((hostname, 443))
  x509 = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, cert)
  return datetime.datetime.strptime(x509.get_notAfter().decode('ascii'), '%Y%m%d%H%M%SZ')

This function get a url, get the certificate of the domain and return a datetime python object listing the last minute the certificate validity. After this the site is not https

def ssl_valid_time_remaining(hostname: str) -> datetime.timedelta:
  expires = ssl_expiry_datetime(hostname)
  logger.debug("SSL cert for {} expires at {}".format(hostname, expires.isoformat()))
  return expires - datetime.datetime.utcnow()

Return a python object with the remaining days of the certificate. It can be negative if is exspired.


def test_host(hostname: str, buffer_days: int = 30) -> str:
  try:
    will_expire_in = ssl_valid_time_remaining(hostname)
  except ValueError as e:
    return "❌ " + hostname + " cert error " + str(e)
  except socket.timeout as e:
    return "❌ " + hostname + " could not connect"
  else:
    if will_expire_in < datetime.timedelta(days=0):
      return "❌ " + hostname + " cert will expired"
    elif will_expire_in < datetime.timedelta(days=buffer_days):
      return "⏳ " + hostname + " cert will expire in " + will_expire_in
    else:
      return "✔️ " + hostname + " cert is fine"

This function build the string for the user to see about the domain. Use the emoji for fast reading for error1

def popupmsg(msg):
  logger.info(msg)

Print into the console the output. I search a good way for making a popup or a toast for gui purpuise but I can’t make it multiOS so I use the standard output

if __name__ == "__main__":
  f = open(os.path.expanduser("~/.sslverify"), "r")
  end_message = ""
  for host in f.readlines():
    host = host.strip()
    message = test_host(host)
    end_message += "\n" + message
  print(end_message)
  popupmsg(end_message)

The starter of the all script. The input of the script is a dotfiles , a .txt with one site for row, In this way I backup it with my dotbot and take with me into every machine I work in.

Changing the output system you can easly make a cronjob out of this script or a lambda function for your need.

I am thinking about making this script into a module or bash app in the near future.

All the code

We end with all the code into a single block

#!/usr/bin/env python3

import datetime
import logging
import os
import socket
import ssl

import OpenSSL

logger = logging.getLogger("SSLVerify")


def ssl_expiry_datetime(hostname: str) -> datetime.datetime:
  cert = ssl.get_server_certificate((hostname, 443))
  x509 = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, cert)
  return datetime.datetime.strptime(x509.get_notAfter().decode('ascii'), '%Y%m%d%H%M%SZ')


def ssl_valid_time_remaining(hostname: str) -> datetime.timedelta:
  expires = ssl_expiry_datetime(hostname)
  logger.debug("SSL cert for {} expires at {}".format(hostname, expires.isoformat()))
  return expires - datetime.datetime.utcnow()


def test_host(hostname: str, buffer_days: int = 30) -> str:
  try:
    will_expire_in = ssl_valid_time_remaining(hostname)
  except ValueError as e:
    return "❌ " + hostname + " cert error " + str(e)
  except socket.timeout as e:
    return "❌ " + hostname + " could not connect"
  else:
    if will_expire_in < datetime.timedelta(days=0):
      return "❌ " + hostname + " cert will expired"
    elif will_expire_in < datetime.timedelta(days=buffer_days):
      return "⏳ " + hostname + " cert will expire in " + will_expire_in
    else:
      return "✔️ " + hostname + " cert is fine"


def popupmsg(msg):
  logger.info(msg)


if __name__ == "__main__":
  f = open(os.path.expanduser("~/.sslverify"), "r")
  end_message = ""
  for host in f.readlines():
    host = host.strip()
    message = test_host(host)
    end_message += "\n" + message
  print(end_message)
  popupmsg(end_message)

  1. The “error” in this case are the only one colored emoji of the bunch. ↩︎


Comments

To reply to this post, you can send a Webmention or you can toot me at [email protected]
You mentioned this post on your site? Send a Webmention


See Also