Created
July 6, 2015 01:49
-
-
Save twirrim/66bfd2b0705f612f9fa8 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
import socket | |
import ssl | |
import datetime | |
import smtplib | |
import argparse | |
import sys | |
import logging | |
# The one external library | |
import certifi | |
def get_connection(address, port): | |
''' Establishes an SSL connection to the specified address and port ''' | |
# Find out where certifi's root certs are | |
cacerts_location = certifi.where() | |
# Establish the socket | |
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | |
# Wrap up the socket with ssl | |
ssl_sock = ssl.wrap_socket(s, | |
ca_certs=cacerts_location, | |
cert_reqs=ssl.CERT_REQUIRED) | |
ssl_sock.connect((address, port)) | |
return ssl_sock | |
def verify_hostname(connection, address): | |
''' | |
Pass in the connection, along with address to verify the certificate | |
matches the specified address. | |
''' | |
for entry in connection.getpeercert()['subject']: | |
if entry[0][0] == "commonName": | |
if entry[0][1] == address: | |
# It's valid | |
return True | |
# If it's not against 'subject', it could be a 'subjectAltName' | |
for entry in connection.getpeercert()['subjectAltName']: | |
if entry[1] == address: | |
# It's valid! | |
return True | |
# If we don't match either 'subject' or 'subjectAltName' it's not valid | |
return False | |
def expiring_certificate(connection, expiry_days): | |
''' | |
Pass in the connection and number of days. Verify that the expiry date isn't | |
within the specified number of days | |
''' | |
# Convert the presented certificate's expiry date into a datetime object. | |
# This could be done in one line, but for readability, we'll do this over three | |
expiry_date = connection.getpeercert()['notAfter'] | |
expiry_epoch = ssl.cert_time_to_seconds(expiry_date) | |
expires = datetime.datetime.fromtimestamp(expiry_epoch) | |
# Create a datetime object of the specified date | |
now = datetime.datetime.now() | |
specified_date = now + datetime.timedelta(days=expiry_days) | |
# this evalutes to True if the certificate expires before the specified | |
# expiry date. | |
return expires < specified_date | |
def send_email(expiry_date, address, email_to, email_from): | |
server = "localhost" | |
# It would probably be neater if used format here | |
message = """\ | |
From: %s | |
To: %s | |
Subject: SSL certificate for %s due to expire %s | |
The SSL certificate for %s is due to expire on %s. Please update and replace. | |
""" % (email_from, email_to, address, expiry_date, address, expiry_date) | |
smtp_server = smtplib.SMTP(server) | |
smtp_server.sendmail(email_from, [email_to], message) | |
smtp_server.quit() | |
def get_arguments(): | |
''' parse the command line arguments ''' | |
parser = argparse.ArgumentParser(description=__doc__) | |
parser.add_argument("--address", dest='address', required=True, type=str, help='The endpoint to check') | |
parser.add_argument("--port", dest='port', required=True, type=int, help='The port on the endpoint to check') | |
parser.add_argument("--to", dest='to_address', required=True, type=str, help='who to email in case of alert') | |
parser.add_argument("--from", dest='from_address', required=True, type=str, help='who should email come from') | |
parser.add_argument("--expiry", dest='expiry', required=True, type=int, help='Number of days until expiry') | |
return parser.parse_args() | |
def main(): | |
''' Tie it all together''' | |
logging.basicConfig(level=logging.DEBUG) | |
args = get_arguments() | |
connection = get_connection(args.address, args.port) | |
if verify_hostname(connection, args.address): | |
logging.debug("Certificate for %s is valid" % args.address) | |
if expiring_certificate(connection, args.expiry): | |
logging.debug("Certificate for %s is going to expire" % args.address) | |
send_email(connection.getpeercert()['notAfter'], | |
args.address, | |
args.to_address, | |
args.from_address) | |
else: | |
logging.debug("Certificate is valid and isn't expiring") | |
else: | |
# If you get here, oops. | |
logging.critical("Certificate did not match host name") | |
sys.exit(1) | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment