Initial commit.

This commit is contained in:
Marsell Kukuljevic 2024-08-24 12:26:38 +02:00
parent 52f417215b
commit bd7027e93b
7 changed files with 409 additions and 0 deletions

View File

@ -0,0 +1,99 @@
#!/usr/bin/env python3
# Copyright (C) 2024 Spearhead Systems SRL
import json
from datetime import datetime, timezone
from cmk.base.plugins.agent_based.agent_based_api.v1 import register, Result, Service, State
# Convert JSON entries into dictionaries indexed by name. We're assuming here
# that the name is unique across AZs and resource groups. If not, add the
# 'location' and 'resource_group' fields in each object to the name.
def parse(string_table):
lookup = {}
for json_str in string_table:
obj = json.loads(json_str)
name = obj["name"]
group = obj["resource_group"]
lookup[f"{name}#{resource_group}"] = obj
return lookup
# Produce a list of Azure objects for discovery.
def discover(section):
for name, details in sorted(section.items()):
yield Service(item=name)
# Given a specific metric, look it up in the parsed output, and produce
# results on that service based upon the metric's range.
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)
register.agent_section(
name="azure_keyvault_metrics",
parse_function=parse
)
register.check_plugin(
name="azure_keyvault_metrics",
service_name="Azure Keyvault Metric %s",
check_function=check_keyvault,
check_default_parameters={},
check_ruleset_name="azure_keyvault_metrics",
discovery_function=discover,
)
register.agent_section(
name="azure_firewall_metrics",
parse_function=parse
)
register.check_plugin(
name="azure_firewall_metrics",
service_name="Azure Firewall Metric %s",
check_function=check_keyvault,
check_default_parameters={},
check_ruleset_name="azure_firewall_metrics",
discovery_function=discover,
)
register.agent_section(
name="azure_defender_alerts",
parse_function=parse
)
register.check_plugin(
name="azure_defender_alerts",
service_name="Azure Defender Alert %s",
check_function=check_keyvault,
check_default_parameters={},
check_ruleset_name="azure_defender_alerts",
discovery_function=discover,
)

View File

@ -0,0 +1,160 @@
#!/usr/bin/env python3
# Copyright (C) 2024 Spearhead Systems SRL
from urllib import request, parse
from datetime import datetime, timezone, timedelta
import json
import sys
import re
VAULT_METRICS = [
'Availability',
'SaturationShoebox',
'ServiceApiLatency',
'ServiceApiHit',
'ServiceApiResult',
]
FIREWALL_METRICS = [
'FirewallHealth',
'Throughput',
'FirewallLatencyPng',
]
REGION_RE = re.compile('/locations/(.+?)/')
RESOURCE_GROUP_RE = re.compile('/resourceGroups/(.+?)/')
def get_token(tenant, username, password):
data = parse.urlencode({
'username': username,
'password': password,
'grant_type': 'password',
'claims': '{"access_token": {"xms_cc": {"values": ["CP1"]}}}',
'scope': 'https://management.core.windows.net//.default offline_access openid profile',
'client_info': 1,
# This is actually the client ID of the Azure CLI tools
'client_id': '04b07795-8ddb-461a-bbee-02f9e1bf7b46',
})
req = request.Request(f'https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token',
data=str.encode(data))
res = request.urlopen(req)
token_data = json.loads(res.read())
token = token_data['access_token']
return token
def get_json(token, path, version='2023-07-01'):
url = f"https://management.azure.com{path}{'?' in path and '&' or '?'}api-version={version}"
req = request.Request(url, headers={'Authorization': f'Bearer {token}'})
res = request.urlopen(req)
data = json.loads(res.read())
return data['value']
def list_subscriptions(token):
return get_json(token, '/subscriptions')
def list_vaults(token, subscription):
return get_json(token, f'/subscriptions/{subscription}/resources?$filter=resourceType%20eq%20%27Microsoft.KeyVault%2Fvaults%27')
def list_firewalls(token, subscription):
return get_json(token, f'/subscriptions/{subscription}/resources?$filter=resourceType%20eq%20%27Microsoft.Network%2FazureFirewalls%27')
def list_defender_alerts(token, subscription):
return get_json(token, f'/subscriptions/{subscription}/providers/Microsoft.Security/alerts', '2022-01-01')
def get_recent_metrics(token, path, metrics):
end = datetime.now()
start = end - timedelta(minutes=2)
start_str = start.isoformat().split('.')[0] + 'Z'
end_str = end.isoformat().split('.')[0] + 'Z'
metrics_str = ','.join(metrics)
return get_json(token, f'{path}/providers/microsoft.insights/metrics?metricnames={metrics_str}&timespan={start_str}/{end_str}', '2023-10-01')
def metrics_to_lookup(metrics):
lookup = {}
for metric in metrics:
name = metric['name']['value']
series = metric['timeseries']
if series:
value = series[0]['data'][-1]
key = next(filter(lambda foo: foo != 'timeStamp', value), None)
lookup[name] = value.get(key)
return lookup
def get_args(argv):
if len(argv) != 5 or not argv[1] in ['keyvault', 'firewall', 'defender']:
print(f"{sys.argv[0]} <command> <tenand ID> <username> <password>", file=sys.stderr)
print(f"Valid commands are: 'keyvault', 'firewall', 'defender'", file=sys.stderr)
exit(1)
return argv[1], argv[2], argv[3], argv[4]
def print_json(obj):
print(json.dumps(obj))
command, tenant, username, password = get_args(sys.argv)
token = get_token(tenant, username, password)
for subscription in list_subscriptions(token):
subscription_id = subscription['subscriptionId']
if command == 'defender':
for alert in list_defender_alerts(token, subscription_id):
properties = alert['properties']
status = properties['status']
if not status in ['Active', 'InProgress']:
continue
print_json({
'type': command,
'name': alert['name'],
'location': re.search(REGION_RE, alert['id'])[1],
'resource_group': re.search(RESOURCE_GROUP_RE, alert['id'])[1],
'alert': {
'status': status,
'severity': properties['severity'],
'url': properties['alertUri'],
'info': properties['alertDisplayName']
}
})
elif command == 'firewall':
for firewall in list_firewalls(token, subscription_id):
metrics = get_recent_metrics(token, firewall['id'], FIREWALL_METRICS)
print_json({
'type': command,
'name': firewall['name'],
'location': firewall['location'],
'resource_group': re.search(RESOURCE_GROUP_RE, firewall['id'])[1],
'metrics': metrics_to_lookup(metrics),
})
elif command == 'keyvault':
for vault in list_vaults(token, subscription_id):
metrics = get_recent_metrics(token, vault['id'], VAULT_METRICS)
print_json({
'type': command,
'name': vault['name'],
'location': vault['location'],
'resource_group': re.search(RESOURCE_GROUP_RE, vault['id'])[1],
'metrics': metrics_to_lookup(metrics),
})

