From 7fb9a07709956a773b99db101dc34baa700992ad Mon Sep 17 00:00:00 2001 From: Marsell Kukuljevic Date: Mon, 13 Oct 2025 10:32:45 +0200 Subject: [PATCH] Update Azure Keyvault to use the new plugin API in CheckMK 2.3+ --- .../plugins/agent_based/azure_keyvault.py | 0 .../agents/special/agent_azure_keyvault | 0 .../check_mk/checks/agent_azure_keyvault | 0 .../web/plugins/wato/azure_keyvault.py | 0 azure-keyvault/2.3/azure_keyvault-0.4.0.mkp | Bin 0 -> 2692 bytes .../agent_based/azure_keyvault.py | 71 +++++++++++++ .../libexec/agent_azure_keyvault | 26 +++++ .../azure_keyvault/rulesets/azure_keyvault.py | 95 ++++++++++++++++++ .../server_side_calls/special_agent.py | 27 +++++ 9 files changed, 219 insertions(+) rename azure-keyvault/{ => 2.2}/local/lib/check_mk/base/plugins/agent_based/azure_keyvault.py (100%) rename azure-keyvault/{ => 2.2}/local/share/check_mk/agents/special/agent_azure_keyvault (100%) rename azure-keyvault/{ => 2.2}/local/share/check_mk/checks/agent_azure_keyvault (100%) rename azure-keyvault/{ => 2.2}/local/share/check_mk/web/plugins/wato/azure_keyvault.py (100%) create mode 100755 azure-keyvault/2.3/azure_keyvault-0.4.0.mkp create mode 100644 azure-keyvault/2.3/local/lib/python3/cmk_addons/plugins/azure_keyvault/agent_based/azure_keyvault.py create mode 100755 azure-keyvault/2.3/local/lib/python3/cmk_addons/plugins/azure_keyvault/libexec/agent_azure_keyvault create mode 100644 azure-keyvault/2.3/local/lib/python3/cmk_addons/plugins/azure_keyvault/rulesets/azure_keyvault.py create mode 100644 azure-keyvault/2.3/local/lib/python3/cmk_addons/plugins/azure_keyvault/server_side_calls/special_agent.py diff --git a/azure-keyvault/local/lib/check_mk/base/plugins/agent_based/azure_keyvault.py b/azure-keyvault/2.2/local/lib/check_mk/base/plugins/agent_based/azure_keyvault.py similarity index 100% rename from azure-keyvault/local/lib/check_mk/base/plugins/agent_based/azure_keyvault.py rename to azure-keyvault/2.2/local/lib/check_mk/base/plugins/agent_based/azure_keyvault.py diff --git a/azure-keyvault/local/share/check_mk/agents/special/agent_azure_keyvault b/azure-keyvault/2.2/local/share/check_mk/agents/special/agent_azure_keyvault similarity index 100% rename from azure-keyvault/local/share/check_mk/agents/special/agent_azure_keyvault rename to azure-keyvault/2.2/local/share/check_mk/agents/special/agent_azure_keyvault diff --git a/azure-keyvault/local/share/check_mk/checks/agent_azure_keyvault b/azure-keyvault/2.2/local/share/check_mk/checks/agent_azure_keyvault similarity index 100% rename from azure-keyvault/local/share/check_mk/checks/agent_azure_keyvault rename to azure-keyvault/2.2/local/share/check_mk/checks/agent_azure_keyvault diff --git a/azure-keyvault/local/share/check_mk/web/plugins/wato/azure_keyvault.py b/azure-keyvault/2.2/local/share/check_mk/web/plugins/wato/azure_keyvault.py similarity index 100% rename from azure-keyvault/local/share/check_mk/web/plugins/wato/azure_keyvault.py rename to azure-keyvault/2.2/local/share/check_mk/web/plugins/wato/azure_keyvault.py diff --git a/azure-keyvault/2.3/azure_keyvault-0.4.0.mkp b/azure-keyvault/2.3/azure_keyvault-0.4.0.mkp new file mode 100755 index 0000000000000000000000000000000000000000..2fcc3bcdee68878331a6ec9ecd8492424a4501c7 GIT binary patch literal 2692 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 literal 0 HcmV?d00001 diff --git a/azure-keyvault/2.3/local/lib/python3/cmk_addons/plugins/azure_keyvault/agent_based/azure_keyvault.py b/azure-keyvault/2.3/local/lib/python3/cmk_addons/plugins/azure_keyvault/agent_based/azure_keyvault.py new file mode 100644 index 0000000..5c7809c --- /dev/null +++ b/azure-keyvault/2.3/local/lib/python3/cmk_addons/plugins/azure_keyvault/agent_based/azure_keyvault.py @@ -0,0 +1,71 @@ +#,!/usr/bin/env python3 +# Copyright (C) 2025 Spearhead Systems SRL - License: GNU General Public License v2 + +import json +from datetime import datetime, timezone +from cmk.agent_based.v2 import Result, Service, State, CheckPlugin, AgentSection + + +# Convert JSON entries into dictionaries indexed by certificate name. +def parse_keyvault(string_table): + raw_json = "" + cert_data = [] + + for row in string_table: + line = row[0] + raw_json += line + if line == "]": + cert_data.extend(json.loads(raw_json)) + raw_json = "" + + lookup = {} + for cert in cert_data: + lookup[cert["name"]] = cert + + return lookup + + +# Produce a list of certificates based on the parsed output. +def discover_keyvault(section): + for name, details in sorted(section.items()): + yield Service(item=name) + + +# Given a specific certificate, look it up in the parsed output, and produce +# results on that service based upon the certificate's expiry. +def check_keyvault(item, params, section): + warn_days = params.get("warn_days") + crit_days = params.get("crit_days") + + cert = section.get(item) + if cert is None: + return + + expires = datetime.fromisoformat(cert["attributes"]["expires"]) + now = datetime.now(timezone.utc) + remaining_days = (expires - now).days + + state = State.OK + if crit_days is not None and remaining_days < crit_days: + state = State.CRIT + elif warn_days is not None and remaining_days < warn_days: + state = State.WARN + + yield Result(state=state, summary="Expires in %d days" % remaining_days) + + +agent_section_azure_keyvault = AgentSection( + name="azure_keyvault", + parse_function=parse_keyvault, +) + +check_plugin_azure_keyvault = CheckPlugin( + name="azure_keyvault", + service_name="Azure Keyvault Certificate %s", + + check_function=check_keyvault, + check_default_parameters={}, + check_ruleset_name="azure_keyvault", + + discovery_function=discover_keyvault, +) diff --git a/azure-keyvault/2.3/local/lib/python3/cmk_addons/plugins/azure_keyvault/libexec/agent_azure_keyvault b/azure-keyvault/2.3/local/lib/python3/cmk_addons/plugins/azure_keyvault/libexec/agent_azure_keyvault new file mode 100755 index 0000000..c594dcd --- /dev/null +++ b/azure-keyvault/2.3/local/lib/python3/cmk_addons/plugins/azure_keyvault/libexec/agent_azure_keyvault @@ -0,0 +1,26 @@ +#!/bin/bash +# Copyright (C) 2023 Spearhead Systems SRL - License: GNU General Public License v2 + +az=/usr/bin/az + +set -euo pipefail + +if [ "$#" -lt 4 ]; then + echo "Usage: $0 ... [vaultN]" >&2 + exit 1 +fi + +tenant="$1" +user="$2" +password="$3" +vaults="${@:4}" + +echo "<<>>" + +"$az" login --service-principal --tenant="$tenant" --user="$user" --password="$password" > /dev/null + +for vault in $vaults; do + "$az" keyvault certificate list --vault-name="$vault" +done + +"$az" logout diff --git a/azure-keyvault/2.3/local/lib/python3/cmk_addons/plugins/azure_keyvault/rulesets/azure_keyvault.py b/azure-keyvault/2.3/local/lib/python3/cmk_addons/plugins/azure_keyvault/rulesets/azure_keyvault.py new file mode 100644 index 0000000..edd5e67 --- /dev/null +++ b/azure-keyvault/2.3/local/lib/python3/cmk_addons/plugins/azure_keyvault/rulesets/azure_keyvault.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python3 +# Copyright (C) 2025 Spearhead Systems SRL - License: GNU General Public License v2 + +from cmk.rulesets.v1 import Label +from cmk.rulesets.v1.form_specs import Dictionary, DictElement, String, Integer, Password, List, DefaultValue +from cmk.rulesets.v1.rule_specs import SpecialAgent, CheckParameters, HostCondition, Topic, Title, Help +from cmk.rulesets.v1.form_specs.validators import LengthInRange, NumberInRange + + +def _formspec_azure_keyvault_check(): + return Dictionary( + title = Title("Azure Key Vault Certificate Checks"), + elements = { + "warn_days": DictElement( + parameter_form = Integer( + title = Title("Certificate Days to Warn"), + help_text = Help("How many days to warn before a certificate in this key vault will expire"), + prefill = DefaultValue(30), + custom_validate = (NumberInRange(min_value=0),), + ), + ), + "crit_days": DictElement( + parameter_form = Integer( + title = Title("Certificate Days to Crit"), + help_text = Help("How many days to crit before a certificate in this key vault will expire"), + prefill = DefaultValue(3), + custom_validate = (NumberInRange(min_value=0),), + ), + ), + }, + ) + + +def _formspec_azure_keyvault_discovery(): + return Dictionary( + title = Title("Azure Key Vault Certificate Discovery"), + elements = { + "tenant": DictElement( + required = True, + parameter_form = String( + title = Title("Tenant ID / Directory ID"), + custom_validate = (LengthInRange(min_value=1),), + ), + ), + "client": DictElement( + required = True, + parameter_form = String( + title = Title("Client ID / Application ID"), + custom_validate = (LengthInRange(min_value=1),), + ), + ), + "secret": DictElement( + required = True, + parameter_form = Password( + title = Title("Client Secret"), + custom_validate = (LengthInRange(min_value=1),), + ), + ), + "vaults": DictElement( + required = True, + parameter_form = List( + title = Title("Keyvaults"), + add_element_label = Label("Add new keyvault"), + custom_validate = (LengthInRange(min_value=1),), + element_template = String(label=Label("Keyvault")), + ), + ), + }, + ) + + +rule_spec_agent_config_azure_keyvault = SpecialAgent( + topic = Topic.NETWORKING, + name = "azure_keyvault", + title = Title("Azure Keyvault"), + parameter_form = _formspec_azure_keyvault_discovery, +) + + +rule_spec_agent_config_azure_keyvault_check = CheckParameters( + topic = Topic.NETWORKING, + name = "azure_keyvault", + title = Title("Azure Keyvault Check"), + parameter_form = _formspec_azure_keyvault_check, + condition = HostCondition(), +) + +#check_plugin_juniper_trpz_cpu_util = CheckPlugin( +# name="juniper_trpz_cpu_util", +# service_name="CPU utilization", +# discovery_function=discovery_juniper_trpz_cpu_util, +# check_function=check_juniper_trpz_cpu_util, +# check_ruleset_name="cpu_utilization", +# check_default_parameters={"util": (80.0, 90.0)}, +#) diff --git a/azure-keyvault/2.3/local/lib/python3/cmk_addons/plugins/azure_keyvault/server_side_calls/special_agent.py b/azure-keyvault/2.3/local/lib/python3/cmk_addons/plugins/azure_keyvault/server_side_calls/special_agent.py new file mode 100644 index 0000000..bae5884 --- /dev/null +++ b/azure-keyvault/2.3/local/lib/python3/cmk_addons/plugins/azure_keyvault/server_side_calls/special_agent.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 +# Copyright (C) 2025 Spearhead Systems SRL - License: GNU General Public License v2 + +from cmk.server_side_calls.v1 import noop_parser, SpecialAgentConfig, SpecialAgentCommand + + +def agent_azure_keyvault(params, hostname): + tenant = params["tenant"] + client = params["client"] + secret = params["secret"] + + if type(secret) != str: + secret = secret.unsafe() + + args = [tenant, client, secret] + + for vault in params["vaults"]: + args.extend([vault.strip()]) + + yield SpecialAgentCommand(command_arguments=args) + + +special_agent_azure_keyvault = SpecialAgentConfig( + name = "azure_keyvault", + parameter_parser = noop_parser, + commands_function = agent_azure_keyvault, +)