diff --git a/checkman/check_crl b/checkman/check_crl new file mode 100644 index 00000000000..d211b945845 --- /dev/null +++ b/checkman/check_crl @@ -0,0 +1,15 @@ +title: Check CRL expiry +agents: active +catalog: agentless +license: GPL +distribution: check_mk + +item: + The URL of the given CRL + +description: + This check screens the remaining validity of a CRL. + The Check can go into warning or critical if the remaining time + is too short. + The default tresholds are 7 days for warning and + 2 days for criticial. diff --git a/checks/check_crl b/checks/check_crl new file mode 100644 index 00000000000..9956456ccd1 --- /dev/null +++ b/checks/check_crl @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Copyright (C) 2019 tribe29 GmbH - License: GNU General Public License v2 +# This file is part of Checkmk (https://checkmk.com). It is subject to the terms and +# conditions defined in the file COPYING, which is part of this source code package. + +# author: Oguzhan Cicek, OpenSource Security GmbH - oguzhan(at)os-s.de + + +def check_crl_arguments(params): + url = params["url"] + warn = params["time"][0] + crit = params["time"][1] + return f"-u {url} -w {warn} -c {crit}" + + +def check_crl_description(params): + url = params["url"] + return f"CRL: {url}" + + +active_check_info["crl"] = { + "command_line": "check_crl $ARG1$", + "argument_function": check_crl_arguments, + "service_description": check_crl_description, + "has_perfdata": False, +} diff --git a/cmk/gui/plugins/wato/active_checks.py b/cmk/gui/plugins/wato/active_checks.py index 2ea8a7b5469..cda21661b3e 100644 --- a/cmk/gui/plugins/wato/active_checks.py +++ b/cmk/gui/plugins/wato/active_checks.py @@ -2490,3 +2490,33 @@ def _valuespec_active_checks_elasticsearch_query(): name="active_checks:elasticsearch_query", valuespec=_valuespec_active_checks_elasticsearch_query, )) + + +def _valuespec_active_checks_crl() -> Dictionary: + return Dictionary( + title=_("Check CRL expiry"), + help=_("Checks if a Certificate Revocation List is still valid"), + optional_keys=[], + elements=[("time", + Tuple(title=_("Time left"), + help=_("These levels make the check go warning or critical whenever the " + "remaining validity of the monitored CRL is too low."), + elements=[ + Age(title=_("warning at"), + display=["days", "hours", "minutes"], + default_value=604800), + Age(title=_("critical at"), + display=["days", "hours", "minutes"], + default_value=172800) + ])), + ("url", TextAscii(title=_('Certificate Revocation List URL'), + allow_empty=False))]) + + +rulespec_registry.register( + HostRulespec( + group=RulespecGroupActiveChecks, + match_type="all", + name="active_checks:crl", + valuespec=_valuespec_active_checks_crl, + )) diff --git a/omd/packages/nagios/skel/local/lib/nagios/plugins/check_crl b/omd/packages/nagios/skel/local/lib/nagios/plugins/check_crl new file mode 100755 index 00000000000..7449fac7a94 --- /dev/null +++ b/omd/packages/nagios/skel/local/lib/nagios/plugins/check_crl @@ -0,0 +1,136 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Copyright (C) 2013 - Remy van Elst + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# Mark Ruys - 2015-8-27 +# Changelog: - catch openssl parsing errors +# - clean up temporary file on error +# - add support for PEM CRL's +# - fix message when CRL has been expired +# - pretty print duration + +# Jeroen Nijhof +# Changelog: - fixed timezone bug by comparing GMT with GMT +# - changed hours to minutes for even more precision + +# Remy van Elst - raymii.org - 2012 +# 05.11.2012 +# Changelog: - check with hours instead of dates for more precision, +# - URL errors are now also catched as nagios exit code. + +# Michele Baldessari - Leitner Technologies - 2011 +# 23.08.2011 + +# Oguzhan Cicek - OpenSource Security GmbH - oguzhan(at)os-s.de - 2021 +# 05.04.2021 +# Changelog: - added argparse parsing +# - added handling for DER-Certificates + +import time +import datetime +import getopt +import os +import pprint +import subprocess +import sys +import tempfile +import typing +import argparse +import urllib.request, urllib.parse, urllib.error + + +def check_crl(url: str, warn: int, crit: int) -> (int, str): + tmpcrl = tempfile.mktemp(".crl") + #request = urllib.request.urlretrieve(url, tmpcrl) + try: + urllib.request.urlretrieve(url, tmpcrl) + except: + msg = "CRL could not be retrieved: %s" % url + os.remove(tmpcrl) + return 2, msg + + try: + inform = 'DER' + crlfile = open(tmpcrl, "r") + try: + for line in crlfile: + if "BEGIN X509 CRL" in line: + inform = 'PEM' + break + except UnicodeDecodeError: + pass + crlfile.close() + + ret = subprocess.check_output(["/usr/bin/openssl", "crl", "-inform", inform, "-noout", "-nextupdate", "-in", tmpcrl], stderr=subprocess.STDOUT) + except: + msg = "CRL could not be parsed: %s %s" % url + os.remove(tmpcrl) + return 3, msg + + nextupdate = ret.strip().decode('utf-8').split("=") + os.remove(tmpcrl) + eol = time.mktime(time.strptime(nextupdate[1],"%b %d %H:%M:%S %Y GMT")) + today = time.mktime(datetime.datetime.utcnow().timetuple()) + minutes = (eol - today) / 60 + if abs(minutes) < 4 * 60: + expires = minutes + unit = "minutes" + elif abs(minutes) < 2 * 24 * 60: + expires = minutes / 60 + unit = "hours" + else: + expires = minutes / (24 * 60) + unit = "days" + gmtstr = time.asctime(time.localtime(eol)) + if minutes < 0: + msg = "CRL expired %d %s ago (on %s GMT)" % (-expires, unit, gmtstr) + exitcode = 2 + elif minutes <= crit: + msg = "CRL expires in %d %s (on %s GMT)" % (expires, unit, gmtstr) + exitcode = 2 + elif minutes <= warn: + msg = "CRL expires in %d %s (on %s GMT)" % (expires, unit, gmtstr) + exitcode = 1 + else: + msg = "CRL expires in %d %s (on %s GMT)" % (expires, unit, gmtstr) + exitcode = 0 + + return exitcode, msg + +def usage(): + sys.stdout.write("check_crl.py -h|--help -v|--verbose -u|--url= -w|--warning= -c|--critical=") + sys.stdout.write("") + sys.stdout.write("Example, if you want to get a warning if a CRL expires in 8 hours and a critical if it expires in 6 hours:") + sys.stdout.write("./check_crl.py -u \"http://domain.tld/url/crl.crl\" -w 480 -c 360") + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("--url", "-u", required=True, type=str) + parser.add_argument("--warning", "-w", required=True, type=int) + parser.add_argument("--critical", "-c", required=True, type=int) + args = parser.parse_args() + if args.url== "": + usage() + return 3, "invalid parameter" + url = args.url + warning = int(args.warning / 60) + critical = int(args.critical / 60) + return check_crl(url, int(warning), int(critical)) + +if __name__ == "__main__": + exitcode, info = main() + sys.stdout.write('%s\n' % info) + sys.exit(exitcode) diff --git a/tests/unit/checks/test_check_crl.py b/tests/unit/checks/test_check_crl.py new file mode 100644 index 00000000000..8fe5c23f466 --- /dev/null +++ b/tests/unit/checks/test_check_crl.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Copyright (C) 2019 tribe29 GmbH - License: GNU General Public License v2 +# This file is part of Checkmk (https://checkmk.com). It is subject to the terms and +# conditions defined in the file COPYING, which is part of this source code package. + +import pytest # type: ignore[import] +from testlib import ActiveCheck # type: ignore[import] + +pytestmark = pytest.mark.checks + + +@pytest.mark.parametrize("params,expected_args", [ + (("foo", 222, 111, "bar", {}), ["--url=foo", "--warn=222", "--crit=111"]), +]) +def test_check_crl_argument_parsing(params, expected_args): + """Tests if all required arguments are present.""" + active_check = ActiveCheck("check_crl") + assert active_check.run_argument_function(params) == expected_args