Merge branch 'develop-issue_4' into 'master'
Neuer Check: TLS-Zertifikat See merge request tools/heimdall!4
This commit is contained in:
commit
c71e4a6bd4
|
|
@ -571,6 +571,49 @@
|
|||
"parameters"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "check 'tls_certificate'",
|
||||
"type": "object",
|
||||
"unevaluatedProperties": false,
|
||||
"properties": {
|
||||
"kind": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"tls_certificate"
|
||||
]
|
||||
},
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"host": {
|
||||
"type": "string"
|
||||
},
|
||||
"strict": {
|
||||
"description": "whether a violation of this check shall be leveled as critical instead of concerning",
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"expiry_threshold": {
|
||||
"description": "in days; allowed amount of valid days before the certificate expires",
|
||||
"type": [
|
||||
"null",
|
||||
"integer"
|
||||
],
|
||||
"default": 7,
|
||||
"minimum": 0
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"host"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"kind",
|
||||
"parameters"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "check 'http_request'",
|
||||
"type": "object",
|
||||
|
|
|
|||
|
|
@ -15,8 +15,6 @@
|
|||
]
|
||||
},
|
||||
"includes": [
|
||||
"script.hmdl.json",
|
||||
"file_state.hmdl.json",
|
||||
"generic_remote.hmdl.json"
|
||||
"tls_certificate.hmdl.json"
|
||||
]
|
||||
}
|
||||
|
|
|
|||
19
examples/tls_certificate.hmdl.json
Normal file
19
examples/tls_certificate.hmdl.json
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"checks": [
|
||||
{
|
||||
"name": "test1",
|
||||
"kind": "tls_certificate",
|
||||
"parameters": {
|
||||
"host": "greenscale.de",
|
||||
"expiry_threshold": 50
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "test2",
|
||||
"kind": "tls_certificate",
|
||||
"parameters": {
|
||||
"host": "chemnitz-gesundheit.de"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -17,6 +17,8 @@
|
|||
"checks.file_state.timestamp_implausible": "Datei ist scheinbar aus der Zukunft",
|
||||
"checks.file_state.too_old": "Datei ist zu alt",
|
||||
"checks.file_state.too_big": "Datei ist zu groß",
|
||||
"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.status_code_mismatch": "Status-Code {{status_code_actual}} stimmt nicht mit dem erwarteten Wert {{status_code_expected}} überein",
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@
|
|||
"checks.file_state.timestamp_implausible": "file is apparently from the future",
|
||||
"checks.file_state.too_old": "file is too old",
|
||||
"checks.file_state.too_big": "file is too big",
|
||||
"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.status_code_mismatch": "actual status code {{status_code_actual}} does not match expected value {{status_code_expected}}",
|
||||
|
|
|
|||
121
source/logic/checks/tls_certificate.py
Normal file
121
source/logic/checks/tls_certificate.py
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
'''
|
||||
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
|
||||
},
|
||||
"strict": {
|
||||
"description": "whether a violation of this check shall be leveled as critical instead of concerning",
|
||||
"type": "boolean",
|
||||
"default": True
|
||||
},
|
||||
"expiry_threshold": {
|
||||
"description": "in days; allowed amount of valid days before the certificate expires",
|
||||
"type": ["null", "integer"],
|
||||
"default": 7,
|
||||
"minimum": 0
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"host"
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
'''
|
||||
[implementation]
|
||||
'''
|
||||
def normalize_conf_node(self, node):
|
||||
if (not "host" in node):
|
||||
raise ValueError("missing mandatory field 'host'")
|
||||
else:
|
||||
return dict_merge(
|
||||
{
|
||||
"strict": True,
|
||||
"port": 443,
|
||||
"expiry_threshold": 7,
|
||||
# "allow_self_signed": False,
|
||||
# "allow_bad_domain": False,
|
||||
},
|
||||
node
|
||||
)
|
||||
return node
|
||||
|
||||
|
||||
'''
|
||||
[implementation]
|
||||
'''
|
||||
def run(self, parameters):
|
||||
context = _ssl.create_default_context()
|
||||
try:
|
||||
socket = _socket.create_connection((parameters["host"], parameters["port"], ))
|
||||
socket_wrapped = context.wrap_socket(socket, server_hostname = parameters["host"])
|
||||
version = socket_wrapped.version()
|
||||
data = socket_wrapped.getpeercert(False)
|
||||
except _ssl.SSLCertVerificationError as error:
|
||||
version = None
|
||||
data = None
|
||||
if (data is None):
|
||||
return {
|
||||
"condition": (
|
||||
enum_condition.critical
|
||||
if parameters["strict"] else
|
||||
enum_condition.concerning
|
||||
),
|
||||
"info": {
|
||||
"host": parameters["host"],
|
||||
"port": parameters["port"],
|
||||
"faults": [
|
||||
translation_get("checks.tls_certificate.not_obtainable"),
|
||||
],
|
||||
"data": {
|
||||
},
|
||||
}
|
||||
}
|
||||
else:
|
||||
# version == "TLSv1.3"
|
||||
expiry_timestamp = _ssl.cert_time_to_seconds(data["notAfter"])
|
||||
current_timestamp = get_current_timestamp()
|
||||
days = _math.ceil((expiry_timestamp - current_timestamp) / (60 * 60 * 24))
|
||||
if (days <= parameters["expiry_threshold"]):
|
||||
return {
|
||||
"condition": (
|
||||
enum_condition.critical
|
||||
if parameters["strict"] else
|
||||
enum_condition.concerning
|
||||
),
|
||||
"info": {
|
||||
"host": parameters["host"],
|
||||
"port": parameters["port"],
|
||||
"faults": [
|
||||
translation_get("checks.tls_certificate.expires_soon"),
|
||||
],
|
||||
"data": {
|
||||
"expiry_timestamp": expiry_timestamp,
|
||||
"days": days,
|
||||
},
|
||||
}
|
||||
}
|
||||
else:
|
||||
return {
|
||||
"condition": enum_condition.ok,
|
||||
"info": {
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -121,6 +121,7 @@ def main():
|
|||
check_kind_implementations = {
|
||||
"script": implementation_check_kind_script(),
|
||||
"file_state": implementation_check_kind_file_state(),
|
||||
"tls_certificate": implementation_check_kind_tls_certificate(),
|
||||
"http_request": implementation_check_kind_http_request(),
|
||||
"generic_remote" : implementation_check_kind_generic_remote(),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import sys as _sys
|
||||
import os as _os
|
||||
import subprocess as _subprocess
|
||||
import math as _math
|
||||
import hashlib as _hashlib
|
||||
import tempfile as _tempfile
|
||||
import argparse as _argparse
|
||||
|
|
@ -11,3 +12,5 @@ import time as _time
|
|||
import datetime as _datetime
|
||||
import smtplib as _smtplib
|
||||
from email.mime.text import MIMEText
|
||||
import ssl as _ssl
|
||||
import socket as _socket
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ def main():
|
|||
_os.path.join(dir_source, "logic", "checks", "_interface.py"),
|
||||
_os.path.join(dir_source, "logic", "checks", "script.py"),
|
||||
_os.path.join(dir_source, "logic", "checks", "file_state.py"),
|
||||
_os.path.join(dir_source, "logic", "checks", "tls_certificate.py"),
|
||||
_os.path.join(dir_source, "logic", "checks", "http_request.py"),
|
||||
_os.path.join(dir_source, "logic", "checks", "generic_remote.py"),
|
||||
_os.path.join(dir_source, "logic", "channels", "_interface.py"),
|
||||
|
|
|
|||
Loading…
Reference in a new issue