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