Created
July 8, 2020 15:48
-
-
Save countless-integers/af84686a3bf95cc5c42bc5151da12265 to your computer and use it in GitHub Desktop.
A quick and dirty script to help with checking page certificate expiry dates. It's in Python2 for compatibility reasons, so logic was redacted to serve as a template and not ready-made solution.
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 python2 | |
from os import environ | |
import datetime | |
import socket | |
import ssl | |
import logging | |
from urllib2 import Request, urlopen, URLError, HTTPError | |
import json | |
# @see: https://serverlesscode.com/post/ssl-expiration-alerts-with-lambda/ | |
class AlreadyExpired(Exception): | |
pass | |
def ssl_expiry_datetime(hostname): | |
ssl_date_fmt = r'%b %d %H:%M:%S %Y %Z' | |
context = ssl.create_default_context() | |
conn = context.wrap_socket( | |
socket.socket(socket.AF_INET), | |
server_hostname=hostname, | |
) | |
conn.settimeout(3.0) | |
conn.connect((hostname, 443)) | |
ssl_info = conn.getpeercert() | |
return datetime.datetime.strptime(ssl_info['notAfter'], ssl_date_fmt) | |
def ssl_valid_time_remaining(hostname): | |
expires = ssl_expiry_datetime(hostname) | |
logging.debug( | |
"SSL cert for %s expires at %s", | |
hostname, expires.isoformat() | |
) | |
return expires - datetime.datetime.utcnow() | |
def ssl_expires_in(hostname, buffer_days=14): | |
"""Check if certificate expires withing the specified time buffer. | |
Return False if not, datetime if yes, rais AlreadyExpired if past | |
""" | |
remaining = ssl_valid_time_remaining(hostname) | |
if remaining < datetime.timedelta(days=0): | |
raise AlreadyExpired("Cert expired %s days ago" % remaining.days) | |
elif remaining < datetime.timedelta(days=buffer_days): | |
return remaining | |
else: | |
return False | |
def notify_via_slack(report): | |
slack_channel = environ['SLACK_CHANNEL'] | |
slack_message = { | |
'username': 'Cert Expiry Bot', | |
'icon_emoji': ':closed_lock_with_key:', | |
'channel': slack_channel, | |
'text': report | |
} | |
webhook_url = environ['SLACK_WEBHOOK'] | |
req = Request(webhook_url, json.dumps(slack_message).encode('utf-8')) | |
try: | |
response = urlopen(req) | |
response.read() | |
logger.info("Slack notification posted to %s", slack_channel) | |
except HTTPError as e: | |
logger.error("Slack request failed: %d %s", e.code, e.reason) | |
except URLError as e: | |
logger.error("Slack server connection failed: %s", e.reason) | |
if __name__ == "__main__": | |
logging.basicConfig(format='[%(asctime)-15s][%(levelname)s] %(message)s') | |
logger = logging.getLogger() | |
logger.setLevel(logging.DEBUG) | |
report = ['Domains expiring in the next {} days:'.format(args.expires_in)] | |
all_domains = [] # write your own logic to fill this in, e.g. use cli arguments | |
for domain in all_domains: | |
try: | |
result = ssl_expires_in(domain, args.expires_in) | |
if result: | |
warning = "expiry for {} in {} days (last update at {})".format(domain, result.days, updated_at) | |
logging.warning(warning) | |
report.append(warning) | |
except AlreadyExpired: | |
error = "{} already expired (last update at {})".format(domain, updated_at) | |
logging.error(error) | |
report.append(error) | |
except Exception as e: | |
exception_class = type(e).__name__ | |
exception_message = "Exception ({}) during {} check: {}".format(exception_class, domain, str(e)) | |
logging.error(exception_message) | |
report.append(exception_message) | |
if notify_via_slack: | |
report.append('Scanned {} domains'.format(len(all_domains))) | |
notify_via_slack("\n".join(report)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment