diff --git a/README.md b/README.md index acbb0bc..3287897 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,126 @@ -Wazuh notifier +# Wazuh notifier -Wazuh notifier enables the Wazuh user to be notified when selected events occur. -It combines a customized custom-ar Python script ( -ref: https://documentation.wazuh.com/current/user-manual/capabilities/active-response/custom-active-response-scripts.html) -with two notifier Python scripts: a Discord notifier and a NTFY.sh notifier. +Wazuh notifier enables the Wazuh manager to be notified when selected events occur. -It is a Stateless implementation and only notifies, using any or both of the messaging services. +## Contents -The ossec.conf configuration needs to include the following command and active-response configuration: - - -linux-custom-ar -custom-ar.py -yes - +The main script is a custom active response Python script: wazuh-active-response.py.
+The actual sending of the messages is done by 2 notifier Python scripts:
+**Discord notifier**: wazuh-discord-notifier.py, and **NTFY.sh notifier**: wazuh-ntfy-notifier.py
+A YAML configuration: wazuh-notifier-config.yaml, and a Python module: wazuh_notifier_lib.py +Wazuh notifier is a stateless implementation and only notifies, using the Discord and/or NTFY.sh messaging services. + +The Wazuh notifier is triggered by configuring the **ossec.conf** and adding an **active response configuration.** + +## Installation ## + +### Step 1 ### + +Download the files from https://github.com/RudiKlein/wazuh-notifier to your server. + +### Step 2 ### + +Copy the 4 Python files to the /var/ossec/active-response/bin/ folder + +``` +$ cp /wazuh-*.py /var/ossec/active-response/bin/ +``` + +Set the correct ownership + +``` +$ chown root:wazuh /var/ossec/active-response/bin/wazuh-*.py +``` + +Set the correct permissions + +``` +$ chmod uog+rx /var/ossec/active-response/bin/wazuh-*.py +``` + +### Step 3 ### + +Copy the YAML file to /var/ossec/etc/ + +``` +$ cp /wazuh-notifier-config.yaml /var/ossec/etc/ +``` + +Set the correct ownership + +``` +$ chown root:wazuh /var/ossec/etc/wazuh-notifier-config.yaml +``` + +Set the correct permissions + +``` +$ chmod uog+r /var/ossec/etc/wazuh-notifier-config.yaml +``` + +### Step 4 ### + +Modify the ossec.conf configuration file and add the following
+ +``` + + wazuh-active-response + wazuh-active-response.py + yes + +``` + +``` - no - linux-custom-ar - local - 503 - 60 + wazuh-active-response + server + + -
+``` + +Add the rules you want to be informed about between the , with the rules id's seperated by comma's. +Example: 5402, 3461, 8777
+(Please refer to the Wazuh online documentation for more information [^Wazuh docs]) + +[^Wazuh docs]: https://documentation.wazuh.com/current/user-manual/capabilities/active-response/index.html + +## The Active Response module ## + +The wazuh-active-response.py acts as the interface between Wazuh and the messaging notifiers for Discord and ntfy. +It is based on the example active response Python script in the [^Wazuh docs]. + +## The Discord notifier ## + +## The ntfy.sh notifier ## + +## The YAML configuration ## + +**Enable/disable the notifiers**
+ +``` +discord_enabled: 1 (0 if not set in the yaml configuration) +ntfy_enabled: 1 (0 if not set in the yaml configuration) +``` + +**Exclude rules that are enabled in the ossec.conf active response definition.**
+This prevents the need to alter the ossec.conf for temporary rule disabling and stopping/starting wazuh-manager. +Additionally, agents can also be excluded from notifications. + +``` +excluded_rules: "5401, 5402, 5403" +excluded_agents: "999" +``` + +Default settings for the ntfy notifier. This overrules the hardcoded defaults. + +``` +ntfy_server: "https://ntfy.sh/" +ntfy_sender: "Wazuh (IDS)" +ntfy_destination: "__KleinTest" +ntfy_priority: "5" +ntfy_message: "Test message" +ntfy_tags: "information, testing, yaml" +ntfy_click: "https://google.com" +``` \ No newline at end of file diff --git a/custom-active-response.py b/wazuh-active-response.py similarity index 89% rename from custom-active-response.py rename to wazuh-active-response.py index cf9b80a..cf9db3c 100755 --- a/custom-active-response.py +++ b/wazuh-active-response.py @@ -27,6 +27,8 @@ from pathlib import PureWindowsPath, PurePosixPath from wazuh_notifier_lib import import_config as ic from wazuh_notifier_lib import set_env as se +# Some variable assignments + wazuh_path, ar_path, config_path = se() ADD_COMMAND = 0 @@ -123,14 +125,13 @@ def send_keys_and_check_message(argv, keys): return ret -def parameters_deconstruct(event_keys): +def parameters_deconstruct(argv, event_keys): a_id: str = str(event_keys["agent"]["id"]) a_name: str = str(event_keys["agent"]["name"]) - e_level: str = str(event_keys["rule"]["level"]) - e_description: str = str(event_keys["rule"]["description"]) e_id: str = str(event_keys["rule"]["id"]) + e_description: str = str(event_keys["rule"]["description"]) + e_level: str = str(event_keys["rule"]["level"]) e_fired_times: str = str(event_keys["rule"]["firedtimes"]) - e_full_event: str = str(json.dumps(event_keys, indent=0).replace('"', '') .replace('{', '') .replace('}', '') @@ -140,10 +141,16 @@ def parameters_deconstruct(event_keys): .replace(' ', '') ) - return a_id, a_name, e_id, e_description, e_level, e_fired_times, e_full_event + if e_id in ic("excluded_rules") or a_id in ic("excluded_agents"): + + write_debug_file(argv[0], "Excluded rule or agent: " + e_id + "/" + a_id) + + else: + + return a_id, a_name, e_id, e_description, e_level, e_fired_times, e_full_event -def construct_basic_message(accent: str, a_id: str, a_name: str, e_id: str, e_description: str, e_level: str, +def construct_basic_message(argv, accent: str, a_id: str, a_name: str, e_id: str, e_description: str, e_level: str, e_fired_times: str): # Adding the BOLD text string to the Discord message. Ntfy has a different message format. @@ -178,8 +185,8 @@ def main(argv): alert = msg.alert["parameters"]["alert"] keys = [alert["rule"]] - agent_id, agent_name, event_level, event_description, event_id, event_fired_times, event_full_message = \ - parameters_deconstruct(alert) + agent_id, agent_name, event_id, event_description, event_level, event_fired_times, event_full_message = \ + parameters_deconstruct(argv, alert) action = send_keys_and_check_message(argv, keys) @@ -201,8 +208,9 @@ def main(argv): discord_notifier = '{0}/active-response/bin/wazuh-discord-notifier.py'.format(wazuh_path) discord_exec = "python3 " + discord_notifier + " " write_debug_file(argv[0], "Start Discord notifier") - discord_message = construct_basic_message(accent, agent_id, agent_name, event_level, event_description, - event_id, event_fired_times) + discord_message = construct_basic_message(argv, accent, agent_id, agent_name, event_id, event_description, + event_level, event_fired_times) + if ic("discord_full_message") == "1": discord_message = discord_message + "\n" + accent + "__Full event__" + accent + event_full_message + '"' @@ -216,7 +224,7 @@ def main(argv): ntfy_notifier = '{0}/active-response/bin/wazuh-ntfy-notifier.py'.format(wazuh_path) ntfy_exec = "python3 " + ntfy_notifier + " " write_debug_file(argv[0], "Start NTFY notifier") - ntfy_message = construct_basic_message(accent, agent_id, agent_name, event_level, event_description, + ntfy_message = construct_basic_message(argv, accent, agent_id, agent_name, event_level, event_description, event_id, event_fired_times) # If the full message flag is set, the full message PLUS the closing parenthesis will be added diff --git a/wazuh-discord-notifier.py b/wazuh-discord-notifier.py index 0e0872e..b96b798 100755 --- a/wazuh-discord-notifier.py +++ b/wazuh-discord-notifier.py @@ -10,7 +10,7 @@ # License (version 2) as published by the FSF - Free Software # Foundation. # -# This script is executed by the active response script (custom-active-response.py), which is triggered by rules firing. +# This script is executed by the active response script (wazuh-active-response.py), which is triggered by rules firing. # # Discord is a voice, video and text communication service used by over a hundred million people to hang out and talk # with their friends and communities. It allows for receiving message using webhooks. @@ -37,11 +37,13 @@ now_message, now_logging = st() # Retrieve webhook from .env -# Catching some errors +# Catching some path errors. try: dotenv_path = join(dirname(__file__), '.env') load_dotenv(dotenv_path) - if not os.path.isfile(dotenv_path): raise Exception(dotenv_path, "file not found") + if not os.path.isfile(dotenv_path): + raise Exception(dotenv_path, "file not found") + discord_webhook = os.getenv("DISCORD_WEBHOOK") except Exception as err: @@ -91,14 +93,20 @@ click = d_click if (ic("discord_click") is None) else ic("discord_click") help_text: str = """ -u, --server is the webhook URL of the Discord server. It is stored in .env. - -s, --sender is the sender of the message, either an app name or a person. The default is "Security message". - -d, --destination is the destination (actually the originator) of the message, either an app name or a person. Default is "Wazuh (IDS)" - -p, --priority is the priority of the message, ranging from 1 (highest), to 5 (lowest). Default is 5. - -m, --message is the text of the message to be sent. Default is "Test message", but may include --tags and/or --click. - -t, --tags is an arbitrary strings of tags (keywords), seperated by a "," (comma). Default is "informational, testing, hard-coded". - -c, --click is a link (URL) that can be followed by tapping/clicking inside the message. Default is https://google.com. - -h, --help shows this help message. Must have no value argument. - -v, --view show config. + -s, --sender is the sender of the message, either an app name or a person. + The default is "Security message". + -d, --destination is the destination (actually the originator) of the message, either an app name or a person. + Default is "Wazuh (IDS)" + -p, --priority is the priority of the message, ranging from 1 (highest), to 5 (lowest). + Default is 5. + -m, --message is the text of the message to be sent. + Default is "Test message", but may include --tags and/or --click. + -t, --tags is an arbitrary strings of tags (keywords), seperated by a "," (comma). + Default is "informational, testing, hard-coded". + -c, --click is a link (URL) that can be followed by tapping/clicking inside the message. + Default is https://google.com. + -h, --help Shows this help message. + -v, --view Show yaml configuration. """ # Get params during execution. Params found here, override minimal defaults and/or config settings. diff --git a/wazuh-notifier-config.yaml b/wazuh-notifier-config.yaml index da561a3..89f2f74 100755 --- a/wazuh-notifier-config.yaml +++ b/wazuh-notifier-config.yaml @@ -10,6 +10,11 @@ discord_enabled: 1 ntfy_enabled: 1 +# Exclude rules that are listed in the ossec.conf active response definition. + +excluded_rules: "5401, 5402, 5403" +excluded_agents: "999" + # COMMON configuration settings end here. @@ -35,7 +40,7 @@ ntfy_tags: "information, testing, yaml" ntfy_click: "https://google.com" # 1 to send the full event data with the message. 0 only sends the message with basic details -ntfy_full_message: "1" +ntfy_full_message: "0" # NTFY configuration settings end here. diff --git a/wazuh-ntfy-notifier.py b/wazuh-ntfy-notifier.py index 68c695a..f037fe8 100755 --- a/wazuh-ntfy-notifier.py +++ b/wazuh-ntfy-notifier.py @@ -10,20 +10,21 @@ # License (version 2) as published by the FSF - Free Software # Foundation. # -# This script is executed by the active response script (custom-active-response.py), which is triggered by rules firing. +# This script is executed by the active response script (wazuh-active-response.py), which is triggered by rules firing. # # ntfy (pronounced notify) is a simple HTTP-based pub-sub notification service. # It allows you to send notifications to your phone or desktop via scripts from any computer, and/or using a REST API. # It's infinitely flexible, and 100% free software. For more information: https://ntfy.sh. -import json -import requests import getopt +import json import sys +import requests + +from wazuh_notifier_lib import import_config as ic from wazuh_notifier_lib import set_env as se from wazuh_notifier_lib import set_time as st -from wazuh_notifier_lib import import_config as ic from wazuh_notifier_lib import view_config as vc # Get path values