]> git.siccegge.de Git - tools.git/blob - tls-check
Make tls-check more flexible and add smtp/starttls check
[tools.git] / tls-check
1 #!/usr/bin/python
2
3 from __future__ import print_function
4 from optparse import OptionParser
5 from ssl import SSLContext, PROTOCOL_TLSv1_2, CERT_REQUIRED, cert_time_to_seconds, SSLError, CertificateError
6 from socket import socket, AF_INET6
7 from datetime import datetime, timedelta
8 from smtplib import SMTP
9
10 VERBOSE=False
11
12 class Verifier:
13 def __init__(self, cafile, warn, crit):
14 self.cafile = cafile
15 self.crit = crit
16 self.warn = warn
17
18 def check(self, proto, host, port):
19 context = SSLContext(PROTOCOL_TLSv1_2)
20 context.verify_mode = CERT_REQUIRED
21 context.load_verify_locations(self.cafile)
22 if hasattr(self, 'remote_check_%s' % proto):
23 getattr(self, 'remote_check_%s' % proto)(context, host, port)
24
25 def remote_check_smtp(self, context, host, port):
26 smtp = SMTP(host, port)
27 try:
28 smtp.starttls(context=context)
29 except SSLError:
30 print("CRIT (invalid certificate) %s:%d" % (host, port))
31 return 2
32
33 cert = smtp.sock.getpeercert()
34 return self.check_cert(cert, host, port)
35
36 def remote_check_ssl(self, context, host, port):
37 connection = context.wrap_socket(socket(AF_INET6),
38 server_hostname=host)
39 try:
40 connection.connect((host, port))
41 except SSLError:
42 print("CRIT (invalid certificate) %s:%d" % (host, port))
43 return 2
44
45 cert = connection.getpeercert()
46 return self.check_cert(cert, host, port)
47
48 def check_cert(self, data, host, port):
49 expiretimestamp = cert_time_to_seconds(data['notAfter'])
50 delta = datetime.utcfromtimestamp(expiretimestamp) - datetime.utcnow()
51
52 if delta < self.crit:
53 print("CRIT (expires in %s) %s:%d" % (delta, host, port))
54 return 2
55 elif delta < self.warn:
56 print("WARN (expires in %s) %s:%d" % (delta, host, port))
57 return 1
58
59 def main():
60 global VERBOSE
61 parser = OptionParser()
62 parser.add_option("-n", "--name",
63 action="append", type="string", dest="hosts",
64 help="hostname:port to check for expired certificates")
65 parser.add_option("-w", "--warning-days",
66 action="store", type=int, dest="warn", default=15,
67 help="minimum remaining validity in days before a warning is issued")
68 parser.add_option("-c", "--critical-days",
69 action="store", type=int, dest="crit", default=5,
70 help="minimum remaining validity in days before a warning is issued")
71 parser.add_option("-v", action="store_true", dest="verbose", default=False)
72 parser.add_option("-q", action="store_false", dest="verbose")
73 parser.add_option("--ca", action="store", type="string", dest="ca",
74 default="/etc/ssl/certs/ca-certificates.crt",
75 help="ca certificate bundle")
76
77
78 opts, _args = parser.parse_args()
79
80 VERBOSE = opts.verbose
81 if not opts.hosts:
82 parser.error("needs at least one host")
83
84 verifier = Verifier(opts.ca, timedelta(opts.warn), timedelta(opts.crit))
85
86 try:
87 hosts = [ (i[0], i[1], int(i[2])) for i in [ j.split(':', 2) for j in opts.hosts ] ]
88 except (ValueError, IndexError):
89 parser.error("names need to be in PROTO:DNSNAME:PORT format")
90
91 for proto, host, port in hosts:
92 verifier.check(proto, host, port)
93
94 if __name__ == "__main__":
95 main()