From 97699c5c60342be372d776da4829167adfdfc133 Mon Sep 17 00:00:00 2001 From: Peter Thurner Date: Wed, 13 Mar 2019 10:14:22 +0100 Subject: 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 --- collectors/python.d.plugin/sslcheck/Makefile.inc | 12 +++ collectors/python.d.plugin/sslcheck/README.md | 21 ++++ .../python.d.plugin/sslcheck/sslcheck.chart.py | 112 +++++++++++++++++++++ collectors/python.d.plugin/sslcheck/sslcheck.conf | 76 ++++++++++++++ 4 files changed, 221 insertions(+) create mode 100644 collectors/python.d.plugin/sslcheck/Makefile.inc create mode 100644 collectors/python.d.plugin/sslcheck/README.md create mode 100644 collectors/python.d.plugin/sslcheck/sslcheck.chart.py create mode 100644 collectors/python.d.plugin/sslcheck/sslcheck.conf (limited to 'collectors/python.d.plugin/sslcheck') diff --git a/collectors/python.d.plugin/sslcheck/Makefile.inc b/collectors/python.d.plugin/sslcheck/Makefile.inc new file mode 100644 index 0000000000..5c798d3ed0 --- /dev/null +++ b/collectors/python.d.plugin/sslcheck/Makefile.inc @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-3.0-or-later + +# THIS IS NOT A COMPLETE Makefile +# IT IS INCLUDED BY ITS PARENT'S Makefile.am +# IT IS REQUIRED TO REFERENCE ALL FILES RELATIVE TO THE PARENT + +# install these files +dist_python_DATA += sslcheck/sslcheck.chart.py +dist_pythonconfig_DATA += sslcheck/sslcheck.conf + +# do not install these files, but include them in the distribution +dist_noinst_DATA += sslcheck/README.md sslcheck/Makefile.inc \ No newline at end of file diff --git a/collectors/python.d.plugin/sslcheck/README.md b/collectors/python.d.plugin/sslcheck/README.md new file mode 100644 index 0000000000..1c5dd6da4e --- /dev/null +++ b/collectors/python.d.plugin/sslcheck/README.md @@ -0,0 +1,21 @@ +# SSL certificate expiry check + +Checks the time until a remote SSL certificate expires. + +## Requirements + +None + +### configuration + +```yaml +update_every : 60 + +example_org: + host: 'example.org' + +my_site_org: + host: 'my-site.org' + days_until_expiration_warning: 10 + days_until_expiration_critical: 1 +``` diff --git a/collectors/python.d.plugin/sslcheck/sslcheck.chart.py b/collectors/python.d.plugin/sslcheck/sslcheck.chart.py new file mode 100644 index 0000000000..64e926f841 --- /dev/null +++ b/collectors/python.d.plugin/sslcheck/sslcheck.chart.py @@ -0,0 +1,112 @@ +# -*- coding: utf-8 -*- +# Description: simple ssl expiration check netdata python.d module +# Original Author: Peter Thurner (github.com/p-thurner) +# SPDX-License-Identifier: GPL-3.0-or-later + +import datetime +import socket +import ssl + +from bases.FrameworkServices.SimpleService import SimpleService + + +update_every = 60 + + +ORDER = [ + 'time_until_expiration', +] + +CHARTS = { + 'time_until_expiration': { + 'options': [ + None, + 'Time Until Certificate Expiration', + 'seconds', + 'certificate expiration time', + 'sslcheck.time_until_expiration', + 'line', + ], + 'lines': [ + ['time'], + ], + 'variables': [ + ['days_until_expiration_warning'], + ['days_until_expiration_critical'], + ], + }, +} + + +SSL_DATE_FMT = r'%b %d %H:%M:%S %Y %Z' + +DEFAULT_PORT = 443 +DEFAULT_CONN_TIMEOUT = 3 +DEFAULT_DAYS_UNTIL_WARN_LIMIT = 14 +DEFAULT_DAYS_UNTIL_CRIT_LIMIT = 7 + + +class Service(SimpleService): + def __init__(self, configuration=None, name=None): + SimpleService.__init__(self, configuration=configuration, name=name) + self.order = ORDER + self.definitions = CHARTS + self.host = configuration.get('host') + self.port = configuration.get('port', DEFAULT_PORT) + self.timeout = configuration.get('timeout', DEFAULT_CONN_TIMEOUT) + self.days_warn = configuration.get('days_until_expiration_warning', DEFAULT_DAYS_UNTIL_WARN_LIMIT) + self.days_crit = configuration.get('days_until_expiration_critical', DEFAULT_DAYS_UNTIL_CRIT_LIMIT) + + def check(self): + if not self.host: + self.error('host parameter is mandatory, but it is not set') + return False + + self.debug('run check : host {host}:{port}, update every {update}s, timeout {timeout}s'.format( + host=self.host, port=self.port, update=self.update_every, timeout=self.timeout)) + + return bool(self.get_data()) + + def get_data(self): + conn = create_ssl_conn(self.host, self.timeout) + + try: + conn.connect((self.host, self.port)) + except Exception as error: + self.error("error on connection to {0}:{1} : {2}".format(self.host, self.port, error)) + return None + + peer_cert = conn.getpeercert() + conn.close() + + if peer_cert is None: + self.warning("no certificate was provided by {0}:{1}".format(self.host, self.port)) + return None + elif not peer_cert: + self.warning("certificate was provided by {0}:{1}, but not validated".format(self.host, self.port)) + return None + + return { + 'time': cert_expiration_seconds(peer_cert), + 'days_until_expiration_warning': self.days_warn, + 'days_until_expiration_critical': self.days_crit, + } + + +def create_ssl_conn(hostname, timeout): + context = ssl.create_default_context() + conn = context.wrap_socket( + socket.socket(socket.AF_INET), + server_hostname=hostname, + ) + conn.settimeout(timeout) + + return conn + + +def cert_expiration_seconds(cert): + expiration_date = datetime.datetime.strptime(cert['notAfter'], SSL_DATE_FMT) + current_date = datetime.datetime.utcnow() + delta = expiration_date - current_date + + return ((delta.days * 86400 + delta.seconds) * 10 ** 6 + delta.microseconds) / 10 ** 6 diff --git a/collectors/python.d.plugin/sslcheck/sslcheck.conf b/collectors/python.d.plugin/sslcheck/sslcheck.conf new file mode 100644 index 0000000000..933246eefc --- /dev/null +++ b/collectors/python.d.plugin/sslcheck/sslcheck.conf @@ -0,0 +1,76 @@ +# netdata python.d.plugin configuration for sslcheck +# +# This file is in YaML format. Generally the format is: +# +# name: value +# +# There are 2 sections: +# - global variables +# - one or more JOBS +# +# JOBS allow you to collect values from multiple sources. +# Each source will have its own set of charts. +# +# JOB parameters have to be indented (using spaces only, example below). + +# ---------------------------------------------------------------------- +# Global Variables +# These variables set the defaults for all JOBs, however each JOB +# may define its own, overriding the defaults. + +# update_every sets the default data collection frequency. +# If unset, the python.d.plugin default is used. +# update_every: 1 + +# priority controls the order of charts at the netdata dashboard. +# Lower numbers move the charts towards the top of the page. +# If unset, the default for python.d.plugin is used. +# priority: 60000 + +# penalty indicates whether to apply penalty to update_every in case of failures. +# Penalty will increase every 5 failed updates in a row. Maximum penalty is 10 minutes. +# penalty: yes + +# autodetection_retry sets the job re-check interval in seconds. +# The job is not deleted if check fails. +# Attempts to start the job are made once every autodetection_retry. +# This feature is disabled by default. +# autodetection_retry: 0 + +# ---------------------------------------------------------------------- +# JOBS (data collection sources) +# +# The default JOBS share the same *name*. JOBS with the same name +# are mutually exclusive. Only one of them will be allowed running at +# any time. This allows autodetection to try several alternatives and +# pick the one that works. +# +# Any number of jobs is supported. +# +# All python.d.plugin JOBS (for all its modules) support a set of +# predefined parameters. These are: +# +# job_name: +# name: myname # the JOB's name as it will appear at the +# # dashboard (by default is the job_name) +# # JOBs sharing a name are mutually exclusive +# update_every: 1 # the JOB's data collection frequency +# priority: 60000 # the JOB's order on the dashboard +# penalty: yes # the JOB's penalty +# autodetection_retry: 0 # the JOB's re-check interval in seconds +# +# Additionally to the above, sslcheck also supports the following: +# +# host: 'host' # [required] the remote host address in either IPv4, IPv6 or as DNS name +# port: 443 # [optional] the port number to check. Specify an integer, not service name. Default is 443. +# timeout: 3 # [optional] the socket timeout when connecting +# days_until_expiration_warning: 14 # [optional] days before the alarm status is warning. Default is 14. +# days_until_expiration_critical: 7 # [optional] days before the alarm status is critical. Default is 7. +# +# ---------------------------------------------------------------------- +# AUTO-DETECTION JOBS +# only one of them will run (they have the same name) + +# example_org: +# host : 'example.org' +# days_until_expiration_warning: 10 -- cgit v1.2.3