From e62d7111cb848efbbce6733552d3a87534982f97 Mon Sep 17 00:00:00 2001 From: Cyberes Date: Fri, 21 Apr 2023 23:54:20 -0600 Subject: [PATCH] check opnsense traffic for host --- check_bandwidth.py | 3 +- check_opnsense_traffic_for_host.py | 130 +++++++++++++++++++++++++++++ requirements.txt | 3 +- 3 files changed, 134 insertions(+), 2 deletions(-) create mode 100755 check_opnsense_traffic_for_host.py diff --git a/check_bandwidth.py b/check_bandwidth.py index 4bc8cc8..b75df5f 100755 --- a/check_bandwidth.py +++ b/check_bandwidth.py @@ -58,7 +58,8 @@ def main(): elif bandwidth_utilization >= warn_value: warn.append(interface) state = 'warning' - exit_code = nagios.WARNING + if exit_code < nagios.WARNING: + exit_code = nagios.WARNING else: ok.append(interface) state = 'ok' diff --git a/check_opnsense_traffic_for_host.py b/check_opnsense_traffic_for_host.py new file mode 100755 index 0000000..f329481 --- /dev/null +++ b/check_opnsense_traffic_for_host.py @@ -0,0 +1,130 @@ +#!/usr/bin/env python3 +import argparse +import sys +import time +import traceback + +import hurry +import numpy as np +import requests +from hurry.filesize import size +from urllib3.exceptions import InsecureRequestWarning + +import checker.nagios as nagios +from checker.markdown import list_to_markdown_table + + +def filesize(bytes: int, spaces: bool = True): + x = size(bytes, system=hurry.filesize.alternative) + if spaces: + return x + else: + return x.replace(' ', '') + + +def main(): + parser = argparse.ArgumentParser(description='Check OPNsense network traffic for a host.') + parser.add_argument('--opnsense', required=True, help='OPNsense hostname or IP address.') + parser.add_argument('--key', required=True, help='OPNsense API key.') + parser.add_argument('--secret', required=True, help='OPNsense API secret.') + parser.add_argument('--interface', required=True, help='Interface to check (e.g., lan). Can be something like "lan,wan"') + parser.add_argument('--host', required=True, help='IP of the host to check.') + parser.add_argument('--duration', default=10, type=int, help='How many seconds to gather statistics.') + args = parser.parse_args() + + check_result = {} + interface_names = {} + + requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning) + + # Map interface names to their internal names + interfaces_mapping = requests.get(f'https://{args.opnsense}/api/diagnostics/traffic/interface', + headers={'Accept': 'application/json'}, auth=(args.key, args.secret), verify=False) + if interfaces_mapping.status_code != 200: + print(f'UNKNOWN: unable to query OPNsense API for interface mappings: {interfaces_mapping.status_code}\n{interfaces_mapping.text}') + sys.exit(nagios.UNKNOWN) + interfaces_mapping = interfaces_mapping.json()['interfaces'] + interfaces_to_check = set(args.interface.split(',')) + for name, interface in interfaces_mapping.items(): + if interface['name'] in interfaces_to_check: + interfaces_to_check.remove(interface['name']) + interface_names[interface['name']] = name + + if not len(interface_names.keys()): + print(f'UNKNOWN: did not find any valid interface names! Double-check the name.') + sys.exit(nagios.UNKNOWN) + + for name, interface in interface_names.items(): + # Fetch the data + # TODO: account for network delays for the check duration + traffic_data = [] + for _ in range(args.duration): + response = requests.get(f'https://{args.opnsense}/api/diagnostics/traffic/top/{interface}', + headers={'Accept': 'application/json'}, auth=(args.key, args.secret), verify=False) + if response.status_code != 200: + print(f'UNKNOWN: unable to query OPNsense API for {interface}: {response.status_code}\n{response.text}') + sys.exit(nagios.UNKNOWN) + for item in response.json().get(interface, {}).get('records', False): + if item['address'] == args.host: + traffic_data.append(item) + time.sleep(1) + + if not len(traffic_data): + print(f'UNKNOWN: Interface {args.interface} not found in OPNsense API response.') + sys.exit(nagios.UNKNOWN) + + check_result[name] = { + 'rate_in': np.average([x['rate_bits_in'] for x in traffic_data]), + 'rate_out': np.average([x['rate_bits_out'] for x in traffic_data]), + 'cumulative_in': np.average([x['cumulative_bytes_in'] for x in traffic_data]), + 'cumulative_out': np.average([x['cumulative_bytes_out'] for x in traffic_data]), + 'connections': int(np.average([len(x['details']) for x in traffic_data])) + } + + # TODO: figure out status + print('OK: no metrics defined.') + warn_value = 0 + crit_value = 0 + + exit_code = nagios.OK + critical = [] + warn = [] + ok = [] + perf_data = [] + + output_table = [('Interface', 'Rate In', 'Rate Out', 'Cumulative In', 'Cumulative Out', 'Connections', 'Status')] + for name, data in check_result.items(): + # TODO: figure out status + if -1 >= crit_value: + critical.append(name) + status = '[CRITICAL]' + exit_code = nagios.CRITICAL + elif -1 >= warn_value: + warn.append(name) + status = '[WARNING]' + exit_code = nagios.WARNING + else: + ok.append(name) + status = '[OK]' + + perf_data.append(f'{name}-rate_in={filesize(data["rate_in"], spaces=False)};{warn_value};{crit_value};') + perf_data.append(f'{name}-rate_out={filesize(data["rate_out"], spaces=False)};{warn_value};{crit_value};') + perf_data.append(f'{name}-cumulative_in={filesize(data["cumulative_in"], spaces=False)};{warn_value};{crit_value};') + perf_data.append(f'{name}-cumulative_out={filesize(data["cumulative_out"], spaces=False)};{warn_value};{crit_value};') + perf_data.append(f'{name}-connections={data["connections"]};{warn_value};{crit_value};') + + output_table.append((name, filesize(data['rate_in']), filesize(data['rate_out']), filesize(data['cumulative_in']), filesize(data['cumulative_out']), + data['connections'], status)) + print(list_to_markdown_table(output_table, align='left', seperator='!', borders=False)) + + print(f' |{" ".join(perf_data)}') + sys.exit(exit_code) + + +if __name__ == "__main__": + try: + main() + except Exception as e: + print(f'UNKNOWN: exception "{e}"') + print(traceback.format_exc()) + sys.exit(nagios.UNKNOWN) diff --git a/requirements.txt b/requirements.txt index 332ba10..58d2517 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,4 +11,5 @@ icinga2api~=0.6.1 urllib3~=1.26.14 aiofiles~=0.6.0 markdown~=3.4.1 -psutil~=5.9.4 \ No newline at end of file +psutil~=5.9.4 +hurry.filesize \ No newline at end of file