sslcheck module: (remote) SSL certificate expiry time check (#5365)
* added WIP ssl certificate expiry time check plugin * fixing bugs * more bugfixes * cleaned up * fixed graphing * More pretty readme * cleaned up style * change author * simplify * add days_until_expiration_warn and correctly calc seconds * update config * config update * readme update * return false from check if module failed to collect data * set default update_every to 60 * add alarm * add sslcheck to makefile * fix indentation * add crit to alarm * update conf * update readme * add days_until_expiration_critical * change default days_until_expiration_warning to 14 * minor
Peter Thurner committed
Mar 13, 2019 at 10:14 UTC
97699c5c60342be372d776da4829167adfdfc133
7 files changed
+233
collectors/python.d.plugin/Makefile.am
+1
@@ -96,6 +96,7 @@ include smartd_log/Makefile.inc
96
include spigotmc/Makefile.inc
97
include springboot/Makefile.inc
98
include squid/Makefile.inc
99
+include sslcheck/Makefile.inc
100
include tomcat/Makefile.inc
101
include tor/Makefile.inc
102
include traefik/Makefile.inc
collectors/python.d.plugin/sslcheck/Makefile.inc
new
+12
@@ -0,0 +1,12 @@
1
+# SPDX-License-Identifier: GPL-3.0-or-later
2
+
3
+# THIS IS NOT A COMPLETE Makefile
4
+# IT IS INCLUDED BY ITS PARENT'S Makefile.am
5
+# IT IS REQUIRED TO REFERENCE ALL FILES RELATIVE TO THE PARENT
6
+
7
+# install these files
8
+dist_python_DATA += sslcheck/sslcheck.chart.py
9
+dist_pythonconfig_DATA += sslcheck/sslcheck.conf
10
+
11
+# do not install these files, but include them in the distribution
12
+dist_noinst_DATA += sslcheck/README.md sslcheck/Makefile.inc
\ No newline at end of file
collectors/python.d.plugin/sslcheck/README.md
new
+21
@@ -0,0 +1,21 @@
1
+# SSL certificate expiry check
2
+
3
+Checks the time until a remote SSL certificate expires.
4
+
5
+## Requirements
6
+
7
+None
8
+
9
+### configuration
10
+
11
+```yaml
12
+update_every : 60
13
+
14
+example_org:
15
+ host: 'example.org'
16
+
17
+my_site_org:
18
+ host: 'my-site.org'
19
+ days_until_expiration_warning: 10
20
+ days_until_expiration_critical: 1
21
+```
collectors/python.d.plugin/sslcheck/sslcheck.chart.py
new
+112
@@ -0,0 +1,112 @@
1
+# -*- coding: utf-8 -*-
2
+# Description: simple ssl expiration check netdata python.d module
3
+# Original Author: Peter Thurner (github.com/p-thurner)
4
+# SPDX-License-Identifier: GPL-3.0-or-later
5
+
6
+import datetime
7
+import socket
8
+import ssl
9
+
10
+from bases.FrameworkServices.SimpleService import SimpleService
11
+
12
+
13
+update_every = 60
14
+
15
+
16
+ORDER = [
17
+ 'time_until_expiration',
18
+]
19
+
20
+CHARTS = {
21
+ 'time_until_expiration': {
22
+ 'options': [
23
+ None,
24
+ 'Time Until Certificate Expiration',
25
+ 'seconds',
26
+ 'certificate expiration time',
27
+ 'sslcheck.time_until_expiration',
28
+ 'line',
29
+ ],
30
+ 'lines': [
31
+ ['time'],
32
+ ],
33
+ 'variables': [
34
+ ['days_until_expiration_warning'],
35
+ ['days_until_expiration_critical'],
36
+ ],
37
+ },
38
+}
39
+
40
+
41
+SSL_DATE_FMT = r'%b %d %H:%M:%S %Y %Z'
42
+
43
+DEFAULT_PORT = 443
44
+DEFAULT_CONN_TIMEOUT = 3
45
+DEFAULT_DAYS_UNTIL_WARN_LIMIT = 14
46
+DEFAULT_DAYS_UNTIL_CRIT_LIMIT = 7
47
+
48
+
49
+class Service(SimpleService):
50
+ def __init__(self, configuration=None, name=None):
51
+ SimpleService.__init__(self, configuration=configuration, name=name)
52
+ self.order = ORDER
53
+ self.definitions = CHARTS
54
+ self.host = configuration.get('host')
55
+ self.port = configuration.get('port', DEFAULT_PORT)
56
+ self.timeout = configuration.get('timeout', DEFAULT_CONN_TIMEOUT)
57
+ self.days_warn = configuration.get('days_until_expiration_warning', DEFAULT_DAYS_UNTIL_WARN_LIMIT)
58
+ self.days_crit = configuration.get('days_until_expiration_critical', DEFAULT_DAYS_UNTIL_CRIT_LIMIT)
59
+
60
+ def check(self):
61
+ if not self.host:
62
+ self.error('host parameter is mandatory, but it is not set')
63
+ return False
64
+
65
+ self.debug('run check : host {host}:{port}, update every {update}s, timeout {timeout}s'.format(
66
+ host=self.host, port=self.port, update=self.update_every, timeout=self.timeout))
67
+
68
+ return bool(self.get_data())
69
+
70
+ def get_data(self):
71
+ conn = create_ssl_conn(self.host, self.timeout)
72
+
73
+ try:
74
+ conn.connect((self.host, self.port))
75
+ except Exception as error:
76
+ self.error("error on connection to {0}:{1} : {2}".format(self.host, self.port, error))
77
+ return None
78
+
79
+ peer_cert = conn.getpeercert()
80
+ conn.close()
81
+
82
+ if peer_cert is None:
83
+ self.warning("no certificate was provided by {0}:{1}".format(self.host, self.port))
84
+ return None
85
+ elif not peer_cert:
86
+ self.warning("certificate was provided by {0}:{1}, but not validated".format(self.host, self.port))
87
+ return None
88
+
89
+ return {
90
+ 'time': cert_expiration_seconds(peer_cert),
91
+ 'days_until_expiration_warning': self.days_warn,
92
+ 'days_until_expiration_critical': self.days_crit,
93
+ }
94
+
95
+
96
+def create_ssl_conn(hostname, timeout):
97
+ context = ssl.create_default_context()
98
+ conn = context.wrap_socket(
99
+ socket.socket(socket.AF_INET),
100
+ server_hostname=hostname,
101
+ )
102
+ conn.settimeout(timeout)
103
+
104
+ return conn
105
+
106
+
107
+def cert_expiration_seconds(cert):
108
+ expiration_date = datetime.datetime.strptime(cert['notAfter'], SSL_DATE_FMT)
109
+ current_date = datetime.datetime.utcnow()
110
+ delta = expiration_date - current_date
111
+
112
+ return ((delta.days * 86400 + delta.seconds) * 10 ** 6 + delta.microseconds) / 10 ** 6
collectors/python.d.plugin/sslcheck/sslcheck.conf
new
+76
@@ -0,0 +1,76 @@
1
+# netdata python.d.plugin configuration for sslcheck
2
+#
3
+# This file is in YaML format. Generally the format is:
4
+#
5
+# name: value
6
+#
7
+# There are 2 sections:
8
+# - global variables
9
+# - one or more JOBS
10
+#
11
+# JOBS allow you to collect values from multiple sources.
12
+# Each source will have its own set of charts.
13
+#
14
+# JOB parameters have to be indented (using spaces only, example below).
15
+
16
+# ----------------------------------------------------------------------
17
+# Global Variables
18
+# These variables set the defaults for all JOBs, however each JOB
19
+# may define its own, overriding the defaults.
20
+
21
+# update_every sets the default data collection frequency.
22
+# If unset, the python.d.plugin default is used.
23
+# update_every: 1
24
+
25
+# priority controls the order of charts at the netdata dashboard.
26
+# Lower numbers move the charts towards the top of the page.
27
+# If unset, the default for python.d.plugin is used.
28
+# priority: 60000
29
+
30
+# penalty indicates whether to apply penalty to update_every in case of failures.
31
+# Penalty will increase every 5 failed updates in a row. Maximum penalty is 10 minutes.
32
+# penalty: yes
33
+
34
+# autodetection_retry sets the job re-check interval in seconds.
35
+# The job is not deleted if check fails.
36
+# Attempts to start the job are made once every autodetection_retry.
37
+# This feature is disabled by default.
38
+# autodetection_retry: 0
39
+
40
+# ----------------------------------------------------------------------
41
+# JOBS (data collection sources)
42
+#
43
+# The default JOBS share the same *name*. JOBS with the same name
44
+# are mutually exclusive. Only one of them will be allowed running at
45
+# any time. This allows autodetection to try several alternatives and
46
+# pick the one that works.
47
+#
48
+# Any number of jobs is supported.
49
+#
50
+# All python.d.plugin JOBS (for all its modules) support a set of
51
+# predefined parameters. These are:
52
+#
53
+# job_name:
54
+# name: myname # the JOB's name as it will appear at the
55
+# # dashboard (by default is the job_name)
56
+# # JOBs sharing a name are mutually exclusive
57
+# update_every: 1 # the JOB's data collection frequency
58
+# priority: 60000 # the JOB's order on the dashboard
59
+# penalty: yes # the JOB's penalty
60
+# autodetection_retry: 0 # the JOB's re-check interval in seconds
61
+#
62
+# Additionally to the above, sslcheck also supports the following:
63
+#
64
+# host: 'host' # [required] the remote host address in either IPv4, IPv6 or as DNS name
65
+# port: 443 # [optional] the port number to check. Specify an integer, not service name. Default is 443.
66
+# timeout: 3 # [optional] the socket timeout when connecting
67
+# days_until_expiration_warning: 14 # [optional] days before the alarm status is warning. Default is 14.
68
+# days_until_expiration_critical: 7 # [optional] days before the alarm status is critical. Default is 7.
69
+#
70
+# ----------------------------------------------------------------------
71
+# AUTO-DETECTION JOBS
72
+# only one of them will run (they have the same name)
73
+
74
+# example_org:
75
+# host : 'example.org'
76
+# days_until_expiration_warning: 10
health/Makefile.am
+1
@@ -68,6 +68,7 @@ dist_healthconfig_DATA = \
68
health.d/retroshare.conf \
69
health.d/softnet.conf \
70
health.d/squid.conf \
71
+ health.d/sslcheck.conf \
72
health.d/stiebeleltron.conf \
73
health.d/swap.conf \
74
health.d/tcp_conn.conf \
health/health.d/sslcheck.conf
new
+10
@@ -0,0 +1,10 @@
1
+
2
+template: sslcheck_days_until_expiration
3
+ on: sslcheck.time_until_expiration
4
+ calc: $time
5
+ units: seconds
6
+ every: 60s
7
+ warn: $this < $days_until_expiration_warning*24*60*60
8
+ crit: $this < $days_until_expiration_critical*24*60*60
9
+ info: certificate time until expiration
10
+ to: webmaster