[add] check:tls_certificate [add] check:generic_remote
This commit is contained in:
parent
d75bec75a9
commit
054208a79e
|
|
@ -26,7 +26,7 @@
|
|||
"checks.tls_certificate.not_obtainable": "TLS-Zertifikat nicht abrufbar; evtl. bereits ausgelaufen",
|
||||
"checks.tls_certificate.expires_soon": "TLS-Zertifikat läuft bald aus",
|
||||
"checks.generic_remote.overflow": "Laufwerk fast voll",
|
||||
"checks.http_request.request_failed": "HTTP-Abfrage fehlgeschlagen",
|
||||
"checks.http_request.request_failed": "HTTP-Abfrage fehlgeschlagen: {{reason}}",
|
||||
"checks.http_request.status_code_mismatch": "Status-Code {{status_code_actual}} stimmt nicht mit dem erwarteten Wert {{status_code_expected}} überein",
|
||||
"checks.http_request.header_missing": "Header '{{key}}' ist nicht gesetzt und hat somit nicht den erwarteten Wert '{{value_expected}}'",
|
||||
"checks.http_request.header_value_mismatch": "Header-Wert für Schlüssel '{{key}}' '{{value_actual}}' stimmt nicht mit erwartetem Wert '{{value_expected}}' überein",
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@
|
|||
"checks.tls_certificate.not_obtainable": "TLS certificate not obtainable; maybe already expired",
|
||||
"checks.tls_certificate.expires_soon": "TLS certificate will expire soon",
|
||||
"checks.generic_remote.overflow": "disk drive almost full",
|
||||
"checks.http_request.request_failed": "HTTP request failed",
|
||||
"checks.http_request.request_failed": "HTTP request failed: {{reason}}",
|
||||
"checks.http_request.status_code_mismatch": "actual status code {{status_code_actual}} does not match expected value {{status_code_expected}}",
|
||||
"checks.http_request.header_missing": "header '{{key}}' is unset and hence does not match the expected value '{{value_expected}}'",
|
||||
"checks.http_request.header_value_mismatch": "actual header value for key '{{key}}' '{{value_actual}}' does not match the expected value {{value_expected}}",
|
||||
|
|
|
|||
|
|
@ -1,254 +0,0 @@
|
|||
class implementation_check_kind_file_state(interface_check_kind):
|
||||
|
||||
'''
|
||||
[implementation]
|
||||
'''
|
||||
def parameters_schema(self):
|
||||
return {
|
||||
"type": "object",
|
||||
"additionalProperties": False,
|
||||
"properties": {
|
||||
"path": {
|
||||
"type": "string"
|
||||
},
|
||||
"exist_mode": {
|
||||
"description": "whether the file is supposed to exist or not",
|
||||
"type": "boolean",
|
||||
"default": True,
|
||||
},
|
||||
"exist_critical": {
|
||||
"description": "whether a violation of the extist state (parameter 'exist_mode') shall be considered as critical (true) or concerning (false)",
|
||||
"type": "boolean",
|
||||
"default": True,
|
||||
},
|
||||
"age_threshold_concerning": {
|
||||
"description": "in seconds; ignored if 'exist_mode' is set to false",
|
||||
"type": ["null", "integer"],
|
||||
"exclusiveMinimum": 0,
|
||||
"default": None,
|
||||
},
|
||||
"age_threshold_critical": {
|
||||
"description": "in seconds; ignored if 'exist_mode' is set to false",
|
||||
"type": ["null", "integer"],
|
||||
"exclusiveMinimum": 0,
|
||||
"default": None,
|
||||
},
|
||||
"size_threshold_concerning": {
|
||||
"description": "in bytes; ignored if 'exist_mode' is set to false",
|
||||
"type": ["null", "integer"],
|
||||
"exclusiveMinimum": 0,
|
||||
"default": None,
|
||||
},
|
||||
"size_threshold_critical": {
|
||||
"description": "in bytes; ignored if 'exist_mode' is set to false",
|
||||
"type": ["null", "integer"],
|
||||
"exclusiveMinimum": 0,
|
||||
"default": None,
|
||||
},
|
||||
|
||||
# deprecated
|
||||
"strict": {
|
||||
"deprecated": True,
|
||||
"description": "",
|
||||
"type": "boolean",
|
||||
"default": True,
|
||||
},
|
||||
"exist": {
|
||||
"deprecated": True,
|
||||
"description": "",
|
||||
"type": "boolean",
|
||||
"default": True,
|
||||
},
|
||||
"age_threshold": {
|
||||
"deprecated": True,
|
||||
"description": "",
|
||||
"type": ["null", "integer"],
|
||||
"exclusiveMinimum": 0,
|
||||
"default": None,
|
||||
},
|
||||
"size_threshold": {
|
||||
"deprecated": True,
|
||||
"description": "",
|
||||
"type": ["null", "integer"],
|
||||
"exclusiveMinimum": 0,
|
||||
"default": None,
|
||||
},
|
||||
},
|
||||
"required": [
|
||||
"path",
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
'''
|
||||
[implementation]
|
||||
'''
|
||||
def normalize_order_node(self, node):
|
||||
version = (
|
||||
"v1"
|
||||
if (not ("exist_mode" in node)) else
|
||||
"v2"
|
||||
)
|
||||
|
||||
if (version == "v1"):
|
||||
if ("path" not in node):
|
||||
raise ValueError("missing mandatory field 'path'")
|
||||
else:
|
||||
node_ = dict_merge(
|
||||
{
|
||||
"critical": True,
|
||||
"exist": True,
|
||||
"age_threshold": None,
|
||||
"size_threshold": None,
|
||||
},
|
||||
node
|
||||
)
|
||||
return {
|
||||
"exist_mode": node_["exist"],
|
||||
"exist_critical": node_["strict"],
|
||||
"age_threshold_concerning": (
|
||||
None
|
||||
if node_["strict"] else
|
||||
node_["age_threshold"]
|
||||
),
|
||||
"age_threshold_critical": (
|
||||
node_["age_threshold"]
|
||||
if node_["strict"] else
|
||||
None
|
||||
),
|
||||
"size_threshold_concerning": (
|
||||
None
|
||||
if node_["strict"] else
|
||||
node_["age_threshold"]
|
||||
),
|
||||
"size_threshold_critical": (
|
||||
node_["age_threshold"]
|
||||
if node_["strict"] else
|
||||
None
|
||||
),
|
||||
}
|
||||
elif (version == "v2"):
|
||||
if ("path" not in node):
|
||||
raise ValueError("missing mandatory field 'path'")
|
||||
else:
|
||||
node_ = dict_merge(
|
||||
{
|
||||
"exist_mode": True,
|
||||
"exist_critical": True,
|
||||
"age_threshold_concerning": None,
|
||||
"age_threshold_critical": None,
|
||||
"size_threshold_concerning": None,
|
||||
"size_threshold_critical": None,
|
||||
},
|
||||
node
|
||||
)
|
||||
return node_
|
||||
else:
|
||||
raise ValueError("unhandled")
|
||||
|
||||
|
||||
|
||||
'''
|
||||
[implementation]
|
||||
'''
|
||||
def run(self, parameters):
|
||||
condition = enum_condition.ok
|
||||
faults = []
|
||||
data = {}
|
||||
exists = _os.path.exists(parameters["path"])
|
||||
if (not parameters["exist_mode"]):
|
||||
if (exists):
|
||||
condition = (
|
||||
enum_condition.critical
|
||||
if parameters["exist_critical"] else
|
||||
enum_condition.concerning
|
||||
)
|
||||
faults.append(translation_get("checks.file_state.exists"))
|
||||
else:
|
||||
pass
|
||||
else:
|
||||
if (not exists):
|
||||
condition = (
|
||||
enum_condition.critical
|
||||
if parameters["exist_critical"] else
|
||||
enum_condition.concerning
|
||||
)
|
||||
faults.append(translation_get("checks.file_state.missing"))
|
||||
else:
|
||||
stat = _os.stat(parameters["path"])
|
||||
## age
|
||||
if True:
|
||||
timestamp_this = get_current_timestamp()
|
||||
timestamp_that = int(stat.st_atime)
|
||||
age = (timestamp_this - timestamp_that)
|
||||
if (age < 0):
|
||||
condition = enum_condition.critical
|
||||
faults.append(translation_get("checks.file_state.timestamp_implausible"))
|
||||
else:
|
||||
if (
|
||||
(parameters["age_threshold_critical"] is not None)
|
||||
and
|
||||
(age > parameters["age_threshold_critical"])
|
||||
):
|
||||
condition = enum_condition.critical
|
||||
faults.append(translation_get("checks.file_state.too_old"))
|
||||
else:
|
||||
if (
|
||||
(parameters["age_threshold_concerning"] is not None)
|
||||
and
|
||||
(age > parameters["age_threshold_concerning"])
|
||||
):
|
||||
condition = enum_condition.concerning
|
||||
faults.append(translation_get("checks.file_state.too_old"))
|
||||
else:
|
||||
pass
|
||||
data = dict_merge(
|
||||
data,
|
||||
{
|
||||
"timestamp_of_checking_instance": timestamp_this,
|
||||
"timestamp_of_file": timestamp_that,
|
||||
"age_value_in_seconds": age,
|
||||
"age_threshold_in_seconds_concerning": parameters["age_threshold_concerning"],
|
||||
"age_threshold_in_seconds_concerning": parameters["age_threshold_critical"],
|
||||
}
|
||||
)
|
||||
## size
|
||||
if True:
|
||||
size = stat.st_size
|
||||
if (size < 0):
|
||||
condition = enum_condition.critical
|
||||
faults.append(translation_get("checks.file_state.size_implausible"))
|
||||
else:
|
||||
if (
|
||||
(parameters["size_threshold_critical"] is not None)
|
||||
and
|
||||
(size > parameters["size_threshold_critical"])
|
||||
):
|
||||
condition = enum_condition.critical
|
||||
faults.append(translation_get("checks.file_state.too_big"))
|
||||
else:
|
||||
if (
|
||||
(parameters["size_threshold_concerning"] is not None)
|
||||
and
|
||||
(size > parameters["size_threshold_concerning"])
|
||||
):
|
||||
condition = enum_condition.concerning
|
||||
faults.append(translation_get("checks.file_state.too_big"))
|
||||
else:
|
||||
pass
|
||||
data = dict_merge(
|
||||
data,
|
||||
{
|
||||
"size_value_in_bytes": size,
|
||||
"size_threshold_in_bytes": parameters["size_threshold"],
|
||||
}
|
||||
)
|
||||
return {
|
||||
"condition": condition,
|
||||
"info": {
|
||||
"path": parameters["path"],
|
||||
"faults": faults,
|
||||
"data": data,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
class implementation_check_kind_script(interface_check_kind):
|
||||
|
||||
'''
|
||||
[implementation]
|
||||
'''
|
||||
def parameters_schema(self):
|
||||
return {
|
||||
"type": "object",
|
||||
"additionalProperties": False,
|
||||
"properties": {
|
||||
"path": {
|
||||
"type": "string"
|
||||
},
|
||||
"arguments": {
|
||||
"type": "array",
|
||||
"item": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
},
|
||||
"required": [
|
||||
"path",
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
'''
|
||||
[implementation]
|
||||
'''
|
||||
def normalize_order_node(self, node):
|
||||
return dict_merge(
|
||||
{
|
||||
},
|
||||
node
|
||||
)
|
||||
|
||||
|
||||
'''
|
||||
[implementation]
|
||||
'''
|
||||
def run(self, parameters):
|
||||
result = shell_command(
|
||||
" ".join([parameters["path"]] + parameters["arguments"])
|
||||
)
|
||||
if (result["return_code"] == 0):
|
||||
condition = enum_condition.ok
|
||||
elif (result["return_code"] == 1):
|
||||
condition = enum_condition.unknown
|
||||
elif (result["return_code"] == 2):
|
||||
condition = enum_condition.concerning
|
||||
elif (result["return_code"] == 3):
|
||||
condition = enum_condition.critical
|
||||
else:
|
||||
# raise ValueError("invalid exit code: %i" % result.returncode)
|
||||
condition = enum_condition.unknown
|
||||
return {
|
||||
"condition": condition,
|
||||
"info": {
|
||||
"stdout": result["stdout"],
|
||||
"stderr": result["stderr"],
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -1,172 +0,0 @@
|
|||
'''
|
||||
todo: allow_self_signed
|
||||
todo: allow_bad_domain
|
||||
todo:
|
||||
'''
|
||||
class implementation_check_kind_tls_certificate(interface_check_kind):
|
||||
|
||||
'''
|
||||
[implementation]
|
||||
'''
|
||||
def parameters_schema(self):
|
||||
return {
|
||||
"type": "object",
|
||||
"additionalProperties": False,
|
||||
"properties": {
|
||||
"host": {
|
||||
"type": "string"
|
||||
},
|
||||
"port": {
|
||||
"type": "integer",
|
||||
"default": 443
|
||||
},
|
||||
"expiry_threshold_concerning": {
|
||||
"description": "in days; allowed amount of valid days before the certificate expires; threshold for condition 'concerning'; 'null' means 'report at no value'",
|
||||
"type": ["null", "integer"],
|
||||
"default": 7,
|
||||
"minimum": 0
|
||||
},
|
||||
"expiry_threshold_critical": {
|
||||
"description": "in days; allowed amount of valid days before the certificate expires; threshold for condition 'critical'; 'null' means 'report at no value'",
|
||||
"type": ["null", "integer"],
|
||||
"default": 1,
|
||||
"minimum": 0
|
||||
},
|
||||
"expiry_threshold": {
|
||||
"deprecated": True,
|
||||
"description": "",
|
||||
"type": ["null", "integer"],
|
||||
"minimum": 0,
|
||||
"default": None,
|
||||
},
|
||||
"strict": {
|
||||
"deprecated": True,
|
||||
"description": "",
|
||||
"type": ["null", "boolean"],
|
||||
"default": None,
|
||||
},
|
||||
},
|
||||
"required": [
|
||||
"host",
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
'''
|
||||
[implementation]
|
||||
'''
|
||||
def normalize_order_node(self, node):
|
||||
version = (
|
||||
"v1"
|
||||
if (
|
||||
(not ("expiry_threshold_concerning" in node))
|
||||
and
|
||||
(not ("expiry_threshold_critical" in node))
|
||||
) else
|
||||
"v2"
|
||||
)
|
||||
|
||||
if (version == "v1"):
|
||||
if (not "host" in node):
|
||||
raise ValueError("missing mandatory field 'host'")
|
||||
else:
|
||||
node_ = dict_merge(
|
||||
{
|
||||
"port": 443,
|
||||
"expiry_threshold": 7,
|
||||
"strict": True,
|
||||
# "allow_self_signed": False,
|
||||
# "allow_bad_domain": False,
|
||||
},
|
||||
node
|
||||
)
|
||||
return {
|
||||
"port": node_["port"],
|
||||
"expiry_threshold_concerning": (
|
||||
None
|
||||
if node_["strict"] else
|
||||
node_["expiry_threshold"]
|
||||
),
|
||||
"expiry_threshold_critical": (
|
||||
node_["expiry_threshold"]
|
||||
if node_["strict"] else
|
||||
None
|
||||
),
|
||||
}
|
||||
elif (version == "v2"):
|
||||
if (not "host" in node):
|
||||
raise ValueError("missing mandatory field 'host'")
|
||||
else:
|
||||
node_ = dict_merge(
|
||||
{
|
||||
"port": 443,
|
||||
"expiry_threshold_concerning": 7,
|
||||
"expiry_threshold_critical": 1,
|
||||
# "allow_self_signed": False,
|
||||
# "allow_bad_domain": False,
|
||||
},
|
||||
node
|
||||
)
|
||||
return node_
|
||||
else:
|
||||
raise ValueError("unhandled")
|
||||
|
||||
|
||||
'''
|
||||
[implementation]
|
||||
'''
|
||||
def run(self, parameters):
|
||||
faults = []
|
||||
data = {}
|
||||
context = _ssl.create_default_context()
|
||||
condition = enum_condition.ok
|
||||
try:
|
||||
socket = _socket.create_connection((parameters["host"], parameters["port"], ))
|
||||
socket_wrapped = context.wrap_socket(socket, server_hostname = parameters["host"])
|
||||
version = socket_wrapped.version()
|
||||
stuff = socket_wrapped.getpeercert(False)
|
||||
except _ssl.SSLCertVerificationError as error:
|
||||
version = None
|
||||
stuff = None
|
||||
if (stuff is None):
|
||||
faults.append(translation_get("checks.tls_certificate.not_obtainable"))
|
||||
condition = enum_condition.critical
|
||||
else:
|
||||
# version == "TLSv1.3"
|
||||
expiry_timestamp = _ssl.cert_time_to_seconds(stuff["notAfter"])
|
||||
current_timestamp = get_current_timestamp()
|
||||
days = _math.ceil((expiry_timestamp - current_timestamp) / (60 * 60 * 24))
|
||||
data = dict_merge(
|
||||
data,
|
||||
{
|
||||
"expiry_timestamp": expiry_timestamp,
|
||||
"days": days,
|
||||
},
|
||||
)
|
||||
if (
|
||||
(parameters["expiry_threshold_critical"] is not None)
|
||||
and
|
||||
(days <= parameters["expiry_threshold_critical"])
|
||||
):
|
||||
faults.append(translation_get("checks.tls_certificate.expires_soon"))
|
||||
condition = enum_condition.critical
|
||||
else:
|
||||
if (
|
||||
(parameters["expiry_threshold_concerning"] is not None)
|
||||
and
|
||||
(days <= parameters["expiry_threshold_concerning"])
|
||||
):
|
||||
faults.append(translation_get("checks.tls_certificate.expires_soon"))
|
||||
condition = enum_condition.concerning
|
||||
else:
|
||||
pass
|
||||
return {
|
||||
"condition": condition,
|
||||
"info": {
|
||||
"host": parameters["host"],
|
||||
"port": parameters["port"],
|
||||
"faults": faults,
|
||||
"data": data,
|
||||
}
|
||||
}
|
||||
|
||||
282
source/logic/check_kinds/generic_remote.ts
Normal file
282
source/logic/check_kinds/generic_remote.ts
Normal file
|
|
@ -0,0 +1,282 @@
|
|||
namespace _heimdall.check_kinds.generic_remote
|
||||
{
|
||||
|
||||
/**
|
||||
*/
|
||||
function parameters_schema(
|
||||
) : _heimdall.helpers.json_schema.type_schema
|
||||
{
|
||||
return {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"host" : {
|
||||
"type" : "string"
|
||||
},
|
||||
"ssh_port": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "null",
|
||||
"default": null
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"default": null
|
||||
},
|
||||
]
|
||||
},
|
||||
"ssh_user" : {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "null",
|
||||
"default": null
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"default": null
|
||||
},
|
||||
]
|
||||
},
|
||||
"ssh_key" : {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "null",
|
||||
"default": null
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"default": null
|
||||
},
|
||||
]
|
||||
},
|
||||
"mount_point" : {
|
||||
"type" : "string",
|
||||
"default" : "/"
|
||||
},
|
||||
"threshold" : {
|
||||
"type" : "integer",
|
||||
"default" : 95,
|
||||
"description" : "maximaler Füllstand in Prozent"
|
||||
},
|
||||
"critical": {
|
||||
"description": "whether a violation of this check shall be leveled as critical instead of concerning",
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"strict": {
|
||||
"deprecated": true,
|
||||
"description": "alias for 'critical'",
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
},
|
||||
"required": [
|
||||
"host",
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
function normalize_order_node(
|
||||
node : any
|
||||
) : any
|
||||
{
|
||||
const version : string = (
|
||||
(! ("critical" in node))
|
||||
? "v1"
|
||||
: "v2"
|
||||
);
|
||||
switch (version) {
|
||||
default: {
|
||||
throw (new Error("unhandled version"));
|
||||
break;
|
||||
}
|
||||
case "v1": {
|
||||
if (! ("host" in node)) {
|
||||
throw (new Error("mandatory parameter \"host\" missing"));
|
||||
}
|
||||
else {
|
||||
const node_ = lib_plankton.object.patched(
|
||||
{
|
||||
"ssh_port": null,
|
||||
"ssh_user": null,
|
||||
"ssh_key": null,
|
||||
"mount_point": "/",
|
||||
"threshold": 95,
|
||||
"strict": false,
|
||||
},
|
||||
node,
|
||||
true
|
||||
);
|
||||
return {
|
||||
"ssh_port": node_["ssh_port"],
|
||||
"ssh_user": node_["ssh_user"],
|
||||
"ssh_key": node_["ssh_key"],
|
||||
"mount_point": node_["ssh_path"],
|
||||
"threshold": node_["ssh_threshold"],
|
||||
"critical": node_["strict"],
|
||||
};
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "v2": {
|
||||
if (! ("host" in node)) {
|
||||
throw (new Error("mandatory parameter \"host\" missing"));
|
||||
}
|
||||
else {
|
||||
const node_ = lib_plankton.object.patched(
|
||||
{
|
||||
"ssh_port": null,
|
||||
"ssh_user": null,
|
||||
"ssh_key": null,
|
||||
"mount_point": "/",
|
||||
"threshold": 95,
|
||||
"critical": false,
|
||||
},
|
||||
node,
|
||||
true
|
||||
);
|
||||
return node_;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
async function run(
|
||||
parameters
|
||||
) : Promise<_heimdall.type_result>
|
||||
{
|
||||
const nm_child_process = require("child_process");
|
||||
|
||||
const inner_command : string = lib_plankton.string.coin(
|
||||
"df {{mount_point}} | tr -s \" \"",
|
||||
{
|
||||
"mount_point": parameters["mount_point"],
|
||||
}
|
||||
);
|
||||
|
||||
let outer_command_parts : Array<string> = [];
|
||||
if (true) {
|
||||
outer_command_parts.push("ssh");
|
||||
}
|
||||
if (true) {
|
||||
outer_command_parts.push(lib_plankton.string.coin("{{host}}", {"host": parameters["host"]}));
|
||||
}
|
||||
if (parameters["ssh_port"] !== null) {
|
||||
outer_command_parts.push(lib_plankton.string.coin("-p {{port}}", {"port": parameters["ssh_port"].toFixed(0)}));
|
||||
}
|
||||
if (parameters["ssh_user"] !== null) {
|
||||
outer_command_parts.push(lib_plankton.string.coin("-l {{user}}", {"user": parameters["ssh_user"]}));
|
||||
}
|
||||
if (parameters["ssh_key"] !== null) {
|
||||
outer_command_parts.push(lib_plankton.string.coin("-i {{key}}", {"key": parameters["ssh_key"]}));
|
||||
}
|
||||
if (true) {
|
||||
outer_command_parts.push(lib_plankton.string.coin("-o BatchMode=yes", {}));
|
||||
}
|
||||
if (true) {
|
||||
outer_command_parts.push(lib_plankton.string.coin("'{{inner_command}}'", {"inner_command": inner_command}));
|
||||
}
|
||||
const outer_command : string = outer_command_parts.join(" ");
|
||||
|
||||
type type_result = {
|
||||
return_code : int;
|
||||
stdout : string;
|
||||
stderr : string;
|
||||
};
|
||||
/**
|
||||
* @see https://nodejs.org/api/child_process.html#child_processspawncommand-args-options
|
||||
*/
|
||||
const result : type_result = await new Promise<type_result>(
|
||||
(resolve, reject) => {
|
||||
nm_child_process.spawnSync(
|
||||
outer_command,
|
||||
[],
|
||||
{
|
||||
},
|
||||
(result) => {
|
||||
if (result.error) {
|
||||
reject(result.error);
|
||||
}
|
||||
else {
|
||||
resolve(
|
||||
{
|
||||
"return_code": result.status,
|
||||
"stdout": result.stdout,
|
||||
"stderr": result.stdin,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
if (result.return_code > 0) {
|
||||
return {
|
||||
"condition": _heimdall.enum_condition.unknown,
|
||||
"info": {
|
||||
"error": result.stderr,
|
||||
}
|
||||
};
|
||||
}
|
||||
else {
|
||||
const stuff : Array<string> = result.stdout.split("\n").slice(-2)[0].split(" ");
|
||||
const data = {
|
||||
"device": stuff[0],
|
||||
"used": parseInt(stuff[2]),
|
||||
"avail": parseInt(stuff[3]),
|
||||
"perc": parseInt(stuff[4].slice(-1)),
|
||||
};
|
||||
let faults : Array<string> = [];
|
||||
if (data["perc"] > parameters["threshold"]) {
|
||||
faults.push(lib_plankton.translate.get("checks.generic_remote.overflow"));
|
||||
}
|
||||
else {
|
||||
// do nothing
|
||||
}
|
||||
return {
|
||||
"condition": (
|
||||
(faults.length <= 0)
|
||||
? _heimdall.enum_condition.ok
|
||||
: (
|
||||
parameters["critical"]
|
||||
? _heimdall.enum_condition.critical
|
||||
: _heimdall.enum_condition.concerning
|
||||
)
|
||||
),
|
||||
"info": {
|
||||
"data": {
|
||||
"host": parameters["host"],
|
||||
"device": data["device"],
|
||||
"mount_point": parameters["mount_point"],
|
||||
"used": _heimdall.helpers.misc.format_bytes(data["used"]),
|
||||
"available": _heimdall.helpers.misc.format_bytes(data["avail"]),
|
||||
"percentage": (data["perc"].toFixed(0) + "%"),
|
||||
},
|
||||
"faults": faults,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export function check_kind_implementation(
|
||||
) : type_check_kind
|
||||
{
|
||||
return {
|
||||
"parameters_schema": parameters_schema,
|
||||
"normalize_order_node": normalize_order_node,
|
||||
"run": run,
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -177,17 +177,29 @@ namespace _heimdall.check_kinds.http_request
|
|||
/**
|
||||
*/
|
||||
async function run(
|
||||
parameters
|
||||
parameters : {
|
||||
request : {
|
||||
target : string;
|
||||
method : string;
|
||||
};
|
||||
timeout : float;
|
||||
follow_redirects : boolean;
|
||||
response : {
|
||||
status_code : int;
|
||||
headers ?: Record<string, string>;
|
||||
body_part ?: string;
|
||||
};
|
||||
critical : boolean;
|
||||
}
|
||||
) : Promise<_heimdall.type_result>
|
||||
{
|
||||
let error : (null | Error);
|
||||
const http_request : lib_plankton.http.type_request = {
|
||||
"host": parameters["request"]["target"],
|
||||
"query": "",
|
||||
"target": parameters.request.target,
|
||||
"method": {
|
||||
"GET": lib_plankton.http.enum_method.get,
|
||||
"POST": lib_plankton.http.enum_method.post,
|
||||
}[parameters["request"]["method"]],
|
||||
}[parameters.request.method],
|
||||
"headers": {},
|
||||
"body": "",
|
||||
};
|
||||
|
|
@ -196,8 +208,9 @@ namespace _heimdall.check_kinds.http_request
|
|||
http_response = await lib_plankton.http.call(
|
||||
http_request,
|
||||
{
|
||||
"timeout": parameters["timeout"],
|
||||
"follow_redirects": parameters["follow_redirects"],
|
||||
"timeout": /*parameters.timeout*/20.0,
|
||||
"follow_redirects": parameters.follow_redirects,
|
||||
"implementation": "http_module",
|
||||
}
|
||||
);
|
||||
error = null;
|
||||
|
|
@ -209,14 +222,14 @@ namespace _heimdall.check_kinds.http_request
|
|||
if (http_response === null) {
|
||||
return {
|
||||
"condition": (
|
||||
parameters["strict"]
|
||||
parameters.critical
|
||||
? _heimdall.enum_condition.critical
|
||||
: _heimdall.enum_condition.concerning
|
||||
),
|
||||
"info": {
|
||||
"request": parameters["request"],
|
||||
"request": parameters.request,
|
||||
"faults": [
|
||||
lib_plankton.translate.get("checks.http_request.request_failed"),
|
||||
lib_plankton.translate.get("checks.http_request.request_failed", {"reason": error.toString()}),
|
||||
],
|
||||
},
|
||||
};
|
||||
|
|
@ -226,15 +239,15 @@ namespace _heimdall.check_kinds.http_request
|
|||
// status code
|
||||
{
|
||||
if (
|
||||
(! ("status_code" in parameters["response"]))
|
||||
(! ("status_code" in parameters.response))
|
||||
||
|
||||
(parameters["response"]["status_code"] === null)
|
||||
(parameters.response.status_code === null)
|
||||
) {
|
||||
// do nothing
|
||||
}
|
||||
else {
|
||||
const status_code_expected : int = (parameters["response"]["status_code"] as int);
|
||||
if (http_response.statuscode === status_code_expected) {
|
||||
const status_code_expected : int = (parameters.response.status_code as int);
|
||||
if (! (http_response.statuscode === status_code_expected)) {
|
||||
faults.push(
|
||||
lib_plankton.translate.get(
|
||||
"checks.http_request.status_code_mismatch",
|
||||
|
|
@ -253,17 +266,21 @@ namespace _heimdall.check_kinds.http_request
|
|||
// headers
|
||||
{
|
||||
if (
|
||||
(! ("headers" in parameters["response"]))
|
||||
(! ("headers" in parameters.response))
|
||||
||
|
||||
(parameters["response"]["headers"] === null)
|
||||
(parameters.response.headers === null)
|
||||
) {
|
||||
// do nothing
|
||||
}
|
||||
else {
|
||||
const headers_expected : Record<string, string> = (parameters["response"]["headers"] as Record<string, string>);
|
||||
const headers_expected : Record<string, string> = (parameters.response.headers as Record<string, string>);
|
||||
Object.entries(headers_expected).forEach(
|
||||
([header_key, header_value]) => {
|
||||
if (! (header_key in http_response.headers)) {
|
||||
if (
|
||||
(! (header_key in http_response.headers))
|
||||
&&
|
||||
(! (header_key.toLowerCase() in http_response.headers))
|
||||
) {
|
||||
faults.push(
|
||||
lib_plankton.translate.get(
|
||||
"checks.http_request.header_missing",
|
||||
|
|
@ -275,7 +292,11 @@ namespace _heimdall.check_kinds.http_request
|
|||
);
|
||||
}
|
||||
else {
|
||||
if (! (http_response.headers[header_key] === header_value)) {
|
||||
if (
|
||||
(! (http_response.headers[header_key] === header_value))
|
||||
&&
|
||||
(! (http_response.headers[header_key.toLowerCase()].toLowerCase() === header_value.toLowerCase()))
|
||||
) {
|
||||
faults.push(
|
||||
lib_plankton.translate.get(
|
||||
"checks.http_request.header_value_mismatch",
|
||||
|
|
@ -298,14 +319,14 @@ namespace _heimdall.check_kinds.http_request
|
|||
// body
|
||||
{
|
||||
if (
|
||||
(! ("body_part" in parameters["response"]))
|
||||
(! ("body_part" in parameters.response))
|
||||
||
|
||||
(parameters["response"]["body_part"] === null)
|
||||
(parameters.response.body_part === null)
|
||||
) {
|
||||
// do nothing
|
||||
}
|
||||
else {
|
||||
const body_part : string = (parameters["response"]["body_part"] as string);
|
||||
const body_part : string = (parameters.response.body_part as string);
|
||||
if (! http_response.body.includes(body_part)) {
|
||||
faults.push(
|
||||
lib_plankton.translate.get(
|
||||
|
|
@ -327,13 +348,13 @@ namespace _heimdall.check_kinds.http_request
|
|||
(faults.length <= 0)
|
||||
? _heimdall.enum_condition.ok
|
||||
: (
|
||||
parameters["critical"]
|
||||
parameters.critical
|
||||
? _heimdall.enum_condition.critical
|
||||
: _heimdall.enum_condition.concerning
|
||||
)
|
||||
),
|
||||
"info": {
|
||||
"request": parameters["request"],
|
||||
"request": parameters.request,
|
||||
"response": {
|
||||
"status_code": http_response.statuscode,
|
||||
"headers": http_response.headers,
|
||||
|
|
|
|||
|
|
@ -122,7 +122,7 @@ namespace _heimdall.check_kinds.script
|
|||
"info": {
|
||||
"result": result,
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
250
source/logic/check_kinds/tls_certificate.ts
Normal file
250
source/logic/check_kinds/tls_certificate.ts
Normal file
|
|
@ -0,0 +1,250 @@
|
|||
namespace _heimdall.check_kinds.tls_certificate
|
||||
{
|
||||
|
||||
/**
|
||||
*/
|
||||
function parameters_schema(
|
||||
) : _heimdall.helpers.json_schema.type_schema
|
||||
{
|
||||
return {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"host": {
|
||||
"type": "string"
|
||||
},
|
||||
"port": {
|
||||
"type": "integer",
|
||||
"default": 443
|
||||
},
|
||||
"expiry_threshold_concerning": {
|
||||
"description": "in days; allowed amount of valid days before the certificate expires; threshold for condition 'concerning'; 'null' means 'report at no value'",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "null",
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
],
|
||||
"default": 7,
|
||||
},
|
||||
"expiry_threshold_critical": {
|
||||
"description": "in days; allowed amount of valid days before the certificate expires; threshold for condition 'critical'; 'null' means 'report at no value'",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "null",
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
],
|
||||
"default": 1,
|
||||
},
|
||||
"expiry_threshold": {
|
||||
"deprecated": true,
|
||||
"description": "",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "null",
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
],
|
||||
"default": null,
|
||||
},
|
||||
"strict": {
|
||||
"deprecated": true,
|
||||
"description": "",
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "null",
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
},
|
||||
],
|
||||
"default": null,
|
||||
},
|
||||
},
|
||||
"required": [
|
||||
"host",
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
function normalize_order_node(
|
||||
node : any
|
||||
) : any
|
||||
{
|
||||
const version : string = (
|
||||
(
|
||||
(! ("expiry_threshold_concerning" in node))
|
||||
&&
|
||||
(! ("expiry_threshold_critical" in node))
|
||||
)
|
||||
? "v1"
|
||||
: "v2"
|
||||
);
|
||||
|
||||
switch (version) {
|
||||
default: {
|
||||
throw (new Error("unhandled version"));
|
||||
break;
|
||||
}
|
||||
case "v1": {
|
||||
if (! ("host" in node)) {
|
||||
throw new Error("missing mandatory field 'host'");
|
||||
}
|
||||
else {
|
||||
const node_ = Object.assign(
|
||||
{
|
||||
"port": 443,
|
||||
"expiry_threshold": 7,
|
||||
"strict": true,
|
||||
},
|
||||
node
|
||||
);
|
||||
return {
|
||||
"host": node_["host"],
|
||||
"port": node_["port"],
|
||||
"expiry_threshold_concerning": (
|
||||
node_["strict"]
|
||||
? null
|
||||
: node_["expiry_threshold"]
|
||||
),
|
||||
"expiry_threshold_critical": (
|
||||
node_["strict"]
|
||||
? node_["expiry_threshold"]
|
||||
: null
|
||||
),
|
||||
};
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "v2": {
|
||||
if (! ("host" in node)) {
|
||||
throw new Error("missing mandatory field 'host'");
|
||||
}
|
||||
else {
|
||||
const node_ = Object.assign(
|
||||
{
|
||||
"port": 443,
|
||||
"expiry_threshold_concerning": 7,
|
||||
"expiry_threshold_critical": 1,
|
||||
},
|
||||
node
|
||||
);
|
||||
return node_;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
async function run(
|
||||
parameters
|
||||
) : Promise<_heimdall.type_result>
|
||||
{
|
||||
// TODO: outsource to parameters
|
||||
const timeout : float = 5.0;
|
||||
|
||||
type type_stuff = {
|
||||
valid_from : int;
|
||||
valid_to : int;
|
||||
};
|
||||
|
||||
// const nm_child_process = require("x509");
|
||||
const nm_tls = require("tls");
|
||||
const nm_ssl_checker = require("ssl-checker");
|
||||
|
||||
let faults : Array<string> = [];
|
||||
let data : Record<string, any> = {};
|
||||
let condition : _heimdall.enum_condition = _heimdall.enum_condition.ok;
|
||||
let version : (null | string);
|
||||
|
||||
const stuff : (null | type_stuff) = await (
|
||||
nm_ssl_checker(parameters["host"], {"port": parameters["port"]})
|
||||
.then(
|
||||
x => ({
|
||||
"valid_from": Math.floor((new Date(x["validFrom"])).getTime() / 1000),
|
||||
"valid_to": Math.floor((new Date(x["validTo"])).getTime() / 1000),
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
if (stuff === null) {
|
||||
faults.push(lib_plankton.translate.get("checks.tls_certificate.not_obtainable"));
|
||||
condition = _heimdall.enum_condition.critical;
|
||||
version = null;
|
||||
}
|
||||
else {
|
||||
version = "TLSv1.3";
|
||||
const current_timestamp : int = _heimdall.get_current_timestamp();
|
||||
const expiry_timestamp = stuff.valid_to;
|
||||
const days : int = Math.ceil((expiry_timestamp - current_timestamp) / (60 * 60 * 24));
|
||||
data = Object.assign(
|
||||
data,
|
||||
{
|
||||
"expiry_timestamp": expiry_timestamp,
|
||||
"days": days,
|
||||
}
|
||||
);
|
||||
if (
|
||||
(parameters["expiry_threshold_critical"] !== null)
|
||||
&&
|
||||
(days <= parameters["expiry_threshold_critical"])
|
||||
) {
|
||||
faults.push(lib_plankton.translate.get("checks.tls_certificate.expires_soon"));
|
||||
condition = _heimdall.enum_condition.critical;
|
||||
}
|
||||
else {
|
||||
if (
|
||||
(parameters["expiry_threshold_concerning"] !== null)
|
||||
&&
|
||||
(days <= parameters["expiry_threshold_concerning"])
|
||||
) {
|
||||
faults.push(lib_plankton.translate.get("checks.tls_certificate.expires_soon"));
|
||||
condition = _heimdall.enum_condition.concerning;
|
||||
}
|
||||
else {
|
||||
// no nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.resolve({
|
||||
"condition": condition,
|
||||
"info": {
|
||||
"host": parameters["host"],
|
||||
"port": parameters["port"],
|
||||
"faults": faults,
|
||||
"data": data,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
export function check_kind_implementation(
|
||||
) : type_check_kind
|
||||
{
|
||||
return {
|
||||
"parameters_schema": parameters_schema,
|
||||
"normalize_order_node": normalize_order_node,
|
||||
"run": run,
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
32
source/logic/helpers/misc.ts
Normal file
32
source/logic/helpers/misc.ts
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
namespace _heimdall.helpers.misc
|
||||
{
|
||||
|
||||
/**
|
||||
*/
|
||||
export function format_bytes(
|
||||
bytes : int
|
||||
) : string
|
||||
{
|
||||
const units : Array<{label : string; digits : int;}> = [
|
||||
{"label": "B", "digits": 0},
|
||||
{"label": "KB", "digits": 1},
|
||||
{"label": "MB", "digits": 1},
|
||||
{"label": "GB", "digits": 1},
|
||||
{"label": "TB", "digits": 1},
|
||||
{"label": "PB", "digits": 1},
|
||||
]
|
||||
let number_ : int = bytes;
|
||||
let index : int = 0;
|
||||
while ((number_ >= 1000) && (index < (units.length - 1))) {
|
||||
number_ /= 1000;
|
||||
index += 1;
|
||||
}
|
||||
return lib_plankton.string.coin(
|
||||
"{{number}} {{label}}",
|
||||
{
|
||||
"number": number_.toFixed(units[index].digits),
|
||||
"label": units[index].label,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -248,6 +248,8 @@ async function main(
|
|||
"script": _heimdall.check_kinds.script.check_kind_implementation(),
|
||||
"http_request": _heimdall.check_kinds.http_request.check_kind_implementation(),
|
||||
"file_state": _heimdall.check_kinds.file_state.check_kind_implementation(),
|
||||
"tls_certificate": _heimdall.check_kinds.tls_certificate.check_kind_implementation(),
|
||||
"generic_remote": _heimdall.check_kinds.generic_remote.check_kind_implementation(),
|
||||
};
|
||||
if (args["show_schema"]) {
|
||||
process.stdout.write(
|
||||
|
|
|
|||
|
|
@ -22,12 +22,15 @@
|
|||
"source/logic/base.ts",
|
||||
"source/logic/helpers/json_schema.ts",
|
||||
"source/logic/helpers/sqlite.ts",
|
||||
"source/logic/helpers/misc.ts",
|
||||
"source/logic/notification_kinds/_abstract.ts",
|
||||
"source/logic/notification_kinds/console.ts",
|
||||
"source/logic/check_kinds/_abstract.ts",
|
||||
"source/logic/check_kinds/script.ts",
|
||||
"source/logic/check_kinds/http_request.ts",
|
||||
"source/logic/check_kinds/file_state.ts",
|
||||
"source/logic/check_kinds/tls_certificate.ts",
|
||||
"source/logic/check_kinds/generic_remote.ts",
|
||||
"source/logic/state_repository.ts",
|
||||
"source/logic/order.ts",
|
||||
"source/logic/main.ts"
|
||||
|
|
|
|||
Loading…
Reference in a new issue