Skip to content

Commit

Permalink
add: Add limit EPS test #2947
Browse files Browse the repository at this point in the history
  • Loading branch information
fedepacher committed Aug 17, 2022
1 parent c68b312 commit 0097ffb
Show file tree
Hide file tree
Showing 42 changed files with 2,124 additions and 20 deletions.
21 changes: 21 additions & 0 deletions deps/wazuh_testing/wazuh_testing/modules/eps/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import os
import json
from datetime import datetime, timedelta
from copy import deepcopy

from wazuh_testing.tools.time import parse_date_time_format


# Timeouts
T_5 = 5
T_10 = 10
T_15 = 15
T_20 = 20
T_60 = 60

ANALYSISD_PREFIX = r'.*wazuh-analysisd.*'
MAILD_PREFIX = r'.*wazuh-maild.*'
# wazuh-analysisd.state file default update configuration
ANALYSISD_STATE_INTERNAL_DEFAULT = '5'
PERCENTAGE_PROCESS_MSGS = 0.95
QUEUE_SIZE = 16384
120 changes: 120 additions & 0 deletions deps/wazuh_testing/wazuh_testing/modules/eps/event_monitor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import re
from datetime import datetime

from wazuh_testing.modules import eps as eps
from wazuh_testing.tools import LOG_FILE_PATH, ANALYSISD_STATE, ALERT_LOGS_PATH
from wazuh_testing.tools.monitoring import FileMonitor


def make_analysisd_callback(pattern, prefix=eps.ANALYSISD_PREFIX):
"""Create a callback function from a text pattern.
It already contains the vulnerability-detector prefix.
Args:
pattern (str): String to match on the log.
prefix (str): regular expression used as prefix before the pattern.
Returns:
lambda: function that returns if there's a match in the file
Examples:
>>> callback_bionic_update_started = make_vuln_callback("Starting Ubuntu Bionic database update")
"""
pattern = r'\s+'.join(pattern.split())
regex = re.compile(r'{}{}'.format(prefix, pattern))

return lambda line: regex.match(line) is not None


def check_analysisd_event(file_monitor=None, callback='', error_message=None, update_position=True,
timeout=eps.T_60, prefix=eps.ANALYSISD_PREFIX, accum_results=1,
file_to_monitor=LOG_FILE_PATH):
"""Check if a analysisd event occurs
Args:
file_monitor (FileMonitor): FileMonitor object to monitor the file content.
callback (str): log regex to check in Wazuh log
error_message (str): error message to show in case of expected event does not occur
update_position (boolean): filter configuration parameter to search in Wazuh log
timeout (str): timeout to check the event in Wazuh log
prefix (str): log pattern regex
accum_results (int): Accumulation of matches.
"""
file_monitor = FileMonitor(file_to_monitor) if file_monitor is None else file_monitor
error_message = f"Could not find this event in {file_to_monitor}: {callback}" if error_message is None else \
error_message

file_monitor.start(timeout=timeout, update_position=update_position, accum_results=accum_results,
callback=make_analysisd_callback(callback, prefix), error_message=error_message)


def check_eps_disabled():
"""Check if the eps module is disabled"""
check_analysisd_event(callback=fr'.*INFO: EPS limit disabled.*', timeout=eps.T_10)


def check_eps_enabled(maximun, timeframe):
"""Check if the eps module is enable"""
check_analysisd_event(callback=fr".*INFO: EPS limit enabled, EPS: '{maximun}', timeframe: '{timeframe}'",
timeout=eps.T_10)


def check_configuration_error():
"""Check the configuration error event in ossec.log"""
check_analysisd_event(timeout=eps.T_10, callback=r".* \(\d+\): Configuration error at.*",
error_message="Could not find the event 'Configuration error at 'etc/ossec.conf' "
'in ossec.log', prefix=eps.MAILD_PREFIX)


def get_words_from_file(words, filename):
"""Get the words from file
Args:
wordss (str): Word to find in the file
Returns:
str: Line that match in file
"""
with open(filename, 'r') as file:
for _, line in enumerate(file):
# search string
if words in line:
return line


def get_analysisd_state(word):
"""Get the value of word in wazuh-analysisd.state
Args:
word (str): Word to find in the file
"""
line = get_words_from_file(word, ANALYSISD_STATE)
return float(line.split("\'")[1::2][0])


def get_alert_timestamp(start_log, end_log):
"""Get the timestamp of the alert if exist in the alerts.log file between two string
Args:
start_log (str): Start message to find
end_log (str): End message to find
"""
with open(ALERT_LOGS_PATH, 'r') as file:
str_file = file.read()
index1 = str_file.find(end_log)
index2 = str_file[0: index1].rfind(start_log)
str_alert = str_file[index2: index1]
timestamp = str_alert[str_alert.find(start_log) + len(start_log):str_alert.find(': ')]

