2 ## Call with "--help" for documentation.
4 import argparse, subprocess, re, os, datetime
6 def check_dir(dirname, days):
7 for name in os.listdir(dirname):
8 name = os.path.join(dirname, name)
9 if os.path.isdir(name):
11 elif name.endswith('.crt'):
12 check_file(name, days)
14 def check_file(filename, days):
15 valid_not_after = subprocess.check_output(["openssl", "x509", "-enddate", "-in", filename, "-out", "/dev/null"]).decode('utf-8')
16 match = re.match("notAfter=([a-zA-Z0-9: ]+)", valid_not_after)
17 assert match is not None, "Unexpected output from openssl: valid_not_after"
18 enddate = match.group(1)
19 enddate = datetime.datetime.strptime(enddate, '%b %d %X %Y %Z')
20 delta = enddate - datetime.datetime.now()
21 if delta < datetime.timedelta(days=days):
22 print("{} expires at {}, which is in {} days".format(filename, enddate, delta.days))
24 if __name__ == "__main__":
25 parser = argparse.ArgumentParser(description='Check for soon-to-expire (and already expired) certificates')
26 parser.add_argument("-d", "--days", metavar='N',
27 dest="days", type=int, default=14,
28 help="Warn about certificates valid for less than N (default 14).")
29 parser.add_argument("certs", metavar='CERTS', nargs='+',
30 help="These certificate files are checked. Directories are searched recursively for files called '*.crt'.")
31 args = parser.parse_args()
33 for name in args.certs:
34 if os.path.isdir(name):
35 check_dir(name, args.days)
37 check_file(name, args.days)