From ef3fc41f51af53e4ed4dfd435f5adf4a4b345bed Mon Sep 17 00:00:00 2001 From: "th.l" <thl-cmk@outlook.com> Date: Thu, 2 May 2024 15:54:11 +0200 Subject: [PATCH] update project --- README.md | 1 + agent_based/ssllabs_grade.py | 148 ------- agent_ssllabs.mkp | Bin 6986 -> 0 bytes checkman/agent_ssllabs | 45 -- checks/agent_ssllabs | 32 -- {doc => img}/.gitkeep | 0 {doc => img}/sample.png | Bin {doc => img}/wato-options-agent.png | Bin {doc => img}/wato-options.png | Bin lib/check_mk/special_agent/agent_ssllabs.py | 144 ------ mkp/agent_ssllabs-2.0.2-20240105.mkp | Bin 0 -> 10161 bytes source/agent_based/ssllabs_grade.py | 412 ++++++++++++++++++ .../agents}/special/agent_ssllabs | 2 +- {checkman => source/checkman}/ssllabs_grade | 0 source/checks/agent_ssllabs | 37 ++ .../wato/check_parameters/ssllabs_grade.py | 126 ++++++ .../cmk/special_agents/agent_ssllabs.py | 251 +++++++++++ {packages => source/packages}/agent_ssllabs | 15 +- source/web/plugins/wato/agent_ssllabs.py | 103 +++++ web/plugins/wato/agent_ssllabs.py | 82 ---- web/plugins/wato/ssllabs_grade.py | 76 ---- 21 files changed, 938 insertions(+), 536 deletions(-) delete mode 100644 agent_based/ssllabs_grade.py delete mode 100644 agent_ssllabs.mkp delete mode 100644 checkman/agent_ssllabs delete mode 100644 checks/agent_ssllabs rename {doc => img}/.gitkeep (100%) rename {doc => img}/sample.png (100%) rename {doc => img}/wato-options-agent.png (100%) rename {doc => img}/wato-options.png (100%) delete mode 100755 lib/check_mk/special_agent/agent_ssllabs.py create mode 100644 mkp/agent_ssllabs-2.0.2-20240105.mkp create mode 100644 source/agent_based/ssllabs_grade.py rename {agents => source/agents}/special/agent_ssllabs (90%) rename {checkman => source/checkman}/ssllabs_grade (100%) create mode 100644 source/checks/agent_ssllabs create mode 100644 source/gui/wato/check_parameters/ssllabs_grade.py create mode 100644 source/lib/python3/cmk/special_agents/agent_ssllabs.py rename {packages => source/packages}/agent_ssllabs (82%) create mode 100644 source/web/plugins/wato/agent_ssllabs.py delete mode 100644 web/plugins/wato/agent_ssllabs.py delete mode 100644 web/plugins/wato/ssllabs_grade.py diff --git a/README.md b/README.md index c7d46b7..67d5149 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ +[PACKAGE]: ../../raw/master/mkp/agent_ssllabs-2.0.2-20240105.mkp "agent_ssllabs-2.0.2-20240105.mkp" # Qualys SSL Labs REST API special agent **Note: this package is for CheckMK version 2.x.** diff --git a/agent_based/ssllabs_grade.py b/agent_based/ssllabs_grade.py deleted file mode 100644 index 1a5e11e..0000000 --- a/agent_based/ssllabs_grade.py +++ /dev/null @@ -1,148 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -# -# License: GNU General Public License v2 -# -# 2015 Karsten Schoeke karsten.schoeke@geobasis-bb.de -# -# 2021-05-15: rewritten for CMK 2.0 by thl-cmk[at]outlook[dot]com -# -# -# Example output from agent: -# servername;status;time;agent_state;last_grade_result -# <<<ssllabs_grade:sep(0)>>> -# server1.de;A+;1435565830118;0;A+ -# server2.de;A;1435565830118;0;B -# <<<<>>>> - - -import time, re - -from typing import Dict, NamedTuple, Optional - -from cmk.base.plugins.agent_based.agent_based_api.v1.type_defs import ( - DiscoveryResult, - CheckResult, -) - -from cmk.base.plugins.agent_based.agent_based_api.v1 import ( - register, - Service, - State, - Result, - get_value_store, -) - - -class SSLLabsGrade(NamedTuple): - lastcheck: str - time_diff: int - grade: Optional[str] - agent_state: Optional[int] - status_detail: str - - -def parse_ssllabs_grade(string_table): - ssl_hosts = {} - - for line in string_table: - line = line[0].split(';') - if len(line) == 5: - ssl_host, status_detail, lastcheck, agent_state, grade = line - lastcheck = time.strftime("%d.%m.%Y %H:%M", time.localtime(int(lastcheck[:10]))) - time_diff = int(time.time() - int(line[2][:10])) - ssl_hosts.update({ssl_host: SSLLabsGrade( - lastcheck=lastcheck, - time_diff=time_diff, - grade=grade, - agent_state=int(agent_state), - status_detail=status_detail - )}) - if len(line) == 4: - ssl_host, status_detail, lastcheck, agent_state = line - lastcheck = time.strftime("%d.%m.%Y %H:%M", time.localtime(int(lastcheck[:10]))) - time_diff = int(time.time() - int(line[2][:10])) - ssl_hosts.update({ssl_host: SSLLabsGrade( - lastcheck=lastcheck, - time_diff=time_diff, - grade=None, - agent_state=None, - status_detail=status_detail - )}) - - return ssl_hosts - - -def discovery_ssllabs_grade(section: Dict) -> DiscoveryResult: - for ssl_host in section.keys(): - yield Service(item=ssl_host) - - -def check_ssllabs_grade(item, params, section: Dict[str, SSLLabsGrade]) -> CheckResult: - #value_store = get_value_store() - #print(f'value_store: {value_store}') - #if not value_store[item][0] == 'last_run': - # value_store[item] = ('last_run', {'grade': 'A+'}) - #grade = value_store[item][1].get('grade') - #print(f'value_store: {grade}') - - try: - ssllabsgrade = section.get(item) - except KeyError: - return None - - ok, warn, crit = params["score"] - warn_last_run, crit_last_run = params["age"] - re_ok = re.compile(ok) - re_warn = re.compile(warn) - re_crit = re.compile(crit) - re_error = re.compile('(HTTP|JSON|unknow)') # API Errors - - if ssllabsgrade.agent_state == 0: # test done - if re_crit.match(ssllabsgrade.grade): - state = State.CRIT - elif re_warn.match(ssllabsgrade.grade): - state = State.WARN - elif re_ok.match(ssllabsgrade.grade): - state = State.OK - else: - state = State.UNKNOWN - yield Result(state=state, summary=f'Grade "{ssllabsgrade.status_detail}"') - - if ssllabsgrade.time_diff > crit_last_run: - state = State.CRIT - elif ssllabsgrade.time_diff > warn_last_run: - state = State.WARN - else: - state = State.OK - yield Result(state=state, summary=f'Last check at {ssllabsgrade.lastcheck}') - elif ssllabsgrade.agent_state == 1: # test in progress - state = State.WARN - yield Result(state=state, summary=f'Server check is in progress, status was "{ssllabsgrade.status_detail}"') - elif ssllabsgrade.agent_state == 2: # API error - state = State.CRIT - yield Result(state=state, summary=f'API error, status was "{ssllabsgrade.status_detail}"') - else: # unknown error - state = State.UNKNOWN - yield Result(state=state, - summary=f'Server check status was "{ssllabsgrade.status_detail}", last check at {ssllabsgrade.lastcheck}') - - yield Result(state=State.OK, notice=f'For details go to https://www.ssllabs.com/ssltest/analyze.html?d={item}') - - -register.agent_section( - name="ssllabs_grade", - parse_function=parse_ssllabs_grade, -) - -register.check_plugin( - name='ssllabs_grade', - service_name='SSL Labs %s', - discovery_function=discovery_ssllabs_grade, - check_function=check_ssllabs_grade, - check_default_parameters={ - "score": ("A", "B|C", "D|E|F|M|T"), - "age": (604800, 864000), - }, - check_ruleset_name='ssllabs_grade' -) diff --git a/agent_ssllabs.mkp b/agent_ssllabs.mkp deleted file mode 100644 index eadc333bcafc203183b78d15dee5c9f2b2fbf0f1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6986 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 diff --git a/checkman/agent_ssllabs b/checkman/agent_ssllabs deleted file mode 100644 index 08ef898..0000000 --- a/checkman/agent_ssllabs +++ /dev/null @@ -1,45 +0,0 @@ -title: Dummy check man page - used as template for new check manuals -agents: linux, windows, aix, solaris, hpux, vms, freebsd, snmp -catalog: see modules/catalog.py for possible values -license: GPL -distribution: check_mk -description: - Describe here: (1) what the check actually does, (2) under which - circumstances it goes warning/critical, (3) which devices are supported - by the check, (4) if the check requires a separated plugin or - tool or separate configuration on the target host. - -item: - Describe the syntax and meaning of the check's item here. Provide all - information one needs if coding a manual check with {checks +=} in {main.mk}. - Give an example. If the check uses {None} as sole item, - then leave out this section. - -examples: - # Give examples for configuration in {main.mk} here. If the check has - # configuration variable, then give example for them here. - - # set default levels to 40 and 60 percent: - foo_default_values = (40, 60) - - # another configuration variable here: - inventory_foo_filter = [ "superfoo", "superfoo2" ] - -perfdata: - Describe precisely the number and meaning of performance variables - the check sends. If it outputs no performance data, then leave out this - section. - -inventory: - Describe how the inventory for the check works. Which items - will it find? Describe the influence of check specific - configuration parameters to the inventory. - -[parameters] -foofirst(int): describe the first parameter here (if parameters are grouped - as tuple) -fooother(string): describe another parameter here. - -[configuration] -foo_default_levels(int, int): Describe global configuration variable of - foo here. Important: also tell the user how they are preset. diff --git a/checks/agent_ssllabs b/checks/agent_ssllabs deleted file mode 100644 index 325b21e..0000000 --- a/checks/agent_ssllabs +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -# { -# 'sslhosts': ['ssl.test.com', 'sslserver2.test.com'] -# 'timeout': '180', -# } - -# 'sslhost', timeout - -def agent_ssllabs_arguments(params, _hostname, _ipaddress): - args = [] - - if 'sslhosts' in params: - args += ['--sslhosts', ','.join(params['sslhosts'])] - - if 'timeout' in params: - args += ['--timeout', params['timeout']] - - if 'proxy' in params: - args += ['--proxy', params['proxy']] - - if 'publishresults' in params: - args += ['--publish', params['publishresults']] - - if 'maxage' in params: - args += ['--maxage', params['maxage']] - - return args - - -special_agent_info['ssllabs'] = agent_ssllabs_arguments diff --git a/doc/.gitkeep b/img/.gitkeep similarity index 100% rename from doc/.gitkeep rename to img/.gitkeep diff --git a/doc/sample.png b/img/sample.png similarity index 100% rename from doc/sample.png rename to img/sample.png diff --git a/doc/wato-options-agent.png b/img/wato-options-agent.png similarity index 100% rename from doc/wato-options-agent.png rename to img/wato-options-agent.png diff --git a/doc/wato-options.png b/img/wato-options.png similarity index 100% rename from doc/wato-options.png rename to img/wato-options.png diff --git a/lib/check_mk/special_agent/agent_ssllabs.py b/lib/check_mk/special_agent/agent_ssllabs.py deleted file mode 100755 index a9bee53..0000000 --- a/lib/check_mk/special_agent/agent_ssllabs.py +++ /dev/null @@ -1,144 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -# -# Karsten Schoeke <karsten.schoeke@geobasis-bb.de> -# -# 2021-05-15: rewritten for CMK 2.0 by thl-cmk[at]outlook[dot]com -# changed cache file name form host_address to ssl_host_address -# 2021-05-16: changed arguments to argparse -# added options for publish results and max cache age -# -# check_mk 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 in version 2. check_mk is distributed -# in the hope that it will be useful, but WITHOUT ANY WARRANTY; with- -# out even the implied warranty of MERCHANTABILITY or FITNESS FOR A -# PARTICULAR PURPOSE. See the GNU General Public License for more de- -# ails. You should have received a copy of the GNU General Public -# License along with GNU Make; see the file COPYING. If not, write -# to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, -# Boston, MA 02110-1301 USA. - -import argparse -import json -import os -import requests -import sys -import time -from typing import Optional, Sequence - -from cmk.utils.paths import tmp_dir - - -def parse_arguments(argv: Sequence[str]) -> argparse.Namespace: - ''''Parse arguments needed to construct an URL and for connection conditions''' - parser = argparse.ArgumentParser() - parser.add_argument('--sslhosts', required=True, type=str, help='Comma separated list of FQDNs to test for') - parser.add_argument('--proxy', required=False, help='URL to HTTPS Proxy i.e.: https://192.168.1.1:3128') - parser.add_argument('--timeout', '-t', type=float, default=60, help='API call timeout in seconds', ) - parser.add_argument('--publish', type=str, default='off', help='Publish test results on ssllabs.com', choices=['on', 'off'] ) - parser.add_argument('--maxage', type=int, default=167, help='Maximum report age, in hours, if retrieving from "ssllabs.com" cache', ) - - return parser.parse_args(argv) - - -def connect_ssllabs_api(ssl_host_address: str, host_cache: str, args: argparse.Namespace, ): - server = 'api.ssllabs.com' - uri = 'api/v3/analyze' - maxAge = args.maxage # default 167 (1 week minus 1 hour) - publish = args.publish # default off - fromCache = 'on' - now = time.time() - - # url for request webservice (&startNew={startNew}&all={all}) - url = f'https://{server}/{uri}?host={ssl_host_address}&publish={publish}&fromCache={fromCache}&maxAge={maxAge}' - proxies = {} - if args.proxy is not None: - proxies = {'https': args.proxy.split('/')[-1]} # remove 'https://' from proxy string - - try: - response = requests.get( - url=url, - timeout=args.timeout, - proxies=proxies, - ) - - jsonData = json.loads(response.text) - - except Exception as err: - sys.stdout.write(f'{ssl_host_address} Connection error: {err} on {url}') - # print(f'{ssl_host_address};{err};{now};2') - return - - print(response.text) - - try: - if jsonData['status'] == 'READY': - # if test finish and json data ok --> write data in cache file - with open(host_cache, 'w') as outfile: - json.dump(jsonData, outfile) - - except (ValueError, KeyError, TypeError): - print(f'{ssl_host_address};request JSON format error;{now};2') # ;{grade_cache} - - -def read_cache(host_cache: str): - # read cache file - with open(host_cache) as json_file: - # check if cache file contains valid json data - try: - jsonData = json.load(json_file) - if jsonData['status'] == 'READY': - print(json.dumps(jsonData)) - except (ValueError, KeyError, TypeError): - # print(f'{ssl_host_address};cache JSON format error;{now};2;{grade_cache}') - return - - -def main(argv: Optional[Sequence[str]] = None) -> None: - args = parse_arguments(argv) - - VERSION = '2.0.1' - now = time.time() - cache_dir = tmp_dir + '/agents/agent_ssllabs' - cache_age = args.maxage - ssl_hosts = args.sslhosts.split(',') - - # Output general information about the agent - sys.stdout.write('<<<check_mk>>>\n') - sys.stdout.write('Version: %s\n' % VERSION) - sys.stdout.write('AgentOS: linux\n') - - # create cache directory, if not exists - if not os.path.exists(cache_dir): - os.makedirs(cache_dir) - - print('<<<ssllabs_grade:sep(0)>>>') - - for ssl_host_address in ssl_hosts: - # changed cache file form host_address to ssl_host_address - host_cache = '%s/%s' % (cache_dir, ssl_host_address) - - # check if cache file exists and is not older as cache_age - if not os.path.exists(host_cache): - connect_ssllabs_api( - ssl_host_address=ssl_host_address, - host_cache=host_cache, - args=args) - else: - json_cache_file_age = os.path.getmtime(host_cache) - cache_age_sec = now - json_cache_file_age - if cache_age_sec < cache_age: - read_cache( - host_cache=host_cache, - ) - else: - connect_ssllabs_api( - ssl_host_address=ssl_host_address, - host_cache=host_cache, - args=args - ) - - -if __name__ == '__main__': - main() diff --git a/mkp/agent_ssllabs-2.0.2-20240105.mkp b/mkp/agent_ssllabs-2.0.2-20240105.mkp new file mode 100644 index 0000000000000000000000000000000000000000..3f792b1793e82d82e5f993b29dcf8cf559bd0a40 GIT binary patch literal 10161 zcmai(V{{#Ex9x+*Y0}uXZR|K{lE${(*p1cLwzackqp_Vdwrz9Y|NEYCaqbysoNw#% zoX?tTj^9HP1rGs{#bE&jz7YEG*lSLBYxqRWzEhC}N%(5jFL|r$ES+sUyDyzJ<QzT_ z0)%CQ%oZ^#;*-0om!4X-kcDLe3Tanv_^l(Y8?YmKA#jmlp^bR2gT6nXGKeD?m93fk zL*DwgbadrtUR)}4bgW&vt-R&rY+N$Tc|SnahGXyqksE$k_H`XFCjwdPUkALeq@2W{ zow?f)Pq>;+u}osD1tUAXc0(Zxvkkr2@bqVAxiUfn(?XQx6B%R!B1XxncR~j06s0(2 zd9gbY!_~?7Y9Yowem~M?uK-|RPC{8wtJjTWKO9$C<n7~QH|p1v>TWg$icHxND4efv zg#J+X3zU~_;Z9bwtnahyQTpK;7)JN+qn|2?RZDh~T1+|>h(;F+JjqDxdBG!?NiH^b zC7EyJ&5<TBR=ygDur#yS0vjIBe{K^ur$nC&=s&Z&e2+!pQ$c#mZ8yaig`qaquI9E) z)j|1z&XPW4o?@F2u$fqw)fymNmVl%pQrXp&i0yVE#C{{3RLAqQA#l58nHM{1j43Mo z4V77;N4Y#eUuuu5hz&6~AQoK=ph}%eh36h2KwS2-!|4O&s&^nu`CDLOwm?1f7y7_5 zw)^!)O2s(!X(ao6=)U7vPbcJq4FfOX111ZW-Fj?y^SY=J)FW0*I@3AWP=_Q{{8kS7 zR=z+nxmtmJCg;RBQ=asWdG>Se+L<zh!GxxO*^5^{d1__eJxp2WW{F=WL+-df`(aXS zuK1<Z6D?n<yc@hQH)jZ;m9$wwn47SE$FW4)7?mYJ%8cO;3zt(WO{7wd%uiR|jO}g| zU@=FB`{AbvqIi9VL^8J}^KpCmD3<a>Ir+Q^DI7l<TmpxyDSy}zQbDzeC4O&h{gKC_ zxju-0IF9c$J63SX!5KZ#2PID(0k>Pe^IMWCa|y4$C-Mollxb3n=cN?sx0#XFw(8G6 zJZ5c29;`TD)_xpUlv;)Nua&lx1q?7@QGQ9%mQ3XkA}vx@s$09qHkRF`5|%?}$*4D< zG#(Z6{sx)iQZ{St)(&z9mX)q@usD*x-t|RkJweiwqORdjtrO{s-)O&f&M$n!t`oSg zXn;pYl|1J~J{CWC;sNg=8-qDef(@rX&+hrx_}&~mEPo9z`?HvUf7X$;EYRr6iQ5Ae znjOnc3c1zTMA?wpKE8()SipQ@mOpN=upM$Yt#fnDFD{!A;2NEQKo6x0MHl7HMb_d- z)*!xntu;$=@v?zRIyPhOMon&Zg+(25UPK8LLde&UV#-pNufw}8F`3m<nsigOpu+M7 zwEQnfTff!*xb`!)l@Ji*N5+KEb}vhb6;4P%**xh+MtqsZ(e=VGJ*#JKl$w~2IwudK zj43c@*JdxP%f^`?POZ;a>8}p}_16pbEgHY{^z6KhA!mcefB*Jp_^5F$iow_!p9Tv( z>Slr0KpXbc6JQp|7qZ)wx9+aBjRXH*9fjZ)j}B1mIr!0@;Cgi}=Q3*5Kco|%S<qiE zsD=4s3-ixQ%^`VgrZ|h$+;$jziPlRP=jSp6&=^@&KaVl-`ySVy3Lj`Xl1?-j)iT9R zBCyEjEW`w6UN&RcAD%?({nFs_fX?aCa=(Z!SQ$XJUjUU^-Uyv6>6+$_Ai@P}Ce#!G z4z+7fFGBO|DL4PG>szK(oNIQL!Q!#6(A7X&ED>{hlu1g96hzguGM6<{p3p?wPrKpw zml{=ksIM&c$iE-cp$60E8i(#VTBz)VGxQry+?Z)d8A(nz>>Isf+-p#|SAXtPX)O<F zFexb+!(xx7Q<A-Omy!ep&WC7&=xA=Jo15s4=p_3!Ysu>bvfixHrDs|cg4`uZ3=G53 zFH;hmtNVCG*NiK~n@{Oi1*5-#);1OmrH#>J`~JSpaCot$;mPE*`pGv**z1}O&G&!s zfS1Q;q(<9@v8l^hy}P>HYvxz%t+fzhTx4=CHo7)#zed3JP`r99M6NaKdOOulK-%`X zs8?y&ULAo5OKt@3Lz<G#KyT|D4qzXDL0H?ICg(W3z1m2tCa!wzEbA}L+jUwolfU{k zS_B9$R%+3$R=)RVAt8}SxWGUT&BiGa!+$YltWvs+bZWbS;eGR`7v+r;@gV*YtdtG{ z#@DPrOY<N)7O4JRiWDLABfdF^h+<EYh%%9g2UZyAbu7?J;ISB83<#XL)wFa$A!s0| zMl4Pl3(`)f?LLWj*~Qa}gAc-nXX{*?Ur#npc2-t0MeCt4em*@IT=CF>iCPXGe_bed zY@I%B1Ktfik`f$${j#kw=}h?8P$*mS4LV;U*$CXo-PjB}a9+FY{Pg~C$Ba*gq~hD0 z8F=u~sJZKJxGUQ8n8HJjFH#&vK~5$S&du(h{t8b^mDg_v)dipAH_;65m}NfX(S52o zOL>js*B>(RL?yNbckuPabd4z1aT?MU_T_l$NXaTEdo~Y;6IVGl=1rVpE2e-Mox^vK z%~4#FdWTp<em%X*by+p%VzjfsXiK@-cff_pLo0K_;|~N!AdQ6DpOEUzqDh#2^3(7# z{*1QKW7$r#ZWvJ|fHqf;d@qjMCw_eJ_2Q004{pXb%P7h>L{2R`<NoDSdi9r!R+s5N z)@w>mr_m>r=F%xdKM3=Ea_T=={WNZdqNI&@B|UD<FRWos2g|9l1LK1c7>Pwv2Ndly zU+%+R+59nPD}Hw{+|uEWZ>eVT+R;o!V9c;orD9nT$w4Tn(-1!l`<m(GxcL|&j=^-E zCOJMU#+QuaTl@Ux-A5AWJdScsl*HUVqd)94Du!6dVwL5_>>cfAIL{9`pi;6O^P}U! z3o8JpSCaZE>*MAa>foh&<`yx_99F2m*z&^yNmapxy%5w0I>1`Z{qysFI4uOiNbSV5 z(@CN<;LM*nRmKQgIXE6b+I)JfvxS}j6IBC3<ufR0yYs-)XIG~$aAm9wYhK;Z9KGh} z`HWqnjzU^yTb!L5#(qd+c6{Yz3FQyen(ct<5-!TH2hE{ZyilxTtz;fD7p=wFQ}{5l zK(?dn&G<x>vogy}F>;8y${ZC-U+(z5Sy@XlIWnF3Dd`22Np=et6Kw@hN6N(MxmIG{ zgHLJ<2}h0S5)4(EHkY3)e8n863|Cl5x&&2*o3U!WZK?_aBwY-21u^4GDP>afU&bUK z)l|9^6Dr+T`=7&zbW=}2&o1l7xWT!CvT9O|HML*9s0>8HVvdvju!JNpta$Zkj1LgH zOJ*lC87|G~JF?<<eRwFkHLNbAMImy}=P8vC&+UU~!xVT}Z5rAN+C`izWMtv%>rt1H zQ4->TB;R<Ey|f`qJ3}qLM_rJYJ+ttr{%|@M7}@)ZiC$>F5v!P*2;ia3FG_}lRHSMx zB;Z)Rojet^mbQgXWiit{+SO0O*--Hzqu17*JGeZxtKc+m+b5nqm2b~Ccl(Py1#CDv zV55lFRj=8b@_y*9g!Ss3(wR;~(U#@IxGk)?w->I6PI7idz76g>Ok`>KW@D$TCfv&_ z|D9c6Lbe4zzcy-owWM@p3I)~CO&YfSq0FjQuKDXJp^wj1Hi@sG>!u(7N2@;yUX=rQ z_St>$+1=;!;_1Ty@YpTX(4CXu0A4~5qXw1~U2thG{8Y2h8M{?KV~@D3cbZUmf*Z4u zFLIh~4*@T=3L9hS`+-uI;Pnb*PzFU_iT(8))g3Fi^jR|>P)ylB#b6s`1M9NbG7c82 zHsF53kd0$>;*FQ6=K_4R*V2591;;@Bo>8^@iS%bK2^>E!iB`9qL)AlPIS7giscpy> zXJc)=)p%6KJFm>>#~w?X*{_Z3U}L^l;?VKwGD${OC5_o>FtWQ#GKY4hHTf{O5*(j3 ziL+SVrS9KiwS+SHgfd3=n4I6mgtphqS0uS+s7j;HbSdCId-#NzGpWE(1^u=Cc4r42 zq^dCdm69|#N@*1jk$#&z871ElXr5?qsivJ#I5n0X&7Tg1);!k&=sN0OoIV&Bs~UgX z`#f=SvadR7UxzD`J|g#!*ChLJeE)o&j~niEGT{##h;WNL_b0vb{Jq{#NX$sEFxRP* z)Ttyu%|q>VrKar!{aAB897tQHq13V9wKr&YDCMDjNTnSKoS3AT0^MwEU$SQfa4u-l zjO9ieQPRK&(&A$=I(e}ctIk#`_ud`A25%QA2Gg@NO2I<UO>t1TsHps&2xxieGf`Zm zB4L3ZuN-e8qCmK<`I^_OyKY}yA}R8sUdxZ6ka-VBFP%Q8xHjZkS^ECAs%J3c<_vGv z#ZxCNV(py0Whn{J(k@A+*RvWl6l*EI7?YBU4F;(=nYf`R_E|QOy6(TW=mSes`0HZD z$@!TBnDgpN2wXqOadjo@I}(_~urX!iWQ!!GPuDGo9qsZhR7&k=1UXZlRvrg}FZFBg z-<^jkGBWK)(;{8{N1gmb-<fyVn$&3jhL+3E+~n<=F@Ls2R_7NZN4vPUCk~f`to|ah zDN0J0EBbfU;vfiFNPQpW6649q*taBl20lovY7wqzXy=(V`FeYl>loH$j;VnyxK;eE zTU4{4Q>9-xvb?JT)*JJ8Z$}|BITz^>BEMCTb`V!Qs?p0;Uj5>swl+zq|D`RdJ|^0A zF+B{;yy$DzGKQoM<+Aw9fnYopW<U`(?NEUeQ5Xw?FUlnyK%)~9rF7GgBXS-<0Md3F z&g%6e(PbN;DXO6z=!#8KWfI&xj{1Nw+10ns>p!kjr55mQxz)D&B4?bSam~CkXad~Y z3Y;Qs8ACyq?{cW48*l1Oo+B>WYui1(@O7?Cx`~)SgK3ZEVcsM{fw-J>d=*HH&ng=< z-ilX<JmJY0Y=hS#cRB#8>PR;hz=R@`W}UL(C^x35EaPFS<w^9>@Y<A#a2Z;>;zHEi zeC6N0W^>Sb>JEctor>XDM@TMEB;^9?J)v{+q**5@+Orbv_Q&fFIhmnLeZ7ig9p!BI zrsY%u8BN&u1=!I=<&#tn>%tBNV6)v}0IjlrPnv9ath9}s%(_rd24m1{0Ju59M$jYJ zn+^x|w4eQy$M(Ed$Gpp&?%_xC<zm|E0cGk%yZ3CN_1BcXwimh(&adr3BKDM>e8LUf zsv?<FmRC7W0nIW<3%<>Eu#x$(&d^E=QVdACr`zj==6pg-_sFP9$LZ@w`@nXw#s+a* zy0s=a_F@b@v8s5osha-zdBrltBiuLS@yZ>-ieu;MXKss3p3!#qm;D+abQS{{(2h&< ze&85!Eh@TKL`WW&Y;{~9gz4ky>34U478Qfml+7StWAu)>is}rs!8Wb*32g_JNvNMo z`CiIP`xNT6$YPb4Fxy)HYoxQ4>k2v~HOo|(d-Af3M%Wf=FPE!g4|VOm832HrLDclp zJf_PEph4QAF}>^;DJ4@_xQ#+`B~97d*cryYCXjPz?`iDtSAL_<c=5+Wy_}Vom7CHg z4XRhy?RK9%dsOX-uHyt#G;jbALjljz{Y4eotTj>5h{Q*LK-p7JffD)^v0}_5`Myt# zf-Z4`&G$;@Ddh693&}DpQ*SH%uGER9VUF$i7nu0JANvM)?v*sXdvql5zXQovy?BCY z+}6iLb^o%xxPT2;0S?LzY~B){OY1d^a-gI?MAzvDTX0ul^)3!T4LZBsN>IoT^#SA4 zQYGCk!yEaMoI0u&UTOV0OcoA8O>|*?>vK7r1uVIjvUn`ptG{jyt~Vd!(^QM<(!{=p zGJx{Jo9qr5+o_#pC;1CLd<|!)mM~B|+;Ib22J4ZsGo?`cm0T3l#fN&wnIb5!{RyZp z&s5$h1#HpI)1e_Pc+uOcp|5H*n7j3h#Wy$Dd>V&IUKDGe6P9LTPrEH~6B7+|=8nrN z2CA{Rl}xUjl~wfQK{eOVc0;4(WrT11gB46I?c3sUed-4FXB`3IFGM(=rFGL%D!b&I zuy5^>1vybd8j1j+9wD+=C-=v|?MsRmZtRl`pr+0z;uXuoS{i1H5r+C8Mx9kMD{=6f z?4uVpv9kTccQ;#-?vB^BH0bxc!||Q87DHh_zAbdyS-2FF#%pVNjI#2<o?=Z{WL#FG zo&ZSb{}_6%h&>0!Yp`DvM7`dJ6+GxZO>I3qJk|#MeGq7o$6v8t{acoF(ZF>J_CKRO zubKq}Is|HTF6`lE<ll#J0)~hDDdBeBnQu!q5h{rP%9Jf^QheY4YHb<Unf0}sCMIT2 z937j3kOFZp_L>kiwjaSGGFv~XQt()_P-4N(d7$(AE)3FP&?f}$6kEY8qZFz+DL-Ox z-7q=b4$p%@fF+LLF$KfP_WURyHg;=)jr-wPmWj7_bzp}aU)heAirlV2-#C!5>$}vK z>5Inj-T}E0(9xTn;Tv~!^_dwVtVR%7Y<_VEKe-X_u$IOlH)1dy-Y+yxis(x%G;3P& zoYrML;vHh(-k`NaNyZsW%B>*#R7qwL6qD1kMfqO7ZOZ}g^QKcs)Hh)@TMG-$|3|m~ zBjA4-wp&ndZy#z-AYZM()9%0q(|3U3m&R%1Za*;2tG~PV&mHi}X;b^!3^*revH1q< zgCNs6P<8^dI*{{gx0L<!#x=A9qZB?RA*<Do_X7H`Q~O3$b2<q%rS3NmEHci!oR=4# zo4Nd+^8VzYKJmm+lHrsP5;O}!QTo=%By>;+<7cEsC@#yz;b0M}Rac+bY+;glw5G){ z1y9ci@~weG1`a@a>GE|5NWk#LRzxyC1I8df0?mAxC~LRhWmhV>QDC25Pr^O4C<+Ln z6^?JD^bg(&H>g@pW=;2;mkjY}i6@w*YOzO0f8Qr$|JD078Kb^DHxG(_b<(7#71tzb zsqyvEf0MAmilivK9f3$(;WcApE~d_?5se-YXSvj=_Sa!|bTw{w6noQr>{<(1UgGrr zk^dTrjhH?q>b&S}T=ubp|52(*c!lOVO)@L|as-s7=k$Ad=BZ!zJiyi)?g144i<~?n z2`;GZ8Xv(3#g)mRpx4CN1hxJb6D~LG-50&s&Tp;w#Et{<3;*A1|1W0!$09I7depsM z41&+yqQOUUJxkGV+B4wL&%BNfq8)JC#>s_y2H3L2{2wEshzY<LD%>$wqw!BTBCMxL z`(qB#7E3tx&Unn|$e~_rC`seWywJrY3V?5};3P5tOuaAGRMHGe5#4sD6GkL2krHl< zM$7C<aGC=fK27J};^o{eG^aDVh@`<<SS-l(0)lzA*ThPNf_Ev(%hi|7nB5TAeLGjI zIa}H4mj<G{qizLjc+SpNHo{d?#D7leJpKIiJGYZAg63*APsti%hp+*#WKU{-5IM(o zo<L_sG5|Ln!_V8r$t$}x3$u3i=x&$2YlO3_nSF?1ztPs)JgDguolojQQDn-76QZNH z?rfk^_Yok@GGeuq40RL9blmCVwEG|x`hA@rtsC?9j`rOrjb_TgULw<U5#bG(C^;Hk zlO4V>+%!<7cWWVeBdC?0t4rs8=r^QQ^|Cu~7841%4!gS;F+w#W&8eO5!kDi?kHvHi z+=EHF7Bg@**AE)0is#=`vi-lA^xwFI%X-}raMJhIK9yIKyANJj*<kz3V}Zo2jyJ5k zFoOhV>P(3K@_VxTFx&>O*gx=h)Pn^;Kcm5IYqlQ$_VubC%)wGqJl5xU>zTDa-0*nz z7C;Wjf4z_bE?GUjQWmx(q!~HkqTg@t{hF)O#hq_V*3O3Pk^*kfa2)4H!2KA5cRimD zDDGqIghVzP26KHl?!W%nBH%XnT%?wV_%RjOs$GukEO|>EI}7?|9lIf5rR|L*q(Ecv z=0@s}?QvgBuw<y@s+D}AemL_?9m6n2k@Dw3c>ZP!Ee${dgOD>94#=K6**R)^ef!-5 z{T-|I=-#ECBu-FK&_q!A^Nu2p2MOIbHGp-2p-FELhQQ7&xJyP5un6=D$mUCk=F@+! zq62u3E+~sxcE-zex{>n_dbSoyqQB^4G9U8iJf~CRPGSeon53iw9f$mB4l;_qd#Urn zB3aHvhpL~d9`5k9%5=#B8Ha3yd=Kp^G&uQ;3j*L27G9729xbBq&P<5o(_%298*LX( zy-OKRr`b@$MsrSH9HU?Cvx~o^o2|%EBA#Xi9lzM<w2*9sOTM{b<WrbFIun28cVoYU z`s~PIDT!Z&j0T{FNszT&usf%59sE%L(z%h`E0|lW=ukc_bty$CtW9|>P#9fPNR=;` ze+4A9y?6ORF&MRt7>{+&`^Od<*q&X@bu_SWcJm$68tVaaNye0aTXOj>T%mKC;oAdB z-6II9vrx=W`Jp7-H;4QEV!Su1ksRxyW<Hv;{hm5z^lK{VKE3>8rs@=fx@zPQd6=qe z6RH^5MT<SMsS#RqnhsF&PnStZ&TPonYVAMqjbHAo<1CnMR;xHufBQoiCGn6)w&Em_ zJSoOKD3>+tjK$>WGTYv8)IkAONJY|$UGp+9l!mZpaKtG$p<-z#hc#$sgM=f$8k`60 zNN5LmF8M1+g`+a6of-xb8Th1QDfN7->MP3V8xh#sZu<fa9H4=wC9G_HIDAGv!+qrm zXc<IXax8oJa#`-qJ!Nk`Xp8)M?<e%j3Nv(e$z*2d<fLqV5_jH@dPo8@=7+^bF_vf0 zU(Y~}xIQ{R!N;a_7uWObY(H-gZ%_Bf`ud`jclbcO`2m<;1_#p3BO`S|OV5Idz&LZ_ z9R1K1=Ma}M=jFW=l-e)U^__y6iL8MSqdoPFne|&~E5NG!2Go7|Hl*MURx+=8e`m^* zy?SHo1LHdqqbvJb?@`a5C4W`Da^7dQ-JZEcBH2m2DWmz4n27^XYZMFI=Sw?Y_qUZq zCXNXwzQ84u@D9MfI-c{7xS{qwo%950O$BG3-($N(Sh7S&i^{<dQ9vKfk<3&5#ItC% z&wTO8q5A(_S^iTz__<wo?KsSVUCRC)&msMWE}R|qHVON?7Q*GbF8BTk;3NB*uKu^~ zmW`DYSA!;gkbRl$zmohj_#M#w(nKVpjXP&}2~inzKQy3wPM1L{a6N%?GMSDMLeUq# zJDY)gO1D+fw4y91U>+_r0gdWn1UZ?4gc%<<aYkm*gp<G;f!q+G*)qL8U1*Un=j>OE zmeWe%w;yE}vEutAs6`dXJ&l7<;cS@|5?gRLk_UdbJZXI0@Yw2&>Yk?!+kG41><F$8 z%K+p&L|nl{O;>f3B~cT6q)!1s_%x1W_--|v_DFb*|N16iZ&lZvy)Gu6Y2Wzy;f6`Q zLwlb9B^#PM8uh{&_&4HgSWRxg<w`OdPZ9NL>)n(g50~m_Gg{317Y?VnL(RsSYqKuC zhS(mVisdhlnkOCcLP&T4tcm_z{>SExxhR4_t+|o^goQ<vNI%GFNS|~!ee$C44mWMr z^nbNjdg%O&39}*+P!w|4o@_bnG6w#GKp)TRcpUzPh2#22^NYSn=_%U}wZ<u-%b@O& zxD9*2gv&#HGX^UHRi-J@QDaL*ac6I2<rg3;^J&Ztf%c-lhfs~tR7700!#bdY`x|nT zO^f)?_N6uEzI@>v*=%>=7f~-KIzPbQ@qGhjeHNOVN=@mu^((t8_l}w;@9r>}ql|I< za#^q8pKTuozy4mfyb3*|f|vBM^UV{wySkb***<5T|7iRrp}NRId>>-(4qQH>^lzCz zaf?bA3jr5=9;2K;o<IJhgZ*00l3~qOEL0+UKIJgMrFaP66zcR;T<jK~C96oRFp&VO zlHaqZGYWu*G(Xt~1>?(IU6AH+e`L}_^uv;dFA+}^C&GKzegorF`MWKmHQR^-O}ysK zI+5~Co#gRbvM=!4+9m79xBckP&ip`$S4+auy!hyXFe8P5^OFAD(*e{Cr^iI0g;D=J zUw%LFS?$+vLeP8_ZAct#Jjs6*wi9S1w8*6tap+Vtj}hEYc{6tjuf5<6=%@xfti7|N z*+lsNA{p?%M^G5Ix<m~6bsXb)pKUuFaA5i(3<1RIUc`|1ZBv~j<6ebax{Qd>j28;- zRHNBR;bX|h^nQeP<c3KVMbnlAt;_3XO&m@wN71KrljIg^B|_GvGg+w9rzBhjEwqvR z*t4OB+)ctlo9t8+nXSiRG!xLe`946>MwihgQ#5N?Bk}AxBQ^69@oWPNvOXQ?{9U>0 zke4r)PRoCWokgYz$_rZH-c<}#XP8PIEo3a!U|<kI-+M6YRA+_=Fy#4gh1U}yU>U{1 z#{DCOzr{v<d+PA>r#6k9qq&r~$Vs~Y7K{eEnSQhg#7ZLK4@b^P>59L*({;lV_%iAt z!G!l5uKI1HYDAWVv4mx%W>7ASVb&>M(azuQ7+g_9jlZL8cBi4!%X?SBRkwXw?!2-Z z%tT!VUuX4KB7qejKLey9cy6RxxA!6^RFFbTq;=XRY}q^#AY+YDEx0a+zNg!gUQ^$l znWB_0LPpc!G?MXu>0^h+MsW(tV=g!&)BVhsjB#rz&O4ySO5i~&PVpSKl<%@TC!^hY zySq!`aPGSlzP7Wm1a3bX4BKCIHm|vNbZ=aC`l%7sM`2gNcGyGm^$f|rX3QIvrPN?N z4vjqc@^#LDu=WW;sQn1x=8(`;rpiimcFdp<YI7f@IEnSNj02hmTGC!#v%@8+&S2Z> z_n8aqR1sZwi%wHE%6m>>UTJ`uzbttSJKw*>3Ro0TE+tTC?vMwa3}tN(dn8ED{?54~ zwxh#Z+m+iYe4TAO`mKt~E_>jI=!f_y%}UinIPquvG`Mdh5rII@hp`v+CkeE^ozs%h z;3mu(|F<`s)}nkn<Jw}j1-VrpY3Q@;gKRBl9@7UDDIwjattjn1NPfeD8|=m&WMq{K zU4n9z(aZQMi60-NDz*ITO?u<9Um4X*+?j^ffrl;9%Vw%a9kf*pgGiK%ma=UM>3%~m zlE=2yrQJK=Qm)*8>W#^WCk<k<Kf&>G^*);4RK9$HR5Se{qZgVNImoMF!?kzp&l^rt zE$!wHyJ3sVge>NY&yrGwW=}Nuzssp{{**b)hZu8;tIt#{70sheB}>m6d*oprv?p9{ z3aMuMMPiV!to;LlJum-Y-rg9Qncs#g(GKwCxR&SC%qG!+Kk-n6Zv@nq>jyHt*zPV3 zB%_$V<pgXZhD6dDqunSngtEa+;>&KmpOR>mntBVgrgam4La5`f_XAM5daVr!@-XDw z4~4FZejf_m4+Yb=`->_=X7cgPIewDz^0eXr?$yjTA#d#+^KO)-1Buu3#M+O)Q{52A zr(PJnFSt0T`e70htsiM+mnH9^v%iQES@BMKVLDK?`^PVBI__*IY0o}z%Mwp10c-Xl z#}}RwD;FAVaAF=0pfxB9pFjA&uQ)jU7#|O~8F@s_ki^|EKY-XeBD0r3ovB^|TnAJW z_4vQ0;hFRi)Q*M25s9n$l75~>Xx3uF4RW^{zT(ckLhhIJHd7aXFpxaFRL`Va5>#T+ z-%4-Ee(h%;Atk-rd`ZJZ1ZKcTio#BW(Ez&}VUXK3)3tm}4vu%KCIEFNku|cmomq6a z9NKL7Yj+1@_O-JH)r%^EvjOWtOiKY}T;$R&>J%ZJrT2rA+S&3k$Rb&_Kegm)P?<@9 zE73}OQh$O;9@OTCc2$3LRMIg?$pegR;`l;s?7iu7l{l#>i2jsAmYSVyovLhT(N)=S z{jhgtRFVF=n7WkBX3j>kS7!2rttw?gG**=<EIR%wn&q4VYhn4DZKAqW5M(h=ZA-Ci z&}qc#l~3f%#26BS6%-{Wld1J&Qt~px$e-DmObElVkvDRdV-GB0p`=oI6|2l`S5HzM zq47gx!=llv6xv0Z<yEL22po(|$@4R*^Je3#ON}X9SWfku=uJ!M9P&Y+iPZ}*#6;D) zOfNa5+SS$4+8IbuOI8(LI$z;>@9j9&l85+XZ*r{QIZ14rVRW5!yxiDliej~w0K%W6 z>Zpz{HipQ_O#P~A-J4w@FSWbYAK^VQsN1*LYEp{etJGUAuA;0Y{W^EN{iTvi3M#a= zE;{%%Khi%NZ>{mgMM`%Gsgk~zVG9xNS%hrEFq3vUz~#sVKN?8KBx(Lnb1$DD&uYHb z{S<6~lS6D`)ioB6RCcnft58$i>|B`3HEL&`EWWxjdq;aMqe@0q<!Mhu!yWIiSXeiZ z?-Fs_DTF-Tt(_(#`uzh1Mb3#(&feNM;jjVlZ`^e40M&BOa|&+zR{<LUtA71>vLnum zy8+u>oHuv$fg2V!dOsbW+}D2Yfvw*GBq!huFFzXs+wKUYZ63t+HYd_aw*va^-zCY& z6ZPz@<`>_4`I?Nf+pMxk0&dGj@zzpe(oDTlVb>?Tn2s+|bmH;8!SJBq%!cRY6T}Ea z0dhuLGh{_@Vsii50Q)U876&A&bWU=mgb#BT&x5#Hu%~}t0q7F!Qg8Z4f)Q&$rGak2 z!#fm0>^kjJj~~~I1r1)#X#le+jknG2<NPIh8RHj1;&|GlQ8hJO`*P7H^7StcII|d{ zIgxvCIwS)t?Yz5vO|C<*S?6(Hq(azjF*KXr`2yGoPYhK<T)5`C+<Z`zNk9-NoAfl) zgpka={A%#p<B)1ywQz&HkV?cs?6MEWrDz*24k6kUM*Om}O%(niYFVTn^dO3A+lHgg zAe2;tDMiJUKv2h_I0X~BTGSR74e!ZW8r1LW{7Z6{&x~95%dux5+$!!yI3LaO7mm0F zCEYcq3<nHhMmXp-X7pUfzc!-ef9XYqWeh`B`8AMmkcpV0IMn-WoN5O`)Ut0}SYp{r zqx8t(akpruN;w=hw+U<wd9{--g)6yiyC%X_#iV%w`&o_b;o8_E(Jz|GoX-*->nwJq z@Vc<eWqk;Hi+8^4#kYE`*+L9vw+<c^YSt*k(cG8EmG|?3vd|vKd$cg$_en@uODRIt zbwtVpJNMM2Nq6ghHwJ{9GyG*7s}q>oVxX-_bGbaJul_yAH;B>LC;i0XcNahO0kikz zjPYH8c#C9u=dxZ0>q-$ldOK`mh=>#+ceNolZDY5W^=Sr8PLoglkqLoP?7*fxVAbgB zrcj04s^Ws6l2OGfUF2rklUtJLxl*nTpJ9<+nWG_pwhbxH>-bRS!kAqqztv9%S4nD4 z$e}xD2{V4b*iiwxBbh|im5lBfc<u7+pJcsSF~a4m9SpFt^GHMY4ZbN8?^skIUYU78 zsElq!=C`mZ&dCB2lq?P$Xp&Ndp5P%~!oirVpTD>@=pVzMBPze^GerezndMq^F^SRJ m3|SlInps%MHukwugAn+w{zoYC{<|Lxjn>_P3}FQc@xK5KaqDXU literal 0 HcmV?d00001 diff --git a/source/agent_based/ssllabs_grade.py b/source/agent_based/ssllabs_grade.py new file mode 100644 index 0000000..f298545 --- /dev/null +++ b/source/agent_based/ssllabs_grade.py @@ -0,0 +1,412 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# License: GNU General Public License v2 +# +# +# Author: thl-cmk[at]outlook[dot]com +# URL : https://thl-cmk.hopto.org +# Date : 2024-04-29 +# File : ssllabs_grade.py (check plugin) + +# based on the ssllabs plugin from Karsten Schoeke karsten.schoeke@geobasis-bb.de +# see https://exchange.checkmk.com/p/ssllabs + +# 2021-05-15: rewritten for CMK 2.0 by thl-cmk[at]outlook[dot]com +# moved to ~/local/lib/check_mk/base/plugins/agent_based + + +# sample string_table: +# [ +# { +# "host": "thl-cmk.hopto.org", +# "port": 443, +# "protocol": "http", +# "isPublic": false, +# "status": "READY", +# "startTime": 1714559152230, +# "testTime": 1714559237958, +# "engineVersion": "2.3.0", +# "criteriaVersion": "2009q", +# "endpoints": [ +# { +# "ipAddress": "91.4.75.201", +# "serverName": "p5b044bc9.dip0.t-ipconnect.de", +# "statusMessage": "Ready", +# "grade": "A+", +# "gradeTrustIgnored": "A+", +# "hasWarnings": false, +# "isExceptional": true, +# "progress": 100, +# "duration": 85530, +# "delegation": 1 +# } +# ] +# }, +# { +# "host": "checkmk.com", +# "port": 443, +# "protocol": "http", +# "isPublic": false, +# "status": "IN_PROGRESS", +# "startTime": 1714563744895, +# "engineVersion": "2.3.0", +# "criteriaVersion": "2009q", +# "endpoints": [ +# { +# "ipAddress": "2a0a:51c1:0:5:0:0:0:4", +# "serverName": "www.checkmk.com", +# "statusMessage": "Ready", +# "grade": "A+", +# "gradeTrustIgnored": "A+", +# "hasWarnings": false, +# "isExceptional": true, +# "progress": 100, +# "duration": 72254, +# "delegation": 1 +# }, +# { +# "ipAddress": "45.133.11.28", +# "serverName": "www.checkmk.com", +# "statusMessage": "In progress", +# "statusDetails": "TESTING_SESSION_RESUMPTION", +# "statusDetailsMessage": "Testing session resumption", "delegation": 1 +# } +# ] +# } +# ] +# + + +from collections.abc import Mapping, Sequence +from dataclasses import dataclass +from json import loads as json_loads, JSONDecodeError +from typing import Tuple +from re import compile as re_compile, match as re_match +from time import localtime, time as now_time, strftime + +from cmk.base.plugins.agent_based.agent_based_api.v1.type_defs import ( + CheckResult, + DiscoveryResult, +) + +from cmk.base.plugins.agent_based.agent_based_api.v1 import ( + Result, + Service, + State, + check_levels, + register, + render, + get_value_store, +) + + +def get_str(field: str, data: Mapping[str: object]) -> str | None: + return str(data[field]) if data.get(field) is not None else None + + +def get_bool(field: str, data: Mapping[str: object]) -> bool | None: + return bool(data[field]) if data.get(field) is not None else None + + +def get_int(field: str, data: Mapping[str: object]) -> bool | None: + return int(data[field]) if data.get(field) is not None else None + + +@dataclass(frozen=True) +class SSLLabsEndpoint: + ip_address: str | None + server_name: str | None + status_message: str | None + grade: str | None + grade_trust_ignored: str | None + has_warnings: bool | None + is_exceptional: bool | None + progress: int | None + duration: int | None + delegation: int | None + statusDetails: str | None + statusDetailsMessage: str | None + + @classmethod + def parse(cls, end_point: Mapping): + return cls( + ip_address=get_str('ipAddress', end_point), + server_name=get_str('serverName', end_point), + status_message=get_str('statusMessage', end_point), + grade=get_str('grade', end_point), + grade_trust_ignored=get_str('gradeTrustIgnored', end_point), + has_warnings=get_bool('hasWarnings', end_point), + is_exceptional=get_bool('isExceptional', end_point), + progress=get_int('progress', end_point), + duration=get_int('duration', end_point), + delegation=get_int('delegation', end_point), + statusDetails=get_str('statusDetails', end_point), + statusDetailsMessage=get_str('statusDetailsMessage', end_point), + ) + + +@dataclass(frozen=True) +class SSLLabsHost: + host: str + port: int + protocol: str + is_public: bool + status: str + start_time: int + test_time: int | None + engine_version: str + criteria_version: str + status_message: str | None + cache_expiry_time: int | None + from_agent_cache: bool | None + end_points: Sequence[SSLLabsEndpoint] + + @classmethod + def parse(cls, ssl_host): + return cls( + host=get_str('host', ssl_host), + port=get_int('port', ssl_host), + protocol=get_str('protocol', ssl_host), + is_public=get_bool('isPublic', ssl_host), + status=get_str('status', ssl_host), + start_time=get_int('startTime', ssl_host), + test_time=get_int('testTime', ssl_host), + engine_version=get_str('engineVersion', ssl_host), + criteria_version=get_str('criteriaVersion', ssl_host), + status_message=get_str('statusMessage', ssl_host), + cache_expiry_time=get_int('cacheExpiryTime', ssl_host), + from_agent_cache=get_bool('from_agent_cache', ssl_host), + end_points=[SSLLabsEndpoint.parse(endpoint) for endpoint in ssl_host.get('endpoints', [])] + ) + + +SECTION = Mapping[str: SSLLabsHost] + + +# _CMK_TIME_FORMAT = '%Y-%m-%dT%H:%M:%S.%m %Z' + + +def parse_ssllabs_grade(string_table) -> SECTION | None: + try: + data = json_loads(string_table[0][0]) + except JSONDecodeError: + return + + ssl_hosts = {host['host']: SSLLabsHost.parse(host) for host in data if host.get('host') is not None} + return ssl_hosts + + +def discovery_ssllabs_grade(section: SECTION) -> DiscoveryResult: + for ssl_host in section: + yield Service(item=ssl_host) + + +def collect_has_warnings(end_points: Sequence[SSLLabsEndpoint]) -> Sequence[bool]: + return list(set([end_point.has_warnings for end_point in end_points if end_point.has_warnings is not None])) + + +def collect_is_exceptional(end_points: Sequence[SSLLabsEndpoint]) -> Sequence[bool]: + return list(set([end_point.is_exceptional for end_point in end_points if end_point.is_exceptional is not None])) + + +def check_grade(score: Tuple, grade: str, name: str, notice_only: bool) -> Result: + re_ok = re_compile(score[0]) + re_warn = re_compile(score[1]) + re_crit = re_compile(score[2]) + + if re_match(re_ok, grade): + state = State.OK + elif re_match(re_warn, grade): + state = State.WARN + elif re_match(re_crit, grade): + state = State.CRIT + else: + state = State.UNKNOWN + + message = f'{name} Grade: {grade}'.strip() + if notice_only: + yield Result(state=state, notice=message) + else: + yield Result(state=state, summary=message) + + +def check_grades(params: Mapping[str: any], ssl_host: SSLLabsHost, value_store): + grades = list(set([end_point.grade for end_point in ssl_host.end_points if end_point.grade is not None])) + if len(grades) == 1: + yield from check_grade(score=params['score'], name='', grade=grades[0], notice_only=False) + if (last_grade := value_store.get(ssl_host.host)) is not None: + yield Result(state=State.OK, summary=f'Last grade: {last_grade}') + value_store[ssl_host.host] = grades[0] + elif len(grades) == 0: + yield Result(state=State(params.get('no_grade', 1)), notice=f'No grade information found') + else: + end_points: Sequence[SSLLabsEndpoint] = ssl_host.end_points + for end_point in end_points: + name = f'{end_point.server_name}/{end_point.ip_address}' + last_grade = value_store.get(name) + if end_point.grade is not None: + yield from check_grade( + score=params['score'], + name=name, + grade=end_point.grade, + notice_only=True, + ) + yield Result(state=State.OK, notice=f'{name} last grade: {last_grade}') + value_store[name] = end_point.grade + elif last_grade is not None: + yield from check_grade( + score=params['score'], + name=f'{name} Last', + grade=last_grade, + notice_only=True, + ) + + +def check_has_warning(params: Mapping[str: any], end_points: Sequence[SSLLabsEndpoint]): + has_warnings = list(set([ + end_point.has_warnings for end_point in end_points if end_point.has_warnings is not None + ])) + if len(has_warnings) == 1 and has_warnings[0] is True: + yield Result(state=State(params.get('has_warnings', 1)), notice=f'Has warnings') + else: + for end_point in end_points: + name = f'{end_point.server_name}/{end_point.ip_address}' + if end_point.has_warnings is True: + yield Result(state=State(params.get('has_warnings', 1)), notice=f'{name}: has warnings') + + +def check_is_exceptional(params: Mapping[str: any], end_points: Sequence[SSLLabsEndpoint]): + is_exceptional = list(set([ + end_point.is_exceptional for end_point in end_points if end_point.is_exceptional is not None + ])) + if len(is_exceptional) == 1 and is_exceptional[0] is not True: + yield Result(state=State(params.get('is_exceptional', 1)), notice=f'Is not exceptional') + else: + for end_point in end_points: + name = f'{end_point.server_name}/{end_point.ip_address}' + if end_point.has_warnings is True: + yield Result(state=State(params.get('is_exceptional', 1)), notice=f'{name}: is not exceptional') + + +def check_status(params: Mapping[str: any], end_points: Sequence[SSLLabsEndpoint]): + for end_point in end_points: + name = f'{end_point.server_name}/{end_point.ip_address}' + + if end_point.status_message.lower() not in ['ready', 'in progress']: + yield Result(state=State.WARN, notice=f'Status {name}: {end_point.status_message}') + + +def check_ssllabs_grade(item: str, params: Mapping[str: any], section: SECTION) -> CheckResult: + try: + ssl_host: SSLLabsHost = section[item] + except KeyError: + return None + + value_store = get_value_store() + + match ssl_host.status: + case 'READY': + levels_upper = None + if params.get('age') is not None: + warn, crit = params.get('age') + levels_upper = (warn * 86400, crit * 86400) # change to days + + yield from check_levels( + value=now_time() - (ssl_host.test_time / 1000), + label='Last tested', + render_func=render.timespan, + levels_upper=levels_upper, + # notice_only=True, + ) + yield from check_grades(params, ssl_host, value_store) + yield from check_has_warning(params, ssl_host.end_points) + yield from check_is_exceptional(params, ssl_host.end_points) + yield from check_status(params, ssl_host.end_points) + + case 'DNS': + yield Result(state=State(params.get('state_dns', 0)), summary=f'DNS: {ssl_host.status_message}') + yield Result( + state=State.OK, + summary=f'Started {render.timespan(now_time() - (ssl_host.start_time / 1000))} before' + ) + case 'ERROR': + yield Result(state=State(params.get('state_error'), 1), notice=f'Error: {ssl_host.status_message}') + if ssl_host.cache_expiry_time: + yield Result( + state=State.OK, + notice=f'Cache expiry time: {render.datetime(ssl_host.cache_expiry_time / 1000)}' + ) + case 'IN_PROGRESS': + yield Result( + state=State(params.get('state_in_progress', 0)), + summary=f'Test is in progress, started ' + f'{render.timespan(now_time() - (ssl_host.start_time / 1000))} before' + ) + yield from check_grades(params, ssl_host, value_store) + yield from check_has_warning(params, ssl_host.end_points) + yield from check_is_exceptional(params, ssl_host.end_points) + yield from check_status(params, ssl_host.end_points) + case _: + yield Result(state=State.UNKNOWN, notice=f'Unknown test status: {ssl_host.status}') + + yield Result(state=State.OK, notice=f'For full details go to https://www.ssllabs.com/ssltest/analyze.html?d={item}') + + if params.get('details'): + yield Result(state=State.OK, notice=f'\nHost details') + yield Result(state=State.OK, notice=f'Host: {ssl_host.host}') + yield Result(state=State.OK, notice=f'Port: {ssl_host.port}') + yield Result(state=State.OK, notice=f'Protocol: {ssl_host.protocol}') + yield Result(state=State.OK, notice=f'Start Time: {render.datetime(ssl_host.start_time / 1000)}') + if ssl_host.test_time is not None: + yield Result(state=State.OK, notice=f'Test Time: {render.datetime(ssl_host.test_time / 1000)}') + yield Result(state=State.OK, notice=f'Engine version: {ssl_host.engine_version}') + yield Result(state=State.OK, notice=f'Criteria version: {ssl_host.criteria_version}') + yield Result(state=State.OK, notice=f'Status: {ssl_host.status}') + if ssl_host.from_agent_cache is not None: + yield Result(state=State.OK, notice=f'From agent cache: {ssl_host.from_agent_cache}') + else: + yield Result(state=State.WARN, notice=f'Live data') + + if ssl_host.end_points: + yield Result(state=State.OK, notice=f'\nEndpoints') + for end_point in ssl_host.end_points: + yield Result(state=State.OK, notice=f'Server name: {end_point.server_name}') + yield Result(state=State.OK, notice=f'IP-Address: {end_point.ip_address}') + yield Result(state=State.OK, notice=f'Status Message: {end_point.status_message}') + if end_point.grade is not None: + yield Result(state=State.OK, notice=f'Grade: {end_point.grade}') + + name = f'{end_point.server_name}/{end_point.ip_address}' + if (last_grade := value_store.get(name)) is not None: + yield Result(state=State.OK, notice=f'Last grade: {last_grade}') + + if end_point.grade_trust_ignored is not None: + yield Result(state=State.OK, notice=f'Grade Trust Ignored: {end_point.grade_trust_ignored}') + if end_point.has_warnings is not None: + yield Result(state=State.OK, notice=f'has warnings: {end_point.has_warnings}') + if end_point.is_exceptional is not None: + yield Result(state=State.OK, notice=f'is exceptional: {end_point.is_exceptional}') + if end_point.progress is not None: + yield Result(state=State.OK, notice=f'progress: {end_point.progress}') + if end_point.duration is not None: + yield Result(state=State.OK, notice=f'duration: {render.timespan(end_point.duration / 1000)}s') + yield Result(state=State.OK, notice=f'delegation: {end_point.delegation}') + yield Result(state=State.OK, notice=f'\n') + + +register.agent_section( + name="ssllabs_grade", + parse_function=parse_ssllabs_grade, +) + +register.check_plugin( + name='ssllabs_grade', + service_name='SSL Labs %s', + discovery_function=discovery_ssllabs_grade, + check_function=check_ssllabs_grade, + check_default_parameters={ + "score": ("A", "B|C", "D|E|F|M|T"), + }, + check_ruleset_name='ssllabs_grade' +) diff --git a/agents/special/agent_ssllabs b/source/agents/special/agent_ssllabs similarity index 90% rename from agents/special/agent_ssllabs rename to source/agents/special/agent_ssllabs index c28031c..d1dc725 100755 --- a/agents/special/agent_ssllabs +++ b/source/agents/special/agent_ssllabs @@ -8,7 +8,7 @@ # if the file in special_agents it will not work, don't now why, it looks in the wrong directory # # from cmk.special_agents.agent_cisco_ise import main -from cmk.special_agent.agent_ssllabs import main +from cmk.special_agents.agent_ssllabs import main if __name__ == '__main__': main() \ No newline at end of file diff --git a/checkman/ssllabs_grade b/source/checkman/ssllabs_grade similarity index 100% rename from checkman/ssllabs_grade rename to source/checkman/ssllabs_grade diff --git a/source/checks/agent_ssllabs b/source/checks/agent_ssllabs new file mode 100644 index 0000000..57e1c5d --- /dev/null +++ b/source/checks/agent_ssllabs @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# License: GNU General Public License v2 +# +# +# Author: thl-cmk[at]outlook[dot]com +# URL : https://thl-cmk.hopto.org +# Date : 2024-04-29 +# File : agent_ssllabs.py (params stub) +# + +# based on the ssllabs plugin from Karsten Schoeke karsten.schoeke@geobasis-bb.de +# see https://exchange.checkmk.com/p/ssllabs + +def agent_ssllabs_arguments(params, _hostname, _ipaddress): + args = [] + + if (ssl_hosts := params.get('ssl_hosts')) is not None: + args += ['--ssl-hosts', ','.join(ssl_hosts)] + + if (timeout := params.get('timeout')) is not None: + args += ['--timeout', timeout] + + if (proxy := params.get('proxy')) is not None: + args += ['--proxy', proxy] + + if (publish_results := params.get('publish_results')) is not None: + args += ['--publish', publish_results] + + if (max_age := params.get('max_age')) is not None: + args += ['--max-age', max_age] + + return args + + +special_agent_info['ssllabs'] = agent_ssllabs_arguments diff --git a/source/gui/wato/check_parameters/ssllabs_grade.py b/source/gui/wato/check_parameters/ssllabs_grade.py new file mode 100644 index 0000000..67406f0 --- /dev/null +++ b/source/gui/wato/check_parameters/ssllabs_grade.py @@ -0,0 +1,126 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# License: GNU General Public License v2 +# +# +# Author: thl-cmk[at]outlook[dot]com +# URL : https://thl-cmk.hopto.org +# Date : 2024-04-29 +# File : ssllabs_grade.py (wato check plugin) + +# based on the ssllabs plugin from Karsten Schoeke karsten.schoeke@geobasis-bb.de +# see https://exchange.checkmk.com/p/ssllabs + +# +# 2021-05-15: rewritten for CMK 2.0 by thl-cmk[at]outlook[dot]com +# 2024-05-01: modified for CMK 2.2.x +# moved to ~/local/lib/check_mk/gui/plugins/wato/check_parameters +# 2024-05-01: changed age to days + +from cmk.gui.i18n import _ +from cmk.gui.valuespec import ( + Dictionary, + FixedValue, + Integer, + RegExp, + RegExpUnicode, + TextAscii, + Tuple, + MonitoringState, +) + +from cmk.gui.plugins.wato.utils import ( + CheckParameterRulespecWithItem, + rulespec_registry, + RulespecGroupCheckParametersNetworking, +) + + +def _parameter_valuespec_ssllabs_grade(): + return Dictionary(elements=[ + ('age', + Tuple( + title=_('Maximum age of ssllabs scan'), + help=_('The maximum age of the last ssllabs check.'), + elements=[ + Integer(title=_('Warning at'), default_value=2, minvalue=1), + Integer(title=_('Critical at'), default_value=3, minvalue=1), + ])), + ('score', + Tuple( + title=_('grade level for ssllabs scan'), + help=_('Put here the Integerttern (regex) for ssllabs grade check level.'), + elements=[ + RegExpUnicode( + title=_('Pattern (regex) Ok level'), + mode=RegExp.prefix, + default_value='A', + ), + RegExpUnicode( + title=_('Pattern (regex) Warning level'), + mode=RegExp.prefix, + default_value='B|C', + ), + RegExpUnicode( + title=_('Pattern (regex) Critical level'), + mode=RegExp.prefix, + default_value='D|E|F|M|T', + ), + ])), + ('no_grade', + MonitoringState( + title=_('Monitoring state if no grade was found'), + default_value=1, + help=_('Set the monitoring state no grade information was found the result. Default is WARN.'), + )), + ('has_warnings', + MonitoringState( + title=_('Monitoring state if host has warnings'), + default_value=1, + help=_('Set the monitoring state if "hasWarnings" in the result is true. Default is WARN.'), + )), + ('is_exceptional', + MonitoringState( + title=_('Monitoring state if host is not exceptional'), + default_value=1, + help=_('Set the monitoring state if "isExceptional" in the result is not true. Default is WARN.'), + )), + ('state_dns', + MonitoringState( + title=_('Monitoring state if the check is in "DNS resolving" state'), + default_value=0, + help=_('Set the monitoring state if the ssllabs scan is in "DNS resolving" state. Default is OK.'), + )), + ('state_error', + MonitoringState( + title=_('Monitoring state if the check is in "ERROR" state'), + default_value=1, + help=_('Set the monitoring state if the ssllabs scan is reporting an "ERROR". Default is WARN.'), + )), + ('state_in_progress', + MonitoringState( + title=_('Monitoring state if the check is in "IN_PROGRESS" state'), + default_value=0, + help=_('Set the monitoring state if the ssllabs scan is "IN_PROGRESS". Default is OK.'), + )), + ('details', + FixedValue( + value=True, + title=_('Show result detail in the service details'), + totext='', + )) + ], + title=_('SSL Server check via ssllabs API'), + ) + + +rulespec_registry.register( + CheckParameterRulespecWithItem( + check_group_name='ssllabs_grade', + group=RulespecGroupCheckParametersNetworking, + item_spec=lambda: TextAscii(title=_('The FQDN on ssl server to check'), ), + match_type='dict', + parameter_valuespec=_parameter_valuespec_ssllabs_grade, + title=lambda: _('SSL Server via ssllabs API.'), + )) diff --git a/source/lib/python3/cmk/special_agents/agent_ssllabs.py b/source/lib/python3/cmk/special_agents/agent_ssllabs.py new file mode 100644 index 0000000..62aaed5 --- /dev/null +++ b/source/lib/python3/cmk/special_agents/agent_ssllabs.py @@ -0,0 +1,251 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# License: GNU General Public License v2 +# +# +# Author: thl-cmk[at]outlook[dot]com +# URL : https://thl-cmk.hopto.org +# Date : 2024-04-29 +# File : ssllabs_grade.py (wato check plugin) +# +# based on the ssllabs plugin from Karsten Schoeke karsten.schoeke@geobasis-bb.de +# see https://exchange.checkmk.com/p/ssllabs +# + +# 2021-05-15: rewritten for CMK 2.0 by thl-cmk[at]outlook[dot]com +# changed cache file name form host_address to ssl_host_address +# 2021-05-16: changed arguments to argparse +# added options for publish results and max cache age +# 2024-05-01: refactoring + +# sample agent output (formatted) +# <<<check_mk>>> +# Version: 2.0.2 +# AgentOS: linux +# +# <<<ssllabs_grade:sep(0)>>> +# [ +# { +# "host": "thl-cmk.hopto.org", +# "port": 443, +# "protocol": "http", +# "isPublic": false, +# "status": "READY", +# "startTime": 1714559152230, +# "testTime": 1714559237958, +# "engineVersion": "2.3.0", +# "criteriaVersion": "2009q", +# "endpoints": [ +# {"ipAddress": "91.4.75.201", +# "serverName": "p5b044bc9.dip0.t-ipconnect.de", +# "statusMessage": "Ready", +# "grade": "A+", +# "gradeTrustIgnored": "A+", +# "hasWarnings": false, +# "isExceptional": true, +# "progress": 100, +# "duration": 85530, +# "delegation": 1 +# } +# ] +# }, +# { +# "host": "checkmk.com", +# "port": 443, +# "protocol": "http", +# "isPublic": false, +# "status": "IN_PROGRESS", +# "startTime": 1714563744895, +# "engineVersion": "2.3.0", +# "criteriaVersion": "2009q", +# "endpoints": [ +# { +# "ipAddress": "2a0a:51c1:0:5:0:0:0:4", +# "serverName": "www.checkmk.com", +# "statusMessage": "Ready", +# "grade": "A+", +# "gradeTrustIgnored": "A+", +# "hasWarnings": false, +# "isExceptional": true, +# "progress": 100, +# "duration": 72254, +# "delegation": 1 +# }, +# { +# "ipAddress": "45.133.11.28", +# "serverName": "www.checkmk.com", +# "statusMessage": "In progress", +# "statusDetails": "TESTING_SESSION_RESUMPTION", +# "statusDetailsMessage": "Testing session resumption", "delegation": 1 +# } +# ] +# } +# ] +# <<<>>> + +from argparse import Namespace +from collections.abc import Sequence +from json import dumps as json_dumps, loads as json_loads, JSONDecodeError +from pathlib import Path +from requests import get +from requests.exceptions import ConnectionError +from sys import stdout as sys_stdout +from time import time as now_time + +from cmk.special_agents.utils.agent_common import special_agent_main +from cmk.special_agents.utils.argument_parsing import create_default_argument_parser +from cmk.utils.paths import tmp_dir + +VERSION = '2.0.2' + + +class Args(Namespace): + max_age: int + proxy: str + publish: str + ssl_hosts: str + timeout: float + + +def write_section(section: dict): + sys_stdout.write('\n<<<ssllabs_grade:sep(0)>>>\n') + sys_stdout.write(json_dumps(section)) + sys_stdout.write('\n<<<>>>\n') + + +def parse_arguments(argv: Sequence[str] | None) -> Args: + """'Parse arguments needed to construct a URL and for connection conditions""" + parser = create_default_argument_parser(__doc__) + parser.description = 'This is a CKK special agent for the Qualys SSL Labs API to monitor SSL Certificate status' + parser.add_argument( + '--ssl-hosts', required=True, type=str, + help='Comma separated list of FQDNs to test for', + ) + parser.add_argument( + '--proxy', required=False, + help='URL to HTTPS Proxy i.e.: https://192.168.1.1:3128', + ) + parser.add_argument( + '--timeout', '-t', type=float, default=60, + help='API call timeout in seconds', + ) + parser.add_argument( + '--publish', type=str, default='off', choices=['on', 'off'], + help='Publish test results on ssllabs.com', + ) + parser.add_argument( + '--max-age', type=int, default=167, + help='Maximum report age, in hours, if retrieving from "ssllabs.com" cache', + ) + parser.epilog = ( + '\n\nAcnowlegement:\n' + ' This agent is based on the work by Karsten Schoeke karsten[dot]schoeke[at]geobasis-bb[dot]de\n' + ' see https://exchange.checkmk.com/p/ssllabs\n\n' + f'written by thl-cmk[at]outlook[dot], Version: {VERSION}, ' + f'For more information see: https://thl-cmk.hopto.org\n' + ) + return parser.parse_args(argv) + + +def connect_ssllabs_api(ssl_host_address: str, host_cache: str, args: Args, ) -> dict | None: + # + # https://github.com/ssllabs/ssllabs-scan + # + server = 'api.ssllabs.com' + uri = 'api/v3/analyze' # change to api v4 (?) + max_age = args.max_age * 24 # default 1 day (1 week minus 1 hour) + publish = args.publish # default off + from_cache = 'on' # on | off + # all_data = 'done' # on | done + ignore_mismatch = 'on' # on | off + start_new = 'on' + + # url for request webservice (&startNew={startNew}&all={all}) + url = ( + f'https://{server}/{uri}' + f'?host={ssl_host_address}' + f'&publish={publish}' + f'&fromCache={from_cache}' + f'&maxAge={max_age}' + # f'&all={all_data}' + f'&ignoreMismatch={ignore_mismatch}' + # f'&startNew={start_new}' + ) + proxies = {} + if args.proxy is not None: + proxies = {'https': args.proxy.split('/')[-1]} # remove 'https://' from proxy string + + try: + response = get( + url=url, + timeout=args.timeout, + proxies=proxies, + headers={ + 'User-Agent': f'CMK SSL Labs special agent {VERSION}', + }, + ) + except ConnectionError as e: + host_data = {'host': ssl_host_address, 'status': 'ConnectionError', 'error': str(e)} + else: + try: + host_data = response.json() + except JSONDecodeError as e: + host_data = {'host': ssl_host_address, 'status': 'JSONDecodeError', 'error': str(e)} + if host_data.get('status') == 'READY': + Path(host_cache).write_text(response.text) + + return host_data + + +def read_cache(host_cache: str) -> dict | None: + try: + data: dict = json_loads(Path(host_cache).read_text()) + except JSONDecodeError: + return + + data.update({'from_agent_cache': True}) + return data + + +def agent_ssllsbs_main(args: Args) -> int: + now = now_time() + cache_dir = f'{tmp_dir}/agents/agent_ssllabs' + cache_age = args.max_age + 86400 + ssl_hosts = args.ssl_hosts.split(',') + + # Output general information about the agent + sys_stdout.write('<<<check_mk>>>\n') + sys_stdout.write(f'Version: {VERSION}\n') + sys_stdout.write('AgentOS: linux\n') + + # create cache directory, if it not exists + Path(cache_dir).mkdir(parents=True, exist_ok=True) + + data = [] + for ssl_host_address in ssl_hosts: + host_cache = f'{cache_dir}/{ssl_host_address}' + + # check if cache file exists and is not older as cache_age + if Path(host_cache).exists() and now - Path(host_cache).stat().st_mtime < cache_age: + if host_data := read_cache(host_cache=host_cache): + data.append(host_data) + else: + if host_data := connect_ssllabs_api( + ssl_host_address=ssl_host_address, + host_cache=host_cache, + args=args, + ): + data.append(host_data) + + if data: + write_section(data) + return 0 + + +def main() -> int: + return special_agent_main(parse_arguments, agent_ssllsbs_main) + + +if __name__ == '__main__': + main() diff --git a/packages/agent_ssllabs b/source/packages/agent_ssllabs similarity index 82% rename from packages/agent_ssllabs rename to source/packages/agent_ssllabs index 6f8dc48..698d35e 100644 --- a/packages/agent_ssllabs +++ b/source/packages/agent_ssllabs @@ -26,13 +26,12 @@ 'agents': ['special/agent_ssllabs'], 'checkman': ['ssllabs_grade'], 'checks': ['agent_ssllabs'], - 'lib': ['check_mk/special_agent/agent_ssllabs.py'], - 'web': ['plugins/wato/agent_ssllabs.py', - 'plugins/wato/ssllabs_grade.py']}, + 'gui': ['wato/check_parameters/ssllabs_grade.py'], + 'lib': ['python3/cmk/special_agents/agent_ssllabs.py'], + 'web': ['plugins/wato/agent_ssllabs.py']}, 'name': 'agent_ssllabs', - 'num_files': 7, 'title': 'ssllabs api check', - 'version': '2.0.1', - 'version.min_required': '2.0.0', - 'version.packaged': '2021.04.10', - 'version.usable_until': None} \ No newline at end of file + 'version': '2.0.2-20240105', + 'version.min_required': '2.2.0b1', + 'version.packaged': '2.2.0p24', + 'version.usable_until': None} diff --git a/source/web/plugins/wato/agent_ssllabs.py b/source/web/plugins/wato/agent_ssllabs.py new file mode 100644 index 0000000..55fb883 --- /dev/null +++ b/source/web/plugins/wato/agent_ssllabs.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# License: GNU General Public License v2 +# +# +# Author: thl-cmk[at]outlook[dot]com +# URL : https://thl-cmk.hopto.org +# Date : 2024-04-29 +# File : ssllabs.py (wato special agent) +# + +# based on the ssllabs plugin from Karsten Schoeke karsten.schoeke@geobasis-bb.de +# see https://exchange.checkmk.com/p/ssllabs + +# 2024-05-01: modified for CMK 2.2.x + +from cmk.gui.i18n import _ +from cmk.gui.plugins.wato.utils import ( + HostRulespec, + rulespec_registry, +) +from cmk.gui.valuespec import ( + Dictionary, + Integer, + TextAscii, + ListOfStrings, + FixedValue, +) + +from cmk.gui.plugins.wato.special_agents.common import RulespecGroupDatasourceProgramsOS + + +def _valuespec_special_agents_ssllabs(): + return Dictionary( + elements=[ + ('ssl_hosts', + ListOfStrings( + title=_('SSL hosts to check'), + orientation='vertical', + allow_empty=False, + size=50, + empty_text='www.checkmk.com', + max_entries=10, + help=_( + 'List of server names to check. Add the host names without "http(s)://". Ie: www.checkmk.com. ' + 'The list is limited to 10 entries. If you need more than 10 entries create another rule.' + ), + )), + ('timeout', + Integer( + title=_('Connect Timeout'), + help=_( + 'The network timeout in seconds when communicating via HTTPS. The default is 30 seconds.' + ), + default_value=30, + minvalue=1, + unit=_('seconds') + )), + ('proxy', + TextAscii( + title=_('proxy server, if required'), + help=_('proxy in the format: https://ip-addres|servername:port'), + )), + ('publish_results', + FixedValue( + value='on', + title=_('Publish results'), + totext=_('Results will be published'), + help=_( + 'By default test results will not be published. If you enable this option the test' + ' results will by public visible on https://www.ssllabs.com/ssltest' + ), + default_value='off', + )), + ('max_age', + Integer( + title=_('Max Age for ssllbas.com cache'), + help=_( + 'Maximum report age, in hours, if retrieving from "ssllabs.com" cache. ' + 'After this time a new test will by initiated. The default (and minimum) is 1 Day' + ), + default_value=1, + minvalue=1, + unit=_('Days') + )), + ], + title=_('Qualys SSL Labs server test'), + help=_( + 'This rule selects the ssllabs agent, which fetches SSL Server status from api.ssllabs.com.' + 'For more details about the SSL server check see https://www.ssllabs.com/ssltest/index.html.' + 'For mor information about the SSL Labs API see: ' + 'https://github.com/ssllabs/ssllabs-scan/blob/master/ssllabs-api-docs-v3.md.' + ), + ) + + +rulespec_registry.register( + HostRulespec( + group=RulespecGroupDatasourceProgramsOS, + name='special_agents:ssllabs', + valuespec=_valuespec_special_agents_ssllabs, + )) diff --git a/web/plugins/wato/agent_ssllabs.py b/web/plugins/wato/agent_ssllabs.py deleted file mode 100644 index 40f4d13..0000000 --- a/web/plugins/wato/agent_ssllabs.py +++ /dev/null @@ -1,82 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -# -# - - -from cmk.gui.i18n import _ -from cmk.gui.plugins.wato import ( - HostRulespec, - rulespec_registry, -) -from cmk.gui.valuespec import ( - Dictionary, - Integer, - TextAscii, - ListOfStrings, - FixedValue, -) - -from cmk.gui.plugins.wato.datasource_programs import ( - RulespecGroupDatasourceProgramsOS, -) - - -def _valuespec_special_agents_ssllabs(): - return Dictionary( - elements=[ - ('sslhosts', - ListOfStrings( - title=_('SSL hosts to check'), - orientation='vertical', - allow_empty=False, - ) - ), - ('timeout', - Integer( - title=_('Connect Timeout'), - help=_('The network timeout in seconds when communicating via HTTPS. ' - 'The default is 60 seconds.'), - default_value=60, - minvalue=1, - unit=_('seconds') - ) - ), - ('proxy', - TextAscii( - title=_('proxy server, if required'), - help=_('proxy in the format: https://ip-addres|servername:port'), - ), - ), - ('publishresults', - FixedValue( - 'on', - title=_('Publish results'), - totext=_('Results will be published'), - help=_('By default test results will not be published. If you enable this option the test' - ' results will by public visible on https://www.ssllabs.com/ssltest'), - default_value='off', - )), - ('maxage', - Integer( - title=_('Max Age for ssllbas.com cache'), - help=_('Maximum report age, in hours, if retrieving from "ssllabs.com" cache. ' - 'After this time a new test will by initiated. The default (and minimum) is 167 hours'), - default_value=167, - minvalue=167, - )), - ], - title=_('Qualys SSL Labs server test'), - help=_('This rule selects the ssllabs agent, which fetches SSL Server status from api.ssllabs.com.' - 'For more details about the SSL server check see https://www.ssllabs.com/ssltest/index.html.' - 'For mor information about the SSL Labs API see: ' - 'https://github.com/ssllabs/ssllabs-scan/blob/master/ssllabs-api-docs-v3.md.'), - ) - - -rulespec_registry.register( - HostRulespec( - group=RulespecGroupDatasourceProgramsOS, - name='special_agents:ssllabs', - valuespec=_valuespec_special_agents_ssllabs, - )) diff --git a/web/plugins/wato/ssllabs_grade.py b/web/plugins/wato/ssllabs_grade.py deleted file mode 100644 index 9e9df0b..0000000 --- a/web/plugins/wato/ssllabs_grade.py +++ /dev/null @@ -1,76 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -# -# -# -# 2015 Karsten Schoeke karsten.schoeke@geobasis-bb.de -# -# 2021-05-15: rewritten for CMK 2.0 by thl-cmk[at]outlook[dot]com -# - -from cmk.gui.i18n import _ -from cmk.gui.valuespec import ( - Dictionary, - TextAscii, - Age, - Tuple, - RegExpUnicode, - RegExp, -) - -from cmk.gui.plugins.wato import ( - CheckParameterRulespecWithItem, - rulespec_registry, - RulespecGroupCheckParametersNetworking, -) - - -def _parameter_valuespec_ssllabs_grade(): - return Dictionary(elements=[ - ('age', - Tuple( - title=_('Maximum age of ssllabs scan'), - help=_('The maximum age of the last ssllabs check.'), - elements=[ - Age(title=_('Warning at'), default_value=604800, minvalue=604800), - Age(title=_('Critical at'), default_value=864000, minvalue=691200), - ] - ), - ), - ('score', - Tuple( - title=_('grade level for ssllabs scan'), - help=_('Put here the Integerttern (regex) for ssllabs grade check level.'), - elements=[ - RegExpUnicode( - title=_('Pattern (regex) Ok level'), - mode=RegExp.prefix, - default_value='A', - ), - RegExpUnicode( - title=_('Pattern (regex) Warning level'), - mode=RegExp.prefix, - default_value='B|C', - ), - RegExpUnicode( - title=_('Pattern (regex) Critical level'), - mode=RegExp.prefix, - default_value='D|E|F|M|T', - ), - ] - ), - ), - ], - title=_('SSL Server check via ssllabs API'), - ) - - -rulespec_registry.register( - CheckParameterRulespecWithItem( - check_group_name='ssllabs_grade', - group=RulespecGroupCheckParametersNetworking, - item_spec=lambda: TextAscii(title=_('The FQDN on ssl server to check'), ), - match_type='dict', - parameter_valuespec=_parameter_valuespec_ssllabs_grade, - title=lambda: _('SSL Server via ssllabs API.'), - )) -- GitLab