View File

@ -0,0 +1 @@
agent_azure

View File

@ -0,0 +1 @@
agent_azure

View File

@ -0,0 +1 @@
agent_azure

View File

@ -0,0 +1,22 @@
#!/usr/bin/env python3
# Copyright (C) 2024 Spearhead Systems SRL
def get_params(params):
return params["tenant"], params["client"], params["secret"]
def agent_azure_keyvault_metrics(params, hostname, ipaddress):
tenant, client, secret = get_params(params)
return ["keyvault", tenant, client, secret]
def agent_azure_firewall_metrics(params, hostname, ipaddress):
tenant, client, secret = get_params(params)
return ["firewall", tenant, client, secret]
def agent_azure_defender_alerts(params, hostname, ipaddress):
tenant, client, secret = get_params(params)
return ["defender", tenant, client, secret]
special_agent_info["azure_keyvault_metrics"] = agent_azure_keyvault_metrics
special_agent_info["azure_firewall_metrics"] = agent_azure_firewall_metrics
special_agent_info["azure_defender_alerts"] = agent_azure_defender_alerts

View File

@ -0,0 +1,125 @@
#!/usr/bin/env python3
# Copyright (C) 2024 Spearhead Systems SRL
import copy
from cmk.gui.i18n import _
from cmk.gui.plugins.wato.utils import (
rulespec_registry,
HostRulespec,
IndividualOrStoredPassword,
RulespecGroupCheckParametersDiscovery,
CheckParameterRulespecWithItem,
RulespecGroupCheckParametersApplications,
)
from cmk.gui.watolib.rulespecs import Rulespec
from cmk.gui.valuespec import (
Dictionary,
TextInput,
Integer,
ListOfStrings,
Password
)
def _valuespec_special_agents_azure_discovery():
return Dictionary(
title=_("Azure Discovery"),
elements=[
(
"tenant",
TextInput(
title=_("Tenant ID / Directory ID"),
allow_empty=False,
size=45,
),
),
(
"client",
TextInput(
title=_("Client ID / Application ID"),
allow_empty=False,
size=45,
),
),
(
"secret",
IndividualOrStoredPassword(
# Password(
title=_("Client Secret"),
allow_empty=False,
size=45,
),
),
],
)
def _valuespec_special_agents_azure_keyvault_metric_check():
return Dictionary(
title=_("Azure Key Vault Metric Checks"),
optional_keys=["warn_percent", "crit_percent"],
elements=[
(
"warn_percent",
Integer(
minvalue=0,
default_value=98,
title=_("Warn when percentage falls below this threshold"),
),
),
(
"crit_percent",
Integer(
minvalue=0,
default_value=90,
title=_("Warn when percentage falls below this threshold"),
),
),
],
)
def _valuespec_special_agents_azure_firewall_metric_check():
return _valuespec_special_agents_azure_keyvault_metric_check()
rulespec_registry.register(
HostRulespec(
name="special_agents:azure_keyvault_metrics",
group=RulespecGroupCheckParametersDiscovery,
match_type='dict',
valuespec=_valuespec_special_agents_azure_discovery,
)
)
rulespec_registry.register(
HostRulespec(
name="special_agents:azure_firewall_metrics",
group=RulespecGroupCheckParametersDiscovery,
match_type='dict',
valuespec=_valuespec_special_agents_azure_discovery,
)
)
rulespec_registry.register(
HostRulespec(
name="special_agents:azure_defender_alerts",
group=RulespecGroupCheckParametersDiscovery,
match_type='dict',
valuespec=_valuespec_special_agents_azure_discovery,
)
)
rulespec_registry.register(
CheckParameterRulespecWithItem(
check_group_name="azure_keyvault_metric",
group=RulespecGroupCheckParametersApplications,
match_type="dict",
parameter_valuespec=_valuespec_special_agents_azure_keyvault_metric_check,
)
)
rulespec_registry.register(
CheckParameterRulespecWithItem(
check_group_name="azure_firewall_metric",
group=RulespecGroupCheckParametersApplications,
match_type="dict",
parameter_valuespec=_valuespec_special_agents_azure_keyvault_metric_check,
)
)