return datetime.fromtimestamp(float(timestamp)).strftime('%Y-%m-%d %H:%M:%S')


def get_msg_with_number(message):
"""Check if the alerts.log file contains the message
Args:
message (str): Message to find
"""
check_analysisd_event(timeout=eps.T_20, callback=message,
error_message="Could not find the event in alerts.log", prefix="", \
file_to_monitor=ALERT_LOGS_PATH)
10 changes: 10 additions & 0 deletions deps/wazuh_testing/wazuh_testing/processes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,13 @@
def check_if_modulesd_is_running():
"""Check if modulesd daemon is running"""
assert check_if_process_is_running('wazuh-modulesd'), 'wazuh-modulesd is not running. It may have crashed'


def check_if_deamon_is_running(daemon):
"""Check if the specified daemon is running"""
assert check_if_process_is_running(daemon), f"{daemon} is not running. It may have crashed"


def check_if_deamon_is_not_running(daemon):
"""Check if the specified daemon is running"""
assert check_if_process_is_running(daemon) == False, f"{daemon} is running. It may have crashed"
49 changes: 34 additions & 15 deletions deps/wazuh_testing/wazuh_testing/scripts/simulate_agents.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,16 @@ def process_script_parameters(args):
args (argparse.Namespace): Script args.
"""
# Add keepalive and receive_message modules if they are not specified in script parameters
if 'keepalive' not in args.modules:
args.modules.append('keepalive')
args.modules_eps.append('0')
if None == args.disable_keepalive:
if 'keepalive' not in args.modules:
args.modules.append('keepalive')
args.modules_eps.append('0')

if 'receive_messages' not in args.modules:
args.modules.append('receive_messages')
args.modules_eps.append('0')

if None == args.disable_receive:
if 'receive_messages' not in args.modules:
args.modules.append('receive_messages')
args.modules_eps.append('0')


def set_agent_modules_and_eps(agent, active_modules, modules_eps):
Expand Down Expand Up @@ -129,13 +132,14 @@ def create_agents(args):
return agents


def create_injectors(agents, manager_address, protocol):
def create_injectors(agents, manager_address, protocol, limit_msg):
"""Create injectos objects from list of agents and connection parameters.
Args:
agents (list): List of agents to create the injectors (1 injector/agent).
manager_address (str): Manager IP address to connect the agents.
protocol (str): TCP or UDP protocol to connect the agents to the manager.
limit_msg (int): Maximun amount of message to be sent.
Returns:
list: List of injector objects.
Expand All @@ -146,12 +150,12 @@ def create_injectors(agents, manager_address, protocol):

for agent in agents:
sender = ag.Sender(manager_address, protocol=protocol)
injectors.append(ag.Injector(sender, agent))
injectors.append(ag.Injector(sender, agent, limit_msg))

return injectors


def start(injector, time_alive):
def start(injector, time_alive, flag_disable_keepalive):
"""Start the injector process for a specified time.
Args:
Expand All @@ -160,7 +164,10 @@ def start(injector, time_alive):
"""
try:
injector.run()
sleep(time_alive)
if not flag_disable_keepalive:
sleep(time_alive)
else:
injector.wait()
finally:
stop(injector)

Expand All @@ -174,7 +181,7 @@ def stop(injector):
injector.stop_receive()


