mirror of
https://github.com/bashclub/check-opnsense.git
synced 2024-11-07 16:31:58 +01:00
Update to 0.79
This commit is contained in:
parent
a3a61e68ed
commit
78d92e8c7d
@ -22,11 +22,12 @@
|
||||
## copy to /usr/local/etc/rc.syshook.d/start/99-checkmk_agent and chmod +x
|
||||
##
|
||||
|
||||
__VERSION__ = "0.73"
|
||||
__VERSION__ = "0.79"
|
||||
|
||||
import sys
|
||||
import os
|
||||
import shlex
|
||||
import glob
|
||||
import re
|
||||
import time
|
||||
import json
|
||||
@ -41,10 +42,15 @@ import base64
|
||||
import traceback
|
||||
from cryptography import x509
|
||||
from cryptography.hazmat.backends import default_backend as crypto_default_backend
|
||||
from cryptography.hazmat.primitives import hashes
|
||||
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
||||
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
|
||||
from xml.etree import cElementTree as ELementTree
|
||||
from collections import Counter,defaultdict
|
||||
from pprint import pprint
|
||||
from socketserver import TCPServer,StreamRequestHandler
|
||||
BASEDIR = os.path.dirname(os.path.abspath(os.path.basename(__file__)))
|
||||
LOCALDIR = os.path.join(BASEDIR,"local")
|
||||
|
||||
class object_dict(defaultdict):
|
||||
def __getattr__(self,name):
|
||||
@ -70,45 +76,119 @@ def etree_to_dict(t):
|
||||
d[t.tag] = text
|
||||
return d
|
||||
|
||||
def pad_pkcs7(message,size=16):
|
||||
_pad = size - (len(message) % size)
|
||||
if type(message) == str:
|
||||
return message + chr(_pad) * _pad
|
||||
else:
|
||||
return message + bytes([_pad]) * _pad
|
||||
|
||||
class checkmk_handler(StreamRequestHandler):
|
||||
def handle(self):
|
||||
with self.server._mutex:
|
||||
try:
|
||||
_strmsg = self.server.do_checks()
|
||||
_strmsg = self.server.do_checks(remote_ip=self.client_address[0])
|
||||
except Exception as e:
|
||||
_strmsg = str(e)
|
||||
with self.wfile as _f:
|
||||
_f.write(_strmsg.encode("utf-8"))
|
||||
raise
|
||||
_strmsg = str(e).encode("utf-8")
|
||||
try:
|
||||
self.wfile.write(_strmsg)
|
||||
except:
|
||||
raise
|
||||
pass
|
||||
|
||||
class checkmk_checker(object):
|
||||
_certificate_timestamp = 0
|
||||
_check_cache = {}
|
||||
_datastore_mutex = threading.RLock()
|
||||
_datastore = object_dict()
|
||||
def do_checks(self,debug=False):
|
||||
|
||||
def encrypt(self,message,password='secretpassword'):
|
||||
SALT_LENGTH = 8
|
||||
KEY_LENGTH = 32
|
||||
IV_LENGTH = 16
|
||||
PBKDF2_CYCLES = 10_000
|
||||
SALT = b"Salted__"
|
||||
_backend = crypto_default_backend()
|
||||
_kdf_key = PBKDF2HMAC(
|
||||
algorithm = hashes.SHA256,
|
||||
length = KEY_LENGTH + IV_LENGTH,
|
||||
salt = SALT,
|
||||
iterations = PBKDF2_CYCLES,
|
||||
backend = _backend
|
||||
).derive(password.encode("utf-8"))
|
||||
_key, _iv = _kdf_key[:KEY_LENGTH],_kdf_key[KEY_LENGTH:]
|
||||
_encryptor = Cipher(
|
||||
algorithms.AES(_key),
|
||||
modes.CBC(_iv),
|
||||
backend = _backend
|
||||
).encryptor()
|
||||
message = pad_pkcs7(message)
|
||||
message = message.encode("utf-8")
|
||||
_encrypted_message = _encryptor.update(message) + _encryptor.finalize()
|
||||
return pad_pkcs7(b"03",10) + SALT + _encrypted_message
|
||||
|
||||
def _encrypt(self,message):
|
||||
_cmd = shlex.split('openssl enc -aes-256-cbc -md sha256 -iter 10000 -k "secretpassword"',posix=True)
|
||||
_proc = subprocess.Popen(_cmd,stderr=subprocess.DEVNULL,stdout=subprocess.PIPE,stdin=subprocess.PIPE)
|
||||
_out,_err = _proc.communicate(input=message.encode("utf-8"))
|
||||
return b"03" + _out
|
||||
|
||||
def do_checks(self,debug=False,remote_ip=None,**kwargs):
|
||||
self._getosinfo()
|
||||
_errors = []
|
||||
_failed_sections = []
|
||||
_lines = ["<<<check_mk>>>"]
|
||||
_lines.append("AgentOS: {os}".format(**self._info))
|
||||
_lines.append(f"Version: {__VERSION__}")
|
||||
_lines.append("Hostname: {hostname}".format(**self._info))
|
||||
if self.onlyfrom:
|
||||
_lines.append("OnlyFrom: {0}".format(",".join(self.onlyfrom)))
|
||||
|
||||
_lines.append(f"LocalDirectory: {LOCALDIR}")
|
||||
|
||||
for _check in dir(self):
|
||||
if _check.startswith("check_"):
|
||||
_name = _check.split("_",1)[1]
|
||||
try:
|
||||
_lines += getattr(self,_check)()
|
||||
except:
|
||||
_failed_sections.append(_name)
|
||||
_errors.append(traceback.format_exc())
|
||||
|
||||
_lines.append("<<<local:sep(0)>>>")
|
||||
for _check in dir(self):
|
||||
if _check.startswith("checklocal_"):
|
||||
_name = _check.split("_",1)[1]
|
||||
try:
|
||||
_lines += getattr(self,_check)()
|
||||
except:
|
||||
_failed_sections.append(_name)
|
||||
_errors.append(traceback.format_exc())
|
||||
|
||||
if os.path.isdir(LOCALDIR):
|
||||
for _local_file in glob.glob(f"{LOCALDIR}/**",recursive=True):
|
||||
if os.path.isfile(_local_file) and os.access(_local_file,os.X_OK):
|
||||
try:
|
||||
_cachetime = int(_local_file.split(os.path.sep)[-2])
|
||||
except:
|
||||
_cachetime = 0
|
||||
try:
|
||||
_lines.append(self._run_cache_prog(_local_file,_cachetime))
|
||||
except:
|
||||
_errors.append(traceback.format_exc())
|
||||
|
||||
_lines.append("")
|
||||
if debug:
|
||||
sys.stderr.write("\n".join(_errors))
|
||||
sys.stderr.flush()
|
||||
return "\n".join(_lines)
|
||||
if _failed_sections:
|
||||
_lines.append("<<<check_mk>>>")
|
||||
_lines.append("FailedPythonPlugins: {0}".format(",".join(_failed_sections)))
|
||||
|
||||
if self.encryptionkey:
|
||||
return self.encrypt("\n".join(_lines),password=self.encryptionkey)
|
||||
return "\n".join(_lines).encode("utf-8")
|
||||
|
||||
def _get_storedata(self,section,key):
|
||||
with self._datastore_mutex:
|
||||
@ -203,7 +283,7 @@ class checkmk_checker(object):
|
||||
def get_opnsense_ipaddr(self):
|
||||
try:
|
||||
_ret = {}
|
||||
for _if,_ip,_mask in re.findall("^([\w_]+):\sflags=(?:8943|8051|8043).*?inet\s([\d.]+)\snetmask\s0x([a-f0-9]+)",subprocess.check_output("ifconfig",encoding="utf-8"),re.DOTALL | re.M):
|
||||
for _if,_ip,_mask in re.findall("^([\w_]+):\sflags=(?:8943|8051|8043).*?inet\s([\d.]+)\snetmask\s0x([a-f0-9]+)",self._run_prog("ifconfig"),re.DOTALL | re.M):
|
||||
_ret[_if] = "{0}/{1}".format(_ip,str(bin(int(_mask,16))).count("1"))
|
||||
return _ret
|
||||
except:
|
||||
@ -248,7 +328,7 @@ class checkmk_checker(object):
|
||||
_interface_status = dict(
|
||||
map(lambda x: (x[0],(x[1:])),
|
||||
re.findall("^(?P<iface>[\w.]+):.*?(?P<adminstate>UP|DOWN),.*?\n(?:\s+(?:media:.*?(?P<speed>\d+G?).*?\<(?P<duplex>.*?)\>|(?:status:\s(?P<operstate>[ \w]+))|).*?\n)*",
|
||||
subprocess.check_output("ifconfig",encoding="utf-8"),re.M)
|
||||
self._run_prog("ifconfig"),re.M)
|
||||
)
|
||||
)
|
||||
_interface_data = self._run_prog("/usr/bin/netstat -i -b -d -n -W -f link").split("\n")
|
||||
@ -290,7 +370,7 @@ class checkmk_checker(object):
|
||||
def checklocal_services(self):
|
||||
_phpcode = '<?php require_once("config.inc");require_once("system.inc"); require_once("plugins.inc"); require_once("util.inc"); foreach(plugins_services() as $_service) { printf("%s;%s;%s\n",$_service["name"],$_service["description"],service_status($_service));} ?>'
|
||||
_proc = subprocess.Popen(["php"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL,encoding="utf-8")
|
||||
_data,_ = _proc.communicate(input=_phpcode)
|
||||
_data,_ = _proc.communicate(input=_phpcode,timeout=15)
|
||||
_services = []
|
||||
for _service in _data.strip().split("\n"):
|
||||
_services.append(_service.split(";"))
|
||||
@ -561,7 +641,7 @@ class checkmk_checker(object):
|
||||
|
||||
def checklocal_ipsec(self):
|
||||
_ret = []
|
||||
_json_data = subprocess.check_output("/usr/local/opnsense/scripts/ipsec/list_status.py",encoding="utf-8")
|
||||
_json_data = self._run_prog("/usr/local/opnsense/scripts/ipsec/list_status.py")
|
||||
if len(_json_data.strip()) < 20:
|
||||
return []
|
||||
for _con in json.loads(_json_data).values():
|
||||
@ -605,7 +685,7 @@ class checkmk_checker(object):
|
||||
_client["bytes_sent"] = 0
|
||||
_client["status"] = 2
|
||||
|
||||
_dump = subprocess.check_output(["wg","show","all","dump"],encoding="utf-8").strip()
|
||||
_dump = self._run_prog(["wg","show","all","dump"]).strip()
|
||||
for _line in _dump.split("\n"):
|
||||
_values = _line.split("\t")
|
||||
if len(_values) != 9:
|
||||
@ -629,7 +709,7 @@ class checkmk_checker(object):
|
||||
def checklocal_unbound(self):
|
||||
_ret = []
|
||||
try:
|
||||
_output = subprocess.check_output(["/usr/local/sbin/unbound-control", "-c", "/var/unbound/unbound.conf", "stats_noreset"],encoding="utf-8",stderr=subprocess.DEVNULL)
|
||||
_output = self._run_prog(["/usr/local/sbin/unbound-control", "-c", "/var/unbound/unbound.conf", "stats_noreset"])
|
||||
_unbound_stat = dict(
|
||||
map(
|
||||
lambda x: (x[0].replace(".","_"),float(x[1])),
|
||||
@ -732,7 +812,7 @@ class checkmk_checker(object):
|
||||
|
||||
def check_kernel(self):
|
||||
_ret = ["<<<kernel>>>"]
|
||||
_out = self._run_prog("sysctl -a")
|
||||
_out = self._run_prog("sysctl -a",timeout=10)
|
||||
_kernel = dict([_v.split(": ") for _v in _out.split("\n") if len(_v.split(": ")) == 2])
|
||||
_ret.append("{0:.0f}".format(time.time()))
|
||||
_ret.append("cpu {0} {1} {2} {4} {3}".format(*(_kernel.get("kern.cp_time","").split(" "))))
|
||||
@ -784,7 +864,7 @@ class checkmk_checker(object):
|
||||
|
||||
def check_ntp(self):
|
||||
_ret = ["<<<ntp>>>"]
|
||||
for _line in self._run_prog("ntpq -np").split("\n")[2:]:
|
||||
for _line in self._run_prog("ntpq -np",timeout=30).split("\n")[2:]:
|
||||
if _line.strip():
|
||||
_ret.append("{0} {1}".format(_line[0],_line[1:]))
|
||||
return _ret
|
||||
@ -813,18 +893,82 @@ class checkmk_checker(object):
|
||||
_ret.append(f"{_uptime_sec} {_idle_sec}")
|
||||
return _ret
|
||||
|
||||
def _run_prog(self,cmdline="",*args,shell=False):
|
||||
if cmdline:
|
||||
args = list(args) + shlex.split(cmdline,posix=True)
|
||||
def _run_prog(self,cmdline="",*args,shell=False,timeout=60):
|
||||
if type(cmdline) == str:
|
||||
_process = shlex.split(cmdline,posix=True)
|
||||
else:
|
||||
_process = cmdline
|
||||
try:
|
||||
return subprocess.check_output(args,encoding="utf-8",shell=shell,stderr=subprocess.DEVNULL)
|
||||
return subprocess.check_output(_process,encoding="utf-8",shell=shell,stderr=subprocess.DEVNULL,timeout=timeout)
|
||||
except subprocess.CalledProcessError as e:
|
||||
return ""
|
||||
except subprocess.TimeoutExpired:
|
||||
return ""
|
||||
|
||||
def _run_cache_prog(self,cmdline="",cachetime=10,*args,shell=False):
|
||||
if type(cmdline) == str:
|
||||
_process = shlex.split(cmdline,posix=True)
|
||||
else:
|
||||
_process = cmdline
|
||||
_process_id = "".join(_process)
|
||||
_runner = self._check_cache.get(_process_id)
|
||||
if _runner == None:
|
||||
_runner = checkmk_cached_process(_process,shell=shell)
|
||||
self._check_cache[_process_id] = _runner
|
||||
return _runner.get(cachetime)
|
||||
|
||||
|
||||
class checkmk_cached_process(object):
|
||||
def __init__(self,process,shell=False):
|
||||
self._processs = process
|
||||
self._islocal = os.path.dirname(process[0]).startswith(LOCALDIR)
|
||||
self._shell = shell
|
||||
self._mutex = threading.Lock()
|
||||
with self._mutex:
|
||||
self._data = (0,"")
|
||||
self._thread = None
|
||||
|
||||
def _runner(self,timeout):
|
||||
try:
|
||||
_data = subprocess.check_output(self._processs,shell=self._shell,encoding="utf-8",stderr=subprocess.DEVNULL,timeout=timeout)
|
||||
except subprocess.CalledProcessError as e:
|
||||
_data = ""
|
||||
except subprocess.TimeoutExpired:
|
||||
_data = ""
|
||||
with self._mutex:
|
||||
self._data = (int(time.time()),_data)
|
||||
self._thread = None
|
||||
|
||||
def get(self,cachetime):
|
||||
with self._mutex:
|
||||
_now = time.time()
|
||||
_mtime = self._data[0]
|
||||
if _now - _mtime > cachetime or cachetime == 0:
|
||||
if not self._thread:
|
||||
if cachetime > 0:
|
||||
_timeout = cachetime*2-1
|
||||
else:
|
||||
_timeout = None
|
||||
with self._mutex:
|
||||
self._thread = threading.Thread(target=self._runner,args=[_timeout])
|
||||
self._thread.start()
|
||||
|
||||
self._thread.join(30) ## waitmax
|
||||
with self._mutex:
|
||||
_mtime, _data = self._data
|
||||
if not _data.strip():
|
||||
return ""
|
||||
if self._islocal:
|
||||
_data = "".join([f"cached({_mtime},{cachetime}) {_line}" for _line in _data.splitlines(True) if len(_line.strip()) > 0])
|
||||
else:
|
||||
_data = re.sub("\B[<]{3}(.*?)[>]{3}\B",f"<<<\\1:cached({_mtime},{cachetime})>>>",_data)
|
||||
return _data
|
||||
|
||||
class checkmk_server(TCPServer,checkmk_checker):
|
||||
def __init__(self,port,pidfile,user,**kwargs):
|
||||
def __init__(self,port,pidfile,user,onlyfrom=None,encryptionkey=None,**kwargs):
|
||||
self.pidfile = pidfile
|
||||
self.onlyfrom=onlyfrom.split(",") if onlyfrom else None
|
||||
self.encryptionkey = encryptionkey
|
||||
self._mutex = threading.Lock()
|
||||
self.user = pwd.getpwnam(user)
|
||||
self.allow_reuse_address = True
|
||||
@ -836,6 +980,11 @@ class checkmk_server(TCPServer,checkmk_checker):
|
||||
os.setgid(_gid)
|
||||
os.setuid(_uid)
|
||||
|
||||
def verify_request(self, request, client_address):
|
||||
if self.onlyfrom and client_address[0] not in self.onlyfrom:
|
||||
return False
|
||||
return True
|
||||
|
||||
def server_start(self):
|
||||
sys.stderr.write("starting checkmk_agent\n")
|
||||
sys.stderr.flush()
|
||||
@ -979,7 +1128,7 @@ class smart_disc(object):
|
||||
|
||||
def _get_data(self):
|
||||
try:
|
||||
self._smartctl_output = subprocess.check_output(["smartctl","-a","-n","standby", f"/dev/{self.device}"],encoding=sys.stdout.encoding)
|
||||
self._smartctl_output = subprocess.check_output(["smartctl","-a","-n","standby", f"/dev/{self.device}"],encoding=sys.stdout.encoding,timeout=10)
|
||||
except subprocess.CalledProcessError as e:
|
||||
if e.returncode & 0x1:
|
||||
raise
|
||||
@ -993,6 +1142,9 @@ class smart_disc(object):
|
||||
_status = "SMART Health Status: DISK FAILING"
|
||||
|
||||
self._smartctl_output += f"\n{_status}\n"
|
||||
except subprocess.TimeoutExpired:
|
||||
self._smartctl_output += "\nSMART smartctl Timeout\n"
|
||||
|
||||
|
||||
def __str__(self):
|
||||
_ret = []
|
||||
@ -1011,17 +1163,21 @@ if __name__ == "__main__":
|
||||
_parser.add_argument("--port",type=int,default=6556,
|
||||
help=_("Port checkmk_agent listen"))
|
||||
_parser.add_argument("--start",action="store_true",
|
||||
help=_(""))
|
||||
help=_("start the daemon"))
|
||||
_parser.add_argument("--stop",action="store_true",
|
||||
help=_(""))
|
||||
help=_("stop the daemon"))
|
||||
_parser.add_argument("--nodaemon",action="store_true",
|
||||
help=_(""))
|
||||
help=_("run in foreground"))
|
||||
_parser.add_argument("--status",action="store_true",
|
||||
help=_(""))
|
||||
help=_("show status if running"))
|
||||
_parser.add_argument("--user",type=str,default="root",
|
||||
help=_(""))
|
||||
_parser.add_argument("--encrypt",type=str,dest="encryptionkey",
|
||||
help=_("Encryption password (do not use from cmdline)"))
|
||||
_parser.add_argument("--pidfile",type=str,default="/var/run/checkmk_agent.pid",
|
||||
help=_(""))
|
||||
_parser.add_argument("--onlyfrom",type=str,
|
||||
help=_("comma seperated ip addresses to allow"))
|
||||
_parser.add_argument("--debug",action="store_true",
|
||||
help=_("debug Ausgabe"))
|
||||
args = _parser.parse_args()
|
||||
|
Loading…
Reference in New Issue
Block a user