diff --git a/agent_based/curl.py b/agent_based/curl.py index d5563422f08802adcb14eb4f091305a68f0fe4a4..34030d6accb16fb422f4d64969df2cf0253570c1 100644 --- a/agent_based/curl.py +++ b/agent_based/curl.py @@ -28,6 +28,7 @@ # 2022-04-26: made http(s) URLs clickable # 2022-05-15: added workaround for raise ValueError("Cannot render negative timespan") # 2022-05-17: fixed wrong import path for _TIME_UNITS and _gen_timespan_chunks +# 2023-06-07: moved gui files to ~/local/lib/chek_mk/gui/plugins/... # # Example output from agent: diff --git a/curl-0.1.9-20230607.mkp b/curl-0.1.9-20230607.mkp new file mode 100644 index 0000000000000000000000000000000000000000..554db2d6b2669da4a6a773151f2804ddb3f6d211 Binary files /dev/null and b/curl-0.1.9-20230607.mkp differ diff --git a/curl.mkp b/curl.mkp index 8a38d4db2aaadcc599b9392c916fd4727df5c172..554db2d6b2669da4a6a773151f2804ddb3f6d211 100644 Binary files a/curl.mkp and b/curl.mkp differ diff --git a/gui/metrics/curl.py b/gui/metrics/curl.py new file mode 100644 index 0000000000000000000000000000000000000000..bf186c5e436ba65459cd49fea5339465e8edd928 --- /dev/null +++ b/gui/metrics/curl.py @@ -0,0 +1,210 @@ +#!/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 : 2022-02-15 +# +# Metrics file for the curl plugin +# +# 2022-02-15: rewritten form cmk 2.0, based on the work by doc[at]snowheaven[dot]de +# 2022-02-20: added num_connects, num_redirects, size_download, size_header and speed_download +# 2022-03-13: moved cert time left graph to the end of graphs +# 2022-05-17: added scalars to cert_time_left + +from cmk.gui.i18n import _ + +from cmk.gui.plugins.metrics.utils import ( + metric_info, + graph_info, + perfometer_info, +) + +metric_info['time_namelookup'] = { + 'title': _('Time name lookup'), + 'unit': 's', + 'color': '11/a', +} +metric_info['time_connect'] = { + 'title': _('Time connect'), + 'unit': 's', + 'color': '21/a', +} +metric_info['time_appconnect'] = { + 'title': _('Time app connect'), + 'unit': 's', + 'color': '31/b', +} +metric_info['time_pretransfer'] = { + 'title': _('Time pre transfer'), + 'unit': 's', + 'color': '41/c', +} +metric_info['time_starttransfer'] = { + 'title': _('Time start transfer'), + 'unit': 's', + 'color': '13/b', +} +metric_info['time_total'] = { + 'title': _('Time Total'), + 'unit': 's', + 'color': '25/a', +} +metric_info['time_redirect'] = { + 'title': _('Time redirect'), + 'unit': 's', + 'color': '33/b', +} + +metric_info['num_connects'] = { + 'title': _('# of connects'), + 'unit': 'count', + 'color': '14/a', +} +metric_info['num_redirects'] = { + 'title': _('# of redirects'), + 'unit': 'count', + 'color': '24/b', +} +metric_info['num_headers'] = { + 'title': _('# of headers'), + 'unit': 'count', + 'color': '34/b', +} + +metric_info['size_download'] = { + 'title': _('Size download'), + 'unit': 'bytes', + 'color': '15/b', +} +metric_info['size_upload'] = { + 'title': _('Size upload'), + 'unit': 'bytes', + 'color': '25/b', +} + +metric_info['size_header'] = { + 'title': _('Size header'), + 'unit': 'bytes', + 'color': '35/b', +} +metric_info['size_request'] = { + 'title': _('Size request'), + 'unit': 'bytes', + 'color': '14/b', +} + +metric_info['speed_download'] = { + 'title': _('Speed download'), + 'unit': 'bytes/s', + 'color': '23/a', +} +metric_info['speed_upload'] = { + 'title': _('Speed upload'), + 'unit': 'bytes/s', + 'color': '13/a', +} + +metric_info['cert_time_left'] = { + 'title': _('Certificate Time left'), + 'unit': 's', + 'color': '33/b', +} + +graph_info['curl_times_total'] = { + 'title': _('Times total'), + 'metrics': [ + ('time_total', 'area'), + ], + 'scalars': [ + ('time_total:crit', _('crit')), + ('time_total:warn', _('warn')), + ], +} +graph_info['curl_times'] = { + 'title': _('Times'), + 'metrics': [ + ('time_redirect', 'line'), + ('time_starttransfer', 'line'), + ('time_pretransfer', 'line'), + ('time_appconnect', 'line'), + ('time_connect', 'line'), + ('time_namelookup', 'line'), + ], + 'optional_metrics': [ + 'time_redirect', + 'time_starttransfer', + 'time_pretransfer', + 'time_appconnect', + 'time_connect', + 'time_namelookup', + ], +} +graph_info['curl_speed'] = { + 'title': _('Speed'), + 'metrics': [ + ('speed_upload', '-area'), + ('speed_download', 'area'), + ], + 'optional_metrics': [ + 'speed_download', + 'speed_upload', + ], +} +graph_info['curl_size_download'] = { + 'title': _('Size download/upload'), + 'metrics': [ + ('size_upload', '-area'), + ('size_download', 'area'), + ], + 'optional_metrics': [ + 'size_upload', + 'size_download', + ], +} +graph_info['curl_size_header'] = { + 'title': _('Size header/request'), + 'metrics': [ + ('size_request', '-area'), + ('size_header', 'area'), + ], + 'optional_metrics': [ + 'size_request', + 'size_header', + ], +} +graph_info['curl_counts'] = { + 'title': _('Counts'), + 'metrics': [ + ('num_redirects', '-line'), + ('num_connects', 'line'), + ('num_headers', 'line'), + + ], + 'optional_metrics': [ + 'num_connects', + 'num_redirects', + 'num_headers', + ], +} +graph_info['curl_cert_time'] = { + 'title': _('Certificate time left'), + 'metrics': [ + ('cert_time_left', 'area'), + ], + 'scalars': [ + ('cert_time_left:crit', _('crit')), + ('cert_time_left:warn', _('warn')), + ], +} + +perfometer_info.append( + { + 'type': 'logarithmic', + 'metric': 'time_total', + 'half_value': 5.0, # 5 seconds -> bar half full + 'exponent': 2.0, # every double of 5 == 10% of bar more full + }, +) diff --git a/gui/wato/curl.py b/gui/wato/curl.py new file mode 100644 index 0000000000000000000000000000000000000000..c55f0a5e7f488a82c4d9768c6441e1b739cf6b8a --- /dev/null +++ b/gui/wato/curl.py @@ -0,0 +1,1330 @@ +#!/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 : 2022-02-15 +# +# WATO file for curl plugin (bakery and check) +# +# 2022-02-19: moved global options under "Default settings" +# added per url settings +# added proxy settings (--proxy, --proxy-user, --proxy-digest, --proxy-basic, --proxy-ntlm, --proxy-anyauth) +# added follow redirects (--location) +# added don't verify certificates (--insecure) +# 2022-02-21: added tls/ssl version (--tlsv1.3, --tlsv1.2, --tlsv1.1, --tlsv1.0, --tlsv1, --sslv3, --sslv2) +# added http version (--http2, --http1.1, --http1.0) +# added header only (--head) +# added user authentication (--user, --basic, --digest, --ntlm, --ntlm-wb, --negotiate, --anyauth) +# added to proxy authentication (--proxy-negotiate) +# 2022-02-22: added option for cURL check plugin +# 2022-02-24: changed forbidden_chars from '<>| ' to '"<>#%{}|\^~[]` ' + "'" +# 2022-02-25: added plugin interval and timeout options +# added noproxy option to default settings +# 2022-02-26: added proxy protocol (--socks4, --socks4a, --socks4a, --socks5-hostname) +# 2022-02-27: added expected_strings option to default settings +# 2022-02-28: added expected_strings option to url settings +# added state_item_not_found option +# 2022-03-01: added options --limit-rate, --max-filesize, --max-time, --speed-limit, --speed-time, --connect-timeout +# added options --user-agent, --compressed, --no-alpn, --no-npn, --tcp-fastopen, --tcp-nodelay, +# added options --cert-status +# 2022-03-02: added options --referer -header and api_key_header (header from password store) +# 2022-03-05: added option --dump-header, state_header_str_not_found +# 2022-03-06: added option --verbose, --stderr cert_time_left +# fixed upper/lower levels +# 2022-03-10: optimized generation of upper/lower limit +# added transform for ms to s -> no need in th agent to change the value any more +# 2022-03-11: added check to avoid duplicate service names in url list +# added --location-trust, --max-redirs +# reworked redirect (--location, --location-trust, --max-redirs) +# reworked cert verify (-insecure, --cert-status, --ssl-no-revok) +# reworked advanced_settings (--no-alpn, --no-npn, --tcp-fastopen, -tcp-nodelay) +# removed get_session_data. moved to curl default options +# 2022-03-12: added --cacert +# added max_age +# 2022-03-13: added post data +# made some entries fordable (Default options/Per URL settings, CA Cert) +# changed url and service_name to curl_service tuple +# changed headers to list of tuple +# 2022-03-15: added regex pattern match for bakery +# 2022-03-18: added regex pattern match for check +# 2022-03-19: added options --dns-interface, -dns-ipv4-addr, --dns-ipv6-addr and --dns-servers +# reworked ip_address_resolution section +# added options --ftp-account, --ftp-alternative-to-user, --ftp-create-dirs, --ftp-method +# added options --ftp-pasv, --disable-epsv, --ftp-pret, --ftp-skip-pasv-ip +# added options --ftp-port, --disable-eprt +# added options --ftp-ssl-control, --ftp-ssl-ccc, --ftp-ssl-ccc-mode +# 2022-03-21: moved --connect-timeout, --limit-rate, --max-filesize, --max-time, --speed-limit, --speed-time +# to "limits" sub Directory +# 2022-03-22: added curl_error_code_to_ignore and http_error_code_to_ignore options +# 2022-03-24: added options --hostpubmd5, --hostpubsha256, --pubkey +# 2022-03-24: added options --key --passs +# reworked user_auth section +# 2022-03-25: added options --compressed-ssh, --list-only, --use-ascii +# added options --path-as-is, --ssl-allow-beast, --no-buffer, --no-keepalive, --no-sessionid +# 2022-03-28: added options --mail-auth, --mail-from, --mail-rcpt, --mail-rcpt-allowfails, --crlf, --upload-file (SMTP) +# 2022-04-10: added options to deploy cURL executable, no separate rules for curl and curl executable anymore +# windows/linux summarized under on option, no separate WATO rules per OS necessary anymore +# 2022-04-26: added check option clickable_url +# clarified option 'Don\'t verify certificate/pub key', 'Don\'t stop at verify errors (certificate/pub key)' +# + +import ipaddress +from cmk.gui.i18n import _ +from cmk.gui.exceptions import MKUserError +from cmk.gui.valuespec import ( + Dictionary, + ListOf, + CascadingDropdown, + TextUnicode, + FixedValue, + Integer, + Tuple, + DropdownChoice, + MonitoringState, + ListOfStrings, + TextInput, + Checkbox, + Transform, + CAorCAChain, + Optional, + Foldable, + Age, + # Url, + UploadOrPasteTextFile, + Alternative, +) +from cmk.gui.plugins.wato.utils import ( + rulespec_registry, + HostRulespec, + RulespecGroupCheckParametersOperatingSystem, + CheckParameterRulespecWithItem, +) +from cmk.gui.plugins.wato.utils import ( + PasswordFromStore, +) + +from cmk.gui.cee.plugins.wato.agent_bakery.rulespecs.utils import ( + RulespecGroupMonitoringAgentsAgentPlugins, +) + +bakery_plugin_version = '20220426.v0.0.8' + +# unsafe characters https://www.tutorialspoint.com/html/html_url_encoding.htm +forbidden_chars = '"<>#%{}|\^~[]` \'' + +_limits_transform = [ + ('time_namelookup', 'Time name lookup', 'ms', ), + ('time_connect', 'Time connect', 'ms', ), + ('time_appconnect', 'Time app connect', 'ms', ), + ('time_pretransfer', 'Time pre transfer', 'ms', ), + ('time_redirect', 'Time redirect', 'ms', ), + ('time_starttransfer', 'Time start transfer', 'ms', ), + ('time_total', 'Time Total', 'ms', ), +] + +_limits_no_transform = [ + ('cert_time_left', 'Certificate life time', 'Day(s)', ), + ('num_connects', '# of connects', 'count', ), + ('num_redirects', '# of redirects', 'count', ), + ('num_headers', '# of headers', 'count', ), + ('size_download', 'Size download', 'bytes', ), + ('size_upload', 'Size upload', 'bytes', ), + ('size_header', 'Size header', 'bytes', ), + ('size_request', 'Size request', 'bytes', ), + ('speed_download', 'Speed download', 'bytes/s', ), + ('speed_upload', 'Speed upload', 'bytes/s', ), +] + +_curl_check_elements = [ + ('clickable_url', + FixedValue( + True, + title=_('Render clickable URLs for HTTP/HTTPS (see Inline help)'), + totext=_(''), + help=_('Needs "Escape HTML in service output" rule or in Global settings enabled to work.') + )), + ('state_item_not_found', + MonitoringState( + default_value=1, + title=_('State on item not found'), + help=_('Monitoring state if the item is not found in the agent data. Default is UNKNOWN.') + )), + ('state_http_result_not_200', + MonitoringState( + default_value=1, + title=_('State on HTTP result not OK'), + help=_('Monitoring state if the HTTP return code is not in 2xx or 3xx. Default is WARN.') + )), + ('http_error_code_to_ignore', + ListOfStrings( + title=_('HTTP error codes to ignore'), + allow_empty=False, + orientation='horizontal', + valuespec=Integer(size=3, minvalue=0, maxvalue=999), + )), + ('state_curl_result_not_0', + MonitoringState( + default_value=1, + title=_('State on cURL exit code not OK'), + help=_('Monitoring state if the exit code is not 0. Default is WARN.') + )), + ('curl_error_code_to_ignore', + ListOfStrings( + title=_('cURL error codes to ignore'), + allow_empty=False, + orientation='horizontal', + valuespec=Integer(size=3, minvalue=0, maxvalue=999), + )), + ('state_verify_sll_not_0', + MonitoringState( + default_value=1, + title=_('State on SSL verify not OK'), + help=_('Monitoring state if the SSL verify code is not 0. Default is WARN.') + )), + ('state_expected_str_not_found', + MonitoringState( + default_value=1, + title=_('State on expected string not found'), + help=_('Monitoring state if one expected string is not found in the cURL output. Default is WARN.') + )), + ('state_header_str_not_found', + MonitoringState( + default_value=1, + title=_('State on expected header not found'), + help=_('Monitoring state if one expected header string is not found in the cURL output. Default is WARN.') + )), + ('state_for_regex', + Tuple( + title=_('State for regex pattern match'), + elements=[ + MonitoringState( + default_value=0, + title=_('State on regex pattern match'), + ), + MonitoringState( + default_value=1, + title=_('State on regex pattern don\'t match'), + ), + MonitoringState( + default_value=0, + title=_('State on regex pattern match info missing'), + ), + ], + )), + ('show_additional_info', + Tuple( + title=_('Show additional info'), + help=_('Shows RAW data from cURL in the service details.'), + elements=[ + Checkbox('Request headers'), + Checkbox('Response headers'), + Checkbox('(TLS/SSL) session info'), + Checkbox('RAW data'), + ], + )), + ('max_age', + Tuple( + title=_('Maximum age'), + elements=[ + Age( + title=_('Warning at'), + help=_('Warn, if the age of the page is older than this'), + default_value=3600 * 24, + ), + Age( + title=_('Critical at'), + help=_('Critical, if the age of the page is older than this'), + default_value=3600 * 24 * 4, + ), + MonitoringState( + default_value=0, + title=_('State if not available'), + help=_( + 'Monitoring state if the "Last-Modified" header is not available in the agent data. Default is OK.' + ) + ), + ] + )), +] + + +def _get_tuple_upper(_unit: str) -> Tuple: + return Tuple( + title=_('Upper limits'), + orientation='horizontal', + elements=[ + Integer(title=_('Warning at'), minvalue=0, unit=_(_unit), size=10), + Integer(title=_('Critical at'), minvalue=0, unit=_(_unit), size=10), + ], + ) + + +def _get_tuple_lower(_unit): + return Tuple( + title=_('Lower limits'), + orientation='horizontal', + elements=[ + Integer(title=_('Warning below'), minvalue=0, unit=_(_unit), size=10), + Integer(title=_('Critical below'), minvalue=0, unit=_(_unit), size=10), + ], + ) + + +for key, label, unit in _limits_transform: + _curl_check_elements.append( + (key, + Dictionary( + title=_(label), + elements=[ + ('upper', + Transform( + _get_tuple_upper(unit), + # run after read from rules.mk before show in wato + forth=lambda elems: (int((elems[0] * 1000)), int((elems[1] * 1000))), + # run before write to rule.mk after input in wato + back=lambda elems: (float((elems[0] / 1000)), float((elems[1] / 1000))), + )), + ('lower', + Transform( + _get_tuple_lower(unit), + forth=lambda elems: (int((elems[0] * 1000)), int((elems[1] * 1000))), + back=lambda elems: (float((elems[0] / 1000)), float((elems[1] / 1000))), + )), + ], + )) + ) + +for key, label, unit in _limits_no_transform: + _curl_check_elements.append( + (key, + Dictionary( + title=_(label), + elements=[ + ('upper', _get_tuple_upper(unit)), + ('lower', _get_tuple_lower(unit)), + ], + )) + ) + + +def _valuespec_curl(): + return Dictionary( + elements=_curl_check_elements, + ) + + +rulespec_registry.register( + CheckParameterRulespecWithItem( + check_group_name='curl', + group=RulespecGroupCheckParametersOperatingSystem, + parameter_valuespec=_valuespec_curl, + title=lambda: _('cURL'), + match_type='dict', + item_spec=lambda: TextUnicode(title=_('cURL service name'), ), + )) + +# ######################################################################################################### +# +# cURL bakery options +# +# ######################################################################################################### + +_option_curl_service = ('curl_service', + Tuple( + elements=[ + TextUnicode( + title=_('Service name'), + help=_('Name for the discovered service, for example www.example.com'), + allow_empty=False, + placeholder='your.service.name', + forbidden_chars=forbidden_chars, + ), + TextUnicode( # ToDo: change to URL? + title=_('URL to check'), + help=_('URL to check with cURL, for example https://www.example.com'), + allow_empty=False, + size=90, + placeholder='https://www.example.com', + # forbidden_chars=forbidden_chars, + ), + ], + orientation='horizontal', + )) + + +def _transform_forth_verify_remote_host(params): + if type(params) == tuple: + if len(params) == 4: # added 2022-03-23 + params = (params[0], params[1], params[2], params[3], None, None, None) + return params + + +_option_verify_remote_host = ('cert_verify', + Transform( + Tuple( + title='Configure verification of remote host (certificate/pub key)', + elements=[ + # Checkbox('Don\'t verify certificate/pub key'), + Checkbox('Don\'t stop at verify errors (certificate/pub key)'), + Checkbox('Use OCSP to check certificate status'), + Checkbox('Disable cert revocation checks (WinSSL)'), + Optional(Foldable(CAorCAChain()), label='Certificate to verify against', ), + Optional(TextUnicode(size=40, minlen=32, maxlen=32, regex='[0-9a-fA-F]', ), + label='Expected MD5 hash of pub key'), + Optional(TextUnicode(size=60, allow_empty=False), + label='Expected SHA256 hash of pub key'), + Optional(Foldable(UploadOrPasteTextFile(title='Public key'), ), + label='Expected public key'), + ] + ), + forth=_transform_forth_verify_remote_host + )) + +_option_redirects = ('redirects', + Tuple( + title='Configure redirects', + elements=[ + Checkbox('Follow redirects'), + Checkbox('Use authentication on redirects'), + Optional(Integer(minvalue=-1, default_value=10), label='Max. redirects', sameline=True) + ] + )) + +_option_regex_response = ('regex_response', + Tuple( + title=_('Regular expression to expect in content'), + elements=[ + TextUnicode( + label=_('Regular expression'), + placeholder=_('If empty regex search will be disabled') + ), + Checkbox('Case insensitive'), + Checkbox('Multiline string matching'), + ] + )) + +_option_advanced_settings = ('advanced_settings', + Tuple( + title='Advanced settings', + elements=[ + Checkbox('Allow SSL beast security flaw to improve interoperability'), + Checkbox('Convert LF to CRLF in upload'), + Checkbox('Disable Application Layer Protocol Negotiation (ALPN)'), + Checkbox('Disable buffering of the output stream'), + Checkbox('Disable Next Protocol Negotiation (NPN)'), + Checkbox('Disable SSL session-ID reusing'), + Checkbox('Disable TCP keep alive on the connection'), + Checkbox('Do not squash .. sequences in URL path'), + Checkbox('Use TCP fast open option'), + Checkbox('Use TCP no delay option'), + ] + )) + +_options_get_header_only = ('get_header_only', + FixedValue( + '--head', + title=_('Get header only'), + totext=_('Only headers will be downloaded'), + help=_('cURL will will only download headers. Implements the "--head" option'), + )) +_url_get_header_only = ('get_header_only', + DropdownChoice( + title=_('Get Header only'), + choices=[ + ('--head', _('Get header only')), + ('', _('Get the hole document')), + ], + )) + +_option_auth_user = ('user_auth', + _('Username/Password'), + Tuple( + title=_('Configure user authentication'), + help=_( + 'The password entered here is stored in plain text within the monitored host. ' + 'This is needed because the agent plugin needs to have access to the unencrypted ' + 'password to authenticate with the server.' + ), + elements=[ + TextUnicode( + title=_('Username'), + allow_empty=False, + forbidden_chars=forbidden_chars, + placeholder='username', + ), + PasswordFromStore( + title=_('Password of the user'), + allow_empty=False, + ), + DropdownChoice( + title=_('Authentication method'), + choices=[ + ('', _('Use cURL default')), + ('--basic', _('Basic authentication')), + ('--digest', _('Digest authentication')), + ('--ntlm', _('NTLM authentication')), + ('--ntlm-wb', _('NTLM authentication with winbind')), + ('--negotiate', _('HTTP Negotiate (SPNEGO) authentication')), + ('--anyauth', _('Any authentication')), + ]), + ], + )) +_option_auth_priv_key = ('priv_key_auth', + _('Private/public key'), + Tuple( + elements=[ + TextUnicode( + title=_('Username'), + allow_empty=False, + forbidden_chars=forbidden_chars, + placeholder='username', + ), + PasswordFromStore( + title=_('Pass phrase'), + allow_empty=False, + ), + Foldable(UploadOrPasteTextFile(title='Private key', file_title='Private key (PEM)'), + title='Private key'), + ] + )) +_option_auth = ('user_auth', + CascadingDropdown( + title=_('Configure authentication'), + sorted=False, + choices=[ + _option_auth_user, + _option_auth_priv_key, + (None, _('No authentication')), + ], + )) + +_option_proxy_server = ('proxy_server', + Tuple( + title=_('Proxy server'), + show_titles=False, + elements=[ + DropdownChoice( + title=_('Protocol'), + choices=[ + ('--proxy', _('HTTP')), + ('--socks4', _('SOCKS4')), + ('--socks4a', _('SOCKS4a')), + ('--socks5', _('SOCKS5')), + ('--socks5-hostname', _('SOCKS5 hostname')), + ]), + TextUnicode( + label=_('Server'), + help=_('Name or IP-address of the proxy server.'), + allow_empty=False, + size=40, + placeholder='your.proxy.server', + forbidden_chars=forbidden_chars, + ), + Integer( + label=_('Port'), + default_value=3128, + minvalue=1, + maxvalue=65565, + ), + ], + orientation='horizontal', + )) + +_option_proxy_auth = ('proxy_auth', + Tuple( + title=_('Proxy authentication'), + help=_( + 'The password entered here is stored in plain text within the monitored host. ' + 'This is needed because the agent plugin needs to have access to the unencrypted ' + 'password to authenticate with the proxy.' + ), + elements=[ + TextUnicode( + title=_('Proxy username'), + allow_empty=False, + forbidden_chars=forbidden_chars, + placeholder='proxyusername', + ), + PasswordFromStore( + title=_('Password of the user'), + allow_empty=False, + ), + DropdownChoice( + title=_('Authentication method'), + choices=[ + ('--proxy-basic', _('Basic authentication')), + ('--proxy-digest', _('Digest authentication')), + ('--proxy-ntlm', _('NTLM authentication')), + ('--proxy-negotiate', _('HTTP Negotiate (SPNEGO) authentication')), + ('--proxy-anyauth', _('Any authentication')), + ('--socks5-basic', _('SOCKS5 basic authentication')), + ]), + ], + )) + +_options_proxy = ('http_proxy', + Alternative( + title=_('Configure proxy server'), + elements=[ + Dictionary( + title='Use proxy', + elements=[ + _option_proxy_server, + _option_proxy_auth, + ], + required_keys=['proxy_server'], + ), + FixedValue('--noproxy', title=_('Don\'t use any proxy'), totext=_('')), + FixedValue(None, title=_('Don\'t configure an proxy (use system settings)'), totext=_('')), + ], + )) + +_option_tls_ssl_version = ('tls_ssl_version', + DropdownChoice( + title=_('Use TLS/SSL version'), + choices=[ + ('', _('cURL default')), + ('--ssl', _('Try TLS/SSL')), + ('--ssl-reqd', _('Require TLS/SSL')), + ('--tlsv1.3', _('Use TLS 1.3')), + ('--tlsv1.2', _('Use TLS 1.2')), + ('--tlsv1.1', _('Use TLS 1.1')), + ('--tlsv1.0', _('Use TLS 1.0')), + ('--tlsv1', _('Use TLS1.0 or greater')), + ('--sslv3', _('Use SSLv3')), + ('--sslv2', _('Use SSLv2')), + ]),) + +_option_http_version = ('http_version', + DropdownChoice( + title=_('Use HTTP version'), + choices=[ + ('', _('cURL preferred version')), + ('--http2', _('Use HTTP/2')), + ('--http1.1', _('Use HTTP 1.1')), + ('--http1.0', _('Use HTTP 1.0')), + ]),) + + +def _transform_forth_address_resolution(params): + if not type(params) == dict: + if params == '': + params = None + params = {'dns_resolve_names': params} + return params + + +def _validate_ipaddress(pattern, varprefix): + if pattern: + try: + ipaddress.ip_address(pattern) + except ValueError: + raise MKUserError(varprefix, _(f'{pattern} is not a valid IP address')) + + +_option_address_resolution = ('ip_address_resolution', + Transform( + Foldable( + Dictionary( + title=_('DNS options'), + elements=[ + ('dns_resolve_names', DropdownChoice( + title=_('Resolve names'), + choices=[ + (None, _('IPv4/IPv6')), + ('--ipv4', _('IPv4 only')), + ('--ipv6', _('IPv6 only')), + ])), + ('dns_source_interface', TextUnicode( + title=_('Source interface'), + regex='[0-9a-zA-Z]', + size=15, + )), + ('dns_source_ipv4', TextUnicode( + title=_('IPv4 source address'), + validate=_validate_ipaddress, + size=15, + )), + ('dns_source_ipv6', TextUnicode( + title=_('IPv6 source address'), + validate=_validate_ipaddress, + size=42 + )), + ('dns_servers', ListOfStrings( + title=_('DNS servers'), + valuespec=TextInput(validate=_validate_ipaddress, size=42), + orientation='horizontal', + + )), + ], + ), + forth=_transform_forth_address_resolution + ), + title=_('Set DNS options'), + )) + +_option_ftp_settings = ('ftp_settings', + Foldable( + Dictionary( + title=_('FTP/SCP/SFTP options'), + elements=[ + ('ftp_account', TextUnicode(title=_('Account data string')),), + ('ftp_alternate_to_user', TextUnicode(title=_('String to replace USER command'))), + ('ftp_create_dirs', FixedValue( + True, + title=_('Create remote dir(s)'), + totext=_('enabled') + )), + ('ftp_change_cwd_method', DropdownChoice( + title=_('Change working directory method'), + choices=[ + ('multicwd', 'one CD for each directory'), + ('nocdw', 'No CD. Use full path in SIZE, RETR, STOR etc.'), + ('singlecwd', 'use one CD with full path') + ] + )), + ('ftp_mode', + CascadingDropdown( + title=_('Passive/Actrive mode'), + sorted=False, + choices=[ + ('ftp_pass', + _('FTP passive mode'), + Tuple( + elements=[ + Checkbox(label=_('Don\'t send EPSV command')), + Checkbox(label=_('Send PRET before PASV')), + Checkbox( + label=_('Use remote IP form control channel for data chanel')) + ] + )), + ('ftp_active', + _('FTP active mode'), + Tuple( + elements=[ + Checkbox(label=_('Don\'t send EPRT command')), + TextUnicode( + label=_('Address to use'), + help=_( + 'Can be the interface name ie "eth0", a exact ip address, a ' + 'hostname/FQDN or "-" to use the same address used for the ' + 'control connection' + ), + default_value='-', + regex='[0-9a-zA-Z\\.:\\-_]', + ), + ] + )) + ] + )), + ('ftp_ssl_control', + Tuple( + title=_('Require SSL/TLS for FTP login'), + elements=[ + Checkbox(label=_('Send CCC after authenticating')), + DropdownChoice( + label=_('Set CCC mode'), + choices=[ + ('active', 'Active'), + ('passive', 'Passive'), + ] + ) + ] + )), + ('compressed_ssh', FixedValue( + True, + title=_('Enable ssh compression'), + totext=_('enabled'), + )), + ('list_only', FixedValue( + True, + title=_('Enable list only'), + totext=_('enabled'), + )), + ('use_ascii', FixedValue( + True, + title=_('Enable ASCII transfer'), + totext=_('enabled'), + )), + ], + ), + title=_('Set FTP/SCP/SFTP options'), + )) + +_option_expected_strings = ('expected_strings', + ListOfStrings( + title=_('Strings to expect in response'), + # orientation='horizontal', + allow_empty=False, + valuespec=TextInput(allow_empty=False, regex='[a-zA-Z0-9\.]'), + )) +_url_expected_strings = ('expected_strings', + Alternative( + title=_('Override strings to expect in response'), + elements=[ + _option_expected_strings[1], + FixedValue(None, title=_('Don\'t expect any strings in the response'), totext=_('')), + ], + )) + +_option_header_strings = ('header_strings', + ListOfStrings( + title=_('Strings to expect in header'), + # orientation='horizontal', + allow_empty=False, + valuespec=TextInput(allow_empty=False, regex='[a-zA-Z0-9\\.]'), + )) +_url_header_strings = ('header_strings', + Alternative( + title=_('Override strings to expect in header'), + elements=[ + _option_expected_strings[1], + FixedValue(None, title=_('Don\'t expect any strings in the header'), totext=_('')), + ], + )) + +_option_request_headers = ('request_headers', + ListOf( + Tuple( + title=_('Set headers'), + orientation='horizontal', + elements=[ + TextUnicode( + label=_('Header'), + allow_empty=False, + placeholder='X-your-header', + regex='[a-zA-Z0-9_\\-]', + # size=30, + ), + TextUnicode( + label=_('Value'), + # allow_empty=False, + placeholder='value of header', + # regex='[a-zA-Z0-9_ :;.,=<>#\\-@\\+\\*\'/\\?!\\(\\)\\{\\}\\[\\]\\$\\&~\\^%|"`\\]', + size=50, + empty_text=';' + ), + ] + ), + allow_empty=False, + title=_('Set headers'), + add_label=_('Add header'), + movable=True, + )) +_url_request_headers = ('request_headers', + Alternative( + title=_('Override default headers'), + elements=[ + _option_request_headers[1], + FixedValue(None, title=_('Don\'t configure request headers'), totext=_('')) + ] + )) + +_option_mail_settings = ('mail_settings', + Foldable( + Dictionary( + title=_('SMTP settings'), + elements=[ + ('mail_from', TextUnicode( + title=_('Mail from address'), + allow_empty=False, + )), + ('mail_rcpt', ListOfStrings( + title=_('Mail to address'), + allow_empty=False, + max_entries=5, + )), + ('mail_auth', TextUnicode( + title=_('Mail originator address'), + )), + # ('oauth2_header', TextUnicode( + # title=_('Oauth2 token'), + # )), + ('request', TextUnicode( + title=_('REQUEST command'), + help=_('Send this command instead of LIST (POP3/IMAP) or HELP/VRFY (SMTP).') + )), + _option_request_headers, + ('message', UploadOrPasteTextFile( + title=_('Message to send'), + )), + ('mail_rpct_allowfail', FixedValue( + True, + title=_('Allow some mail to addresses to fail'), + totext=_('enabled') + )) + ] + ), + title=_('Set SMTP options'), + )) + +_option_api_key_header = ('api_key_header', + Tuple( + title=_('Set API key header'), + help=_( + 'The password entered here is stored in plain text within the monitored host. ' + 'This is needed because the agent plugin needs to have access to the unencrypted ' + 'password to authenticate with the server.' + ), + elements=[ + TextUnicode( + title=_('API Key header'), + allow_empty=False, + forbidden_chars='|"', + placeholder='X-API-Key: ', + ), + PasswordFromStore( + title=_('API Key'), + allow_empty=False, + ), + ], + )) +_url_api_key_header = ('api_key_header', + Alternative( + title=_('Set API key header'), + elements=[ + _option_api_key_header[1], + FixedValue(None, title=_('Don\'t configure an API key header'), totext=_('')), + ], + )) + +_option_limit_rate = ('limit_rate', + Tuple( + title=_('Maximum UP-/Download rate'), + # show_titles=False, + elements=[ + Integer( + label=_('Speed'), + # default_value=3128, + minvalue=1, + # maxvalue=65565, + ), + DropdownChoice( + default_value='M', + choices=[ + ('B', _('Byte/s')), + ('K', _('KByte/s')), + ('M', _('MByte/s')), + ('G', _('GByte/s')), + ]), + ], + orientation='horizontal', + )) +_url_limit_rate = ('limit_rate', + Alternative( + title=_('Maximum UP-/Download rate'), + elements=[ + _option_limit_rate[1], + FixedValue(None, title=_('Don\'t configure a rate limit'), totext=_('')), + ], + )) + +_option_max_file_size = ('max_file_size', + Tuple( + title=_('Maximum file size'), + # show_titles=False, + elements=[ + Integer( + label=_('Size'), + # default_value=3128, + minvalue=1, + # maxvalue=65565, + ), + DropdownChoice( + # title=_('Unit'), + choices=[ + ('B', _('Byte')), + ('K', _('KByte')), + ('M', _('MByte')), + ('G', _('GByte')), + ]), + ], + orientation='horizontal', + )) +_url_max_file_size = ('max_file_size', + Alternative( + title=_('Maximum file size'), + elements=[ + _option_max_file_size[1], + FixedValue(None, title=_('Don\'t configure a file size limit'), totext=_('')), + ], + )) + +_option_max_time = ('max_time', + Integer( + title=_('Maximum transfer time'), + default_value=10, + minvalue=1, + unit='s', + )) +_url_max_time = ('max_time', + Alternative( + title=_('Maximum transfer time'), + elements=[ + _option_max_time[1], + FixedValue(None, title=_('Don\'t configure a transfer time limit'), totext=_('')), + ], + )) + +_option_speed_limit = ('speed_limit', + Tuple( + title=_('Minimum speed'), + # show_titles=False, + elements=[ + Integer( + label=_('Speed'), + minvalue=1, + ), + DropdownChoice( + default_value=1024, + choices=[ + (1, _('Byte/s')), + (1024, _('KByte/s')), + (1048576, _('MByte/s')), + (1073741824, _('GByte/s')), + ]), + ], + orientation='horizontal', )) +_url_speed_limit = ('speed_limit', + Alternative( + title=_('Minimum speed'), + elements=[ + _option_speed_limit[1], + FixedValue(None, title=_('Don\'t configure a lower speed limit'), totext=_('')), + ], + )) + +_option_speed_time = ('speed_time', + Integer( + title=_('Minimum speed time'), + default_value=30, + minvalue=1, + unit='s', )) +_url_speed_time = ('speed_time', + Alternative( + title=_('Minimum speed time'), + elements=[ + _option_speed_time[1], + FixedValue(None, title=_('Don\'t configure a minimum speed time limit'), totext=_('')), + ], + )) + +_option_connect_timeout = ('connect_timeout', + Integer( + title=_('Maximum time to connect'), + default_value=1, + minvalue=1, + unit='s', )) +_url_connect_timeout = ('connect_timeout', + Alternative( + title=_('Maximum time to connect'), + elements=[ + _option_connect_timeout[1], + FixedValue(None, title=_('Don\'t configure a maximum time to connect'), totext=_('')), + ], + )) + +_option_limits = ('limits', + Foldable( + Dictionary( + title=_('Limits'), + elements=[ + _option_limit_rate, + _option_max_file_size, + _option_connect_timeout, + _option_max_time, + _option_speed_limit, + _option_speed_time, + ] + ), + title=_('Set connection limits') + )) +_url_limits = ('limits', + Foldable( + Dictionary( + title=_('Limits'), + elements=[ + _url_limit_rate, + _url_max_file_size, + _url_connect_timeout, + _url_max_time, + _url_speed_limit, + _url_speed_time, + ] + ), + title=_('Override connection limits') + )) + +_option_user_agent = ('user_agent', + TextUnicode( + title=_('Set user agent'), + allow_empty=False, + placeholder='your user agent', + # forbidden_chars=forbidden_chars, + forbidden_chars='"|' + )) +_url_user_agent = ('user_agent', + CascadingDropdown( + title=_('Set user agent'), + sorted=False, + choices=[ + ('user_agent', _('Override default user agent'), + _option_user_agent[1],), + ('', _('Don\'t configure a user agent')), + ], + )) + +_option_referer = ('referer', + TextUnicode( + title=_('Set referer'), + allow_empty=False, + placeholder='http://your.referer.url/', + # forbidden_chars=forbidden_chars, + forbidden_chars='|" ', + )) +_url_referer = ('referer', + Alternative( + title=_('Override default referer'), + elements=[ + _option_referer[1], + FixedValue(None, title=_('Don\'t configure a referer'), totext=_('')) + ] + )) + +_options_compressed = ('compressed', + FixedValue( + '--compressed', + title=_('Request compressed response'), + totext=_('Request compressed response enabled'), + )) +_url_compressed = ('compressed', + DropdownChoice( + title=_('Request compressed response'), + choices=[ + ('--compressed', _('Request compressed response')), + ('', _('Don\'t Request compressed response')), + ], + )) + +_option_post = ('post_binary', + Tuple( + title=_('Send HTTP POST data'), + elements=[ + TextUnicode( + label=_('Content-Type'), + allow_empty=False, + forbidden_chars=forbidden_chars, + default_value='text/html' + ), + UploadOrPasteTextFile( + # title=_('HTTP POST data'), + allow_empty=False, + default_value=_( + 'This posts data exactly as specified with no extra processing whatsoever.\n\n' + 'To disable HTTP POST data in per URL settings leave this empty.' + ), + ), + ] + )) + +_option_url_settings = ('url_settings', + Foldable( + Dictionary( + title=_('Per URL settings'), + elements=[ + _option_verify_remote_host, + _options_proxy, + _option_redirects, + _option_auth, + _url_get_header_only, + _option_regex_response, + _url_compressed, + _option_post, + _url_api_key_header, + _url_referer, + _url_request_headers, + _url_user_agent, + _url_header_strings, + _url_expected_strings, + _url_limits, + _option_address_resolution, + _option_ftp_settings, + _option_mail_settings, + _option_tls_ssl_version, + _option_http_version, + _option_advanced_settings, + ], + ), + title=_('Override default settings'), + )) + + +def _validate_service_names(pattern, varprefix): + service_names = [] + + for url in pattern: + service_names.append(url['curl_service'][0]) + duplicates = [service_name for service_name in service_names if service_names.count(service_name) > 1] + duplicates = list(set(duplicates)) + if duplicates: + raise MKUserError( + varprefix, + _(f'There are duplicate service names. Please check the following service names: {", ".join(duplicates)}') + ) + + +def _option_url_transform_curl_service(params): + # transform form separate dict entries to tuple + # added in version 20220314.v0.1.0 + # {'url': 'https://excample.com', 'service_name': 'example'} + # {'curl_service': ('example', 'https://eaxample.com')} + if type(params) == dict: + try: + url = params['url'] + service_name = params['service_name'] + params.pop('url'), + params.pop('service_name') + params.update({'curl_service': (service_name, url)}) + except KeyError: + pass + return params + + +_option_url = ('url_list', + Foldable( + ListOf( + Transform( + Dictionary( + elements=[ + _option_curl_service, + _option_url_settings, + ], + required_keys=['curl_service', ], + ), + forth=_option_url_transform_curl_service + ), + add_label=_('Add URL'), + movable=True, + title=_('URLs to check'), + allow_empty=False, + validate=_validate_service_names, + ))) + +_option_default_settings = ('default_settings', + Foldable( + Dictionary( + title=_('Plugin settings'), + elements=[ + _option_verify_remote_host, + _options_proxy, + _option_redirects, + _option_auth, + _options_get_header_only, + _option_regex_response, + _options_compressed, + _option_post, + _option_api_key_header, + _option_referer, + _option_request_headers, + _option_user_agent, + _option_header_strings, + _option_expected_strings, + _option_limits, + _option_address_resolution, + _option_ftp_settings, + _option_mail_settings, + _option_http_version, + _option_tls_ssl_version, + _option_advanced_settings, + ], + ), + title='Default setting', + )) + +_option_plugin_interval = ('interval', + Integer( + title=_('Plugin run interval'), + minvalue=1, + unit=_('min'), + # default_value=1, + help=_( + 'This is the interval at witch the plugin runs. If not set the plugin will ' + 'run with every cycle of the agent (By default every 1 minute).' + ), + ),) + +_option_plugin_timeout = ('timeout', + Integer( + title=_('Plugin timeout'), + minvalue=1, + unit=_('min'), + # default_value=300, + help=_( + 'This is the maximum run time for the plugin. If not set the timeout for ' + 'the plugin is 1 minute by default.' + ), + ),) + +_option_curl_executable = ('curl_executable', + DropdownChoice( + title=_('cURL executable to use'), + help=_( + 'By default this plugin will use the system provided cURL executable. You can ' + 'decide to deploy a separate cURL executable. If you do so you need to install the ' + 'optional CMK package curl_executable<version>.mkp.' + ), + choices=[ + (None, _('Use system provided cURL executable')), + ('64bit', _('Deploy 64bit cURL version')), + ('32bit', _('Deploy 32bit cURL version')), + ] + )) + + +def _transform_forth_agent_config_curl(params): + # transform added 20220410 on removing os specific rules + if type(params) == tuple: + if params[0] in ['linux', 'windows', 'deploy']: + params = params[1] + else: + params = None + return params + + +def _valuespec_agent_config_curl(): + return Transform( + Alternative( + title=_('cURL'), + elements=[ + Dictionary( + title=_('Deploy cURL agent plugin'), + elements=[ + _option_url, + _option_plugin_interval, + _option_plugin_timeout, + _option_curl_executable, + _option_default_settings, + ], + required_keys=['url_list'], + ), + FixedValue( + None, + title=_('Do not deploy the cURL agent plugin'), + totext=_('The cURL agent plugin will not be deployed') + ), + ], + ), + forth=_transform_forth_agent_config_curl, + ) + + +rulespec_registry.register( + HostRulespec( + group=RulespecGroupMonitoringAgentsAgentPlugins, + name='agent_config:curl', + valuespec=_valuespec_agent_config_curl, + ) +) diff --git a/packages/curl b/packages/curl index 84453ca91784da2657f9a96f69432ed52986a965..24e5b6d3f89ec1f6169ba16f3129e7401b7bcb6a 100644 --- a/packages/curl +++ b/packages/curl @@ -22,11 +22,10 @@ 'download_url': 'https://thl-cmk.hopto.org/gitlab/checkmk/vendor-independent/curl', 'files': {'agent_based': ['curl.py'], 'agents': ['bakery/curl.py', 'plugins/curl.sh', 'plugins/curl.ps1'], - 'web': ['plugins/wato/curl.py', 'plugins/metrics/curl.py']}, + 'gui': ['metrics/curl.py', 'wato/curl.py']}, 'name': 'curl', - 'num_files': 6, 'title': 'cURL agent plugin', - 'version': '20220515.v0.1.8c', - 'version.min_required': '2.0.0', - 'version.packaged': '2021.09.20', + 'version': '0.1.9-20230607', + 'version.min_required': '2.1.0b1', + 'version.packaged': '2.1.0p21', 'version.usable_until': None} \ No newline at end of file