SSL Check With a Script

home ~ SSL Check With a Script

Card image cap

coding devops
2020-08-20

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.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
#!/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.

14
15
16
17
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

20
21
22
23
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.

27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

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

44
45
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

48
49
50
51
52
53
54
55
56
57
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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#!/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. ↩︎

by Fundor333


See Also




comments powered by Disqus