From patchwork Thu Jul 30 19:15:52 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Robin Roevens X-Patchwork-Id: 10080 Return-Path: Received: from mail01.ipfire.org (mail01.haj.ipfire.org [172.28.1.202]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519) (Client CN "mail01.haj.ipfire.org", Issuer "YR2" (not verified)) by web04.haj.ipfire.org (Postfix) with ESMTPS id 4hB0KB28m1z3wqJ for ; Thu, 30 Jul 2026 19:54:38 +0000 (UTC) Received: from mail02.haj.ipfire.org (mail02.haj.ipfire.org [IPv6:2001:678:b28::201]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519) (Client CN "mail02.haj.ipfire.org", Issuer "YE1" (not verified)) by mail01.ipfire.org (Postfix) with ESMTPS id 4hB0K20qKWz2V for ; Thu, 30 Jul 2026 19:54:30 +0000 (UTC) Received: from mail02.haj.ipfire.org (localhost [IPv6:::1]) by mail02.haj.ipfire.org (Postfix) with ESMTP id 4hB0GZ45W2z37BM for ; Thu, 30 Jul 2026 19:52:22 +0000 (UTC) X-Original-To: development@lists.ipfire.org Received: from mail01.ipfire.org (mail01.haj.ipfire.org [IPv6:2001:678:b28::25]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519) (Client CN "mail01.haj.ipfire.org", Issuer "YR2" (not verified)) by mail02.haj.ipfire.org (Postfix) with ESMTPS id 4hB0GK1gfWz36WH for ; Thu, 30 Jul 2026 19:52:09 +0000 (UTC) Received: from layka.disroot.org (layka.disroot.org [178.21.23.139]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519) (Client did not present a certificate) by mail01.ipfire.org (Postfix) with ESMTPS id 4hB0G904XQz44j for ; Thu, 30 Jul 2026 19:52:00 +0000 (UTC) Authentication-Results: mail01.ipfire.org; dkim=pass header.d=disroot.org header.s=mail header.b=G4b32QcR; spf=pass (mail01.ipfire.org: domain of robin.roevens@disroot.org designates 178.21.23.139 as permitted sender) smtp.mailfrom=robin.roevens@disroot.org; dmarc=pass (policy=reject) header.from=disroot.org ARC-Seal: i=1; a=rsa-sha256; d=lists.ipfire.org; s=202003rsa; cv=none; t=1785441121; b=fjnb9K7sEKY0wDMX03/yhKMXt2oaCgJ5EX4/EGUmOIb/dizD7BBZWeMo0iVhwpDRv5a55H jsTrA5letqaIF+whA0YTjIhyw3av+3eIyHfCrbGTUncvAjAao3+68tTcoPazhV90bumfDV jYIRLwG2XE1iEBz2iy7y5+R+rFzlXylWsUb2oOLOy0rY+k4ULFs0EptSS/pa1CzDZ4JbfR Qyolns34IuoFWHmxDBV7aWkmzsAQbMT5WxX+P9WBkYncJfczMOCEyk+RIOmPUs+f58MYpQ /iKB0ivyJGAef9dcik0GU70SvEywtChNlsznzBkxEtoYmpQg6mzQLBmANPqoMw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=lists.ipfire.org; s=202003rsa; t=1785441121; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references:dkim-signature; bh=k6QGlpT8SYjbpDIMY7vzyzvv39dOgyQz6sNVOdZld90=; b=wnZUudMnS/KzSdE6gWtsGNcA8b4J4WT9t9NbKxYTuk300+ZiVdIFH0B2kVjZRwD/FCGqBb k4F4IGwRnOkmfYl5jPQZsda41n7pDJSLL1o41ag0sWNh1B0fB7xiJVkKr6A5gX3ntTjgcU iqVOBsj3Ca71pRydiBznvO4JzPlNxfhzVoE8MopIGISj50Wd+LbmmjYTtexY6QQ7Po06gf 6UiOhN+7kLx/FmzyG/HPXODd+8t63O1gcNnn4vpnwWxBmoCTBpc9kAU8gEyReKNRBH70ce KMTu7e/mjjv1IAbhu5BvdByUWA512f1sTvnSrgNYljn1Ig8S75wyZ7Nvuv/cCQ== ARC-Authentication-Results: i=1; mail01.ipfire.org; dkim=pass header.d=disroot.org header.s=mail header.b=G4b32QcR; spf=pass (mail01.ipfire.org: domain of robin.roevens@disroot.org designates 178.21.23.139 as permitted sender) smtp.mailfrom=robin.roevens@disroot.org; dmarc=pass (policy=reject) header.from=disroot.org Received: from mail01.layka.lan (localhost [127.0.0.1]) by disroot.org (Postfix) with ESMTP id 9B74D41BD3 for ; Thu, 30 Jul 2026 21:52:00 +0200 (CEST) X-Virus-Scanned: SPAM Filter at disroot.org Received: from layka.disroot.org ([127.0.0.1]) by localhost (disroot.org [127.0.0.1]) (amavis, port 10024) with ESMTP id RrU967xW71KN for ; Thu, 30 Jul 2026 21:52:00 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=disroot.org; s=mail; t=1785441119; bh=BdreWelglfM5jtJ5dWS9JLfdBOnXMnBtTMhPeohRZ/0=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=G4b32QcReCU2dPsyNYirX2Gi0GVAxtNpxY6PfsBeJLZzX8nCxnlY8hJF4HybX8H5e 0HjmeQNVeedzlItvt3Q+K0FknSsscPCJ10D/sEb1fsm0WUJGBD8GQLH7PiCUS6M+Tw y52Llj+UmqnjmM9sEfq+DX/HNw8IzvEiAzyuoz5PUX1Aoaq+3J+l5qOqCALP9H1F0w i7l9uBsrtY6fiVOpK6tO7tbCn3RVm9n3Kj0txO5tbL0EqO41Ev7gtiv2dfK4qvAKrQ PIJYLOCAkjWNEeQ0HvJ+7WAAdMhaUdtZbf3gETYcK7SYaaz0pZY6nPvJbDZ8m/aKDe vUe1payMi3T8Q== Received: from chojin.roevenslambrechts.be (chojin.roevenslambrechts.be [192.168.0.50]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) (no client certificate requested) (Authenticated sender) by hachiman (MailScanner Milter) with SMTP id ED4F4585FDA; Thu, 30 Jul 2026 21:51:53 +0200 (CEST) From: Robin Roevens To: development@lists.ipfire.org Cc: Robin Roevens Subject: [PATCH 1/5] Initialize async zabbix sender from zabbix_utils Date: Thu, 30 Jul 2026 21:15:52 +0200 Message-ID: <20260730195148.3278295-2-robin.roevens@disroot.org> In-Reply-To: <20260730195148.3278295-1-robin.roevens@disroot.org> References: <20260730195148.3278295-1-robin.roevens@disroot.org> Precedence: list List-Id: List-Subscribe: , List-Unsubscribe: , List-Post: List-Help: Sender: Mail-Followup-To: MIME-Version: 1.0 X-RoevensLambrechts-MailScanner-ID: ED4F4585FDA.AD543 X-RoevensLambrechts-MailScanner: Found to be clean X-RoevensLambrechts-MailScanner-From: robin.roevens@disroot.org X-RoevensLambrechts-MailScanner-Watermark: 1786045915.32049@4M0wFwCqx7fEdv+b8jQPvg X-Rspamd-Server: mail01.haj.ipfire.org X-Rspamd-Queue-Id: 4hB0G904XQz44j X-Rspamd-Action: no action X-Spamd-Result: default: False [-5.63 / 11.00]; BAYES_HAM(-3.00)[100.00%]; R_DKIM_ALLOW(-1.65)[disroot.org:s=mail]; MID_CONTAINS_FROM(1.00)[]; DKIM_REPUTATION(-0.92)[-0.92153870218341]; SPF_REPUTATION_HAM(-0.65)[-0.65402885146808]; DMARC_POLICY_ALLOW(-0.50)[disroot.org,reject]; R_MISSING_CHARSET(0.50)[]; R_SPF_ALLOW(-0.20)[+a:c]; MIME_GOOD(-0.10)[text/plain]; MX_GOOD(-0.10)[disroot.org]; RCPT_COUNT_TWO(0.00)[2]; ASN(0.00)[asn:50673, ipnet:178.21.23.0/24, country:NL]; IP_REPUTATION_HAM(0.00)[asn: 50673(0.00), country: NL(-0.01), ip: 178.21.23.139(0.00)]; ARC_NA(0.00)[]; TO_DN_SOME(0.00)[]; MIME_TRACE(0.00)[0:+]; RCVD_COUNT_THREE(0.00)[3]; RCVD_TLS_LAST(0.00)[]; TO_MATCH_ENVRCPT_SOME(0.00)[]; MISSING_XM_UA(0.00)[]; FROM_EQ_ENVFROM(0.00)[]; DKIM_TRACE(0.00)[disroot.org:+]; ARC_SIGNED(0.00)[lists.ipfire.org:s=202003rsa:i=1]; PREVIOUSLY_DELIVERED(0.00)[development@lists.ipfire.org]; FROM_HAS_DN(0.00)[] When Zabbix is enabled in new config section [zabbix], and the zabbix_utils python module is available, a zabbix AsyncSender object will be initialized for sending alerts to Zabbix using parameters from the new config section. Signed-off-by: Robin Roevens --- src/reporter.conf.in | 23 +++++++++++++++++++++++ src/suricata-reporter.in | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/src/reporter.conf.in b/src/reporter.conf.in index 5943006..bab01b6 100644 --- a/src/reporter.conf.in +++ b/src/reporter.conf.in @@ -45,3 +45,26 @@ ; 3 = Low Severity ; 4 = Informational ;severity = 3 + +[zabbix] +; Enable sending alerts to Zabbix +;enabled = false + +; Path to the Zabbix agent configuration file +;zabbix_agentd_config = /etc/zabbix_agentd/zabbix_agentd.conf + +; Zabbix server ip or hostname (required if zabbix_agentd_config is not set) +;zabbix_server_host = 127.0.0.1 + +; Zabbix server port (defaults to 10051 if not set) +;zabbix_server_port = 10051 + +; Hostname as defined in Zabbix server to send alerts to (defaults to either the +; Hostname directive in Zabbix Agent config or system hostname) +;alert_item_hostname = IPFire + +; Zabbix item key to send alerts to +;alert_item_key = ipfire.suricata.event.get + +; Max age (seconds) to retry sending alerts to Zabbix +;alert_max_age = 3600 \ No newline at end of file diff --git a/src/suricata-reporter.in b/src/suricata-reporter.in index 28b55bc..f9da7b4 100644 --- a/src/suricata-reporter.in +++ b/src/suricata-reporter.in @@ -37,6 +37,13 @@ import socket import sqlite3 import sys +# Load zabbix_utils module if available +zabbix_utils_available = True +try: + from zabbix_utils import AsyncSender, ItemValue +except ImportError: + zabbix_utils_available = False + # Fetch the hostname HOSTNAME = socket.gethostname() @@ -75,6 +82,10 @@ class Reporter(object): # Remember the last time the database was cleaned self.last_cleanup_at = None + # Initialize Zabbix sender + self.zabbix_sender = None + self.init_zabbix_sender() + # Register any signals for signo in (signal.SIGINT, signal.SIGTERM): self.loop.add_signal_handler(signo, self.terminate) @@ -97,6 +108,32 @@ class Reporter(object): return config + def init_zabbix_sender(self): + """ + Initialize the Zabbix async sender if configured + """ + if not self.config.getboolean('zabbix', 'enabled', fallback=False): + return + + if not zabbix_utils_available: + log.error("zabbix-utils is not installed. Zabbix alerts will not be sent.") + return + + zabbix_config = self.config.get('zabbix', 'zabbix_agentd_config', fallback='') + zabbix_server_host = self.config.get('zabbix', 'zabbix_server_host', fallback='') + zabbix_server_port = self.config.getint('zabbix', 'zabbix_server_port', fallback=10051) + + if zabbix_config: + if not os.path.isfile(zabbix_config): + log.error(f"Zabbix agent config file {zabbix_config} does not exist.") + return + self.zabbix_sender = AsyncSender(use_config=True, config_path=zabbix_config) + else: + if not zabbix_server_host: + log.error("zabbix_server_host must be specified when zabbix_agentd_config is not provided.") + return + self.zabbix_sender = AsyncSender(server=zabbix_server_host, port=zabbix_server_port) + def _open_database(self): """ Opens the database