DIY Infrastructure Monitoring Part 2

Rob MitchellRob Mitchell
2 min read

My current organization uses Let's Encrypt certificates that expire at 90 days old. Obviously, we need to know ahead of time when any one of them is going to expire. Rather than maintaining yet another spreadsheet wouldn't it be nice to be notified of an expiration about 5 days out? In this post we will automate the checking of SSL/TLS certificates to be informed of an upcoming expiration.

Requirements

We need the pyOpenSSL package for the utility info class we are going to make.

~/pylib/infracheck$ cat requirements.txt 
requests==2.22.0
pyOpenSSL==22.1.0
~/pylib/infracheck$

Install the pip packages in the requirements.txt file with pip install -r requirements.txt

The SSLInfo class

~/pylib/infracheck/web/ssl_info.py

from datetime import datetime, timezone
import OpenSSL
import ssl
import socket


class SSLInfo:
    def __init__(self, hostname, port=443):
        self.hostname = hostname
        self.port = port

    def get_num_days_before_expired(self) -> int:
        """
        Get number of days before an TLS/SSL certificate of a domain expired
        """
        try:
            context = ssl.SSLContext()
            connection = socket.create_connection((self.hostname, self.port))
            sock = context.wrap_socket(connection, server_hostname = self.hostname)
            certificate = sock.getpeercert(True)
            pem_cert = ssl.DER_cert_to_PEM_cert(certificate)
            x509 = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, pem_cert)
            cert_expires = datetime.strptime(x509.get_notAfter().decode('utf-8'), '%Y%m%d%H%M%S%z')
            sock.close()
            connection.close()
            return (cert_expires - datetime.now(timezone.utc)).days
        finally:
            sock.close()
            connection.close()

Using the SSLInfo class

Let's use the SSLInfo class manually. As we did with part 1, we will use github to validate the class.

~$ python3 
Python 3.8.10 (default, Jun 22 2022, 20:18:18) 
[GCC 9.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from infracheck.web.ssl_info import SSLInfo 
>>> SSLInfo('github.com').get_num_days_before_expired()
112
>>>

We can see from the output that (today) github.com's SSL certificate will expire in 112 days.

Adding the infrastructure test

~/infra-tests/test_github_certificate.py

from infracheck.web.ssl_info import SSLInfo
import unittest

class Test_Github_SSL_Certificate(unittest.TestCase):
    def test_github_certificate_expires_lessthan_5_days(self):
        self.assertTrue(SSLInfo('github.com').get_num_days_before_expired() > 5)

Running the test

We've added the test to our infra-tests directory, now let's run it with our run script from part 1).

~/infra-tests$ bash run.sh 
test_github_certificate_expires_lessthan_5_days (test_github_certificate.Test_Github_SSL_Certificate) ... ok

----------------------------------------------------------------------
Ran 1 test in 0.097s

OK
~/infra-tests$

Now we simply add a test_XXX method for each certificate we want to be informed about.

0
Subscribe to my newsletter

Read articles from Rob Mitchell directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Rob Mitchell
Rob Mitchell