def run(injectors, time_alive):
def run(injectors, time_alive, flag_disable_keepalive):
"""Run each injector in a separated process.
Args:
Expand All @@ -184,7 +191,7 @@ def run(injectors, time_alive):
processes = []

for injector in injectors:
processes.append(Process(target=start, args=(injector, time_alive)))
processes.append(Process(target=start, args=(injector, time_alive, flag_disable_keepalive)))

for agent_process in processes:
agent_process.start()
Expand Down Expand Up @@ -322,6 +329,18 @@ def main():
help='Waiting time in seconds between agent registration and the sending of events.',
required=False, default=0, dest='waiting_connection_time')

arg_parser.add_argument('-e', '--limit-msg', metavar='<limit_msg>', type=int,
help='Amount of message to sent.',
required=False, default=None, dest='limit_msg')

arg_parser.add_argument('-k', '--disable-keepalive', metavar='<disable_keepalive>', type=bool,
help='Disable keepalive module',
required=False, default=False, dest='disable_keepalive')

arg_parser.add_argument('-d', '--disable-receive', metavar='<disable_receive>', type=bool,
help='Disable receive message module',
required=False, default=False, dest='disable_receive')

args = arg_parser.parse_args()

process_script_parameters(args)
Expand All @@ -333,10 +352,10 @@ def main():
# Waiting time to prevent CPU overload when registering many agents (registration + event generation).
sleep(args.waiting_connection_time)

injectors = create_injectors(agents, args.manager_address, args.agent_protocol)
injectors = create_injectors(agents, args.manager_address, args.agent_protocol, args.limit_msg)

run(injectors, args.simulation_time)
run(injectors, args.simulation_time, args.disable_keepalive)


if __name__ == "__main__":
main()
main()
4 changes: 4 additions & 0 deletions deps/wazuh_testing/wazuh_testing/tools/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@
else:
HOSTS_FILE_PATH = os.path.join('/', 'etc', 'hosts')
GLOBAL_DB_PATH = os.path.join(WAZUH_PATH, 'queue', 'db', 'global.db')
ANALYSISD_STATE = os.path.join(WAZUH_PATH, 'var', 'run', 'wazuh-analysisd.state')
SIMULATE_AGENT = os.path.join('deps','wazuh_testing','wazuh_testing','scripts','simulate_agents.py')
WAZUH_INTERNAL_OPTIONS = os.path.join(WAZUH_PATH, 'etc', 'internal_options.conf')

try:
import grp
Expand Down Expand Up @@ -126,6 +129,7 @@ def get_service():
CLIENT_CUSTOM_CERT_PATH = os.path.join(_data_path, 'sslmanager.cert')

WAZUH_LOGS_PATH = os.path.join(WAZUH_PATH, 'logs')
ALERT_PATH = os.path.join(WAZUH_LOGS_PATH, 'alerts')
ALERT_FILE_PATH = os.path.join(WAZUH_LOGS_PATH, 'alerts', 'alerts.json')
ALERT_LOGS_PATH = os.path.join(WAZUH_LOGS_PATH, 'alerts', 'alerts.log')
CLUSTER_LOGS_PATH = os.path.join(WAZUH_LOGS_PATH, 'cluster.log')
Expand Down
20 changes: 17 additions & 3 deletions deps/wazuh_testing/wazuh_testing/tools/agent_simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -1527,6 +1527,7 @@ class Injector:
thread_number (int): total number of threads created. This may change depending on the modules used in the
agent.
threads (list): list containing all the threads created.
limit_msg (int): Maximun amount of message to be sent.
Examples:
To create an Injector, you need to create an agent, a sender and then, create the injector using both of them.
Expand All @@ -1538,16 +1539,17 @@ class Injector:
>>> injector.run()
"""

def __init__(self, sender, agent):
def __init__(self, sender, agent, limit):
self.sender = sender
self.agent = agent
self.limit_msg = limit
self.thread_number = 0
self.threads = []
for module, config in self.agent.modules.items():
if config["status"] == "enabled":
self.threads.append(
InjectorThread(self.thread_number, f"Thread-{self.agent.id}{module}", self.sender,
self.agent, module))
self.agent, module, self.limit_msg))
self.thread_number += 1

def run(self):
Expand All @@ -1566,6 +1568,11 @@ def stop_receive(self):
self.sender.socket.close()


def wait(self):
for thread in range(self.thread_number):
self.threads[thread].join()


class InjectorThread(threading.Thread):
"""This class creates a thread who will create and send the events to the manager for each module.
Expand All @@ -1576,8 +1583,9 @@ class InjectorThread(threading.Thread):
agent (Agent): agent owner of the injector and the sender.
module (str): module used to send events (fim, syscollector, etc).
stop_thread (int): 0 if the thread is running, 1 if it is stopped.
limit_msg (int): Maximun amount of message to be sent.
"""
def __init__(self, thread_id, name, sender, agent, module):
def __init__(self, thread_id, name, sender, agent, module, limit_msg):
super(InjectorThread, self).__init__()
self.thread_id = thread_id
self.name = name
Expand All @@ -1586,6 +1594,7 @@ def __init__(self, thread_id, name, sender, agent, module):
self.totalMessages = 0
self.module = module
self.stop_thread = 0
self.limit_msg = limit_msg

def keep_alive(self):
"""Send a keep alive message from the agent to the manager."""
Expand Down Expand Up @@ -1668,6 +1677,11 @@ def run_module(self, module):
char_size = getsizeof(event_msg[0]) - getsizeof('')
event_msg += 'A' * (dummy_message_size//char_size)

# Add message limitiation
if self.totalMessages >= self.limit_msg:
self.stop_thread = 1
break

event = self.agent.create_event(event_msg)
self.sender.send_event(event)
self.totalMessages += 1
Expand Down
18 changes: 18 additions & 0 deletions deps/wazuh_testing/wazuh_testing/tools/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -742,3 +742,21 @@ def update_configuration_template(configurations, old_values, new_values):
configurations_to_update = configurations_to_update.replace(old_value, new_value)

return json.loads(configurations_to_update)


def get_simulate_agent_configuration(data_file_path):
"""Load simulate agent configuration file.
Args:
data_file_path (str): Test case template file path.
Returns:
dict: Configurations names.
"""
configuration_file = file.read_yaml(data_file_path)
configuration_parameters = {}

for test_case in configuration_file:
configuration_parameters.update(test_case['configuration_parameters'])

return configuration_parameters
Loading

0 comments on commit 0097ffb

Please sign in to comment.