diff --git a/deps/wazuh_testing/wazuh_testing/__init__.py b/deps/wazuh_testing/wazuh_testing/__init__.py index 60add07a56..416f382bb2 100644 --- a/deps/wazuh_testing/wazuh_testing/__init__.py +++ b/deps/wazuh_testing/wazuh_testing/__init__.py @@ -19,6 +19,12 @@ def is_tcp(protocol): return protocol.upper() == TCP +def is_tcp_udp(protocol): + _protocol = protocol.replace(' ','').upper().split(',') + _protocol.sort() + return ','.join(_protocol) == TCP_UDP + + class Parameters: """Class to allocate all global parameters for testing""" diff --git a/deps/wazuh_testing/wazuh_testing/remote.py b/deps/wazuh_testing/wazuh_testing/remote.py index 5489a15ed1..3e5f0d03b2 100644 --- a/deps/wazuh_testing/wazuh_testing/remote.py +++ b/deps/wazuh_testing/wazuh_testing/remote.py @@ -544,3 +544,22 @@ def intercept_socket_data(data): finally: mitm.shutdown() control_service('start', daemon='wazuh-analysisd') + + +def check_agent_received_message(message_queue, search_pattern, timeout=5, update_position=True): + """Allow to monitor the agent received messages to search a pattern regex. + + Args: + message_queue (monitoring.Queue): Queue containing the messages received in the agent. + search_pattern (str): Regex to search in agent received messages. + timeout (int): Maximum time in seconds to search the event. + update_position (boolean): True to search in the entire queue, False to search in the current position of the + queue. + + Raises: + TimeoutError: if the search pattern isn't found in the queue in the expected time. + """ + queue_monitor = monitoring.QueueMonitor(message_queue) + + queue_monitor.start(timeout=timeout, callback=monitoring.make_callback(search_pattern, '.*'), + update_position=update_position) diff --git a/deps/wazuh_testing/wazuh_testing/tools/agent_simulator.py b/deps/wazuh_testing/wazuh_testing/tools/agent_simulator.py index 8c684f77c2..608f6b2039 100644 --- a/deps/wazuh_testing/wazuh_testing/tools/agent_simulator.py +++ b/deps/wazuh_testing/wazuh_testing/tools/agent_simulator.py @@ -19,7 +19,7 @@ import ssl import threading import zlib -from collections import deque + from random import randint, sample, choice from stat import S_IFLNK, S_IFREG, S_IRWXU, S_IRWXG, S_IRWXO from string import ascii_letters, digits @@ -29,9 +29,10 @@ import wazuh_testing.wazuh_db as wdb from wazuh_testing import TCP from wazuh_testing import is_udp, is_tcp -from wazuh_testing.tools.monitoring import wazuh_unpack +from wazuh_testing.tools.monitoring import wazuh_unpack, Queue from wazuh_testing.tools.remoted_sim import Cipher + _data_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), '..', 'data') os_list = ["debian7", "debian8", "debian9", "debian10", "ubuntu12.04", @@ -92,7 +93,7 @@ class Agent: stop_receive (int): Flag to determine when to activate and deactivate the agent event listener. stage_disconnect (str): WPK process state variable. rcv_msg_limit (int): max elements for the received message queue. - rcv_msg_queue (deque): Doubly Ended Queue to store received messages in the agent. + rcv_msg_queue (monitoring.Queue): Queue to store received messages in the agent. disable_all_modules (boolean): Disable all simulated modules for this agent """ def __init__(self, manager_address, cypher="aes", os=None, inventory_sample=None, rootcheck_sample=None, @@ -138,7 +139,7 @@ def __init__(self, manager_address, cypher="aes", os=None, inventory_sample=None self.stop_receive = 0 self.stage_disconnect = None self.setup(disable_all_modules=disable_all_modules) - self.rcv_msg_queue = deque([], maxlen=rcv_msg_limit) + self.rcv_msg_queue = Queue(rcv_msg_limit) def setup(self, disable_all_modules): """Set up agent: os, registration, encryption key, start up msg and activate modules.""" @@ -403,7 +404,7 @@ def process_message(self, sender, message): message (str): Decoder message in ISO-8859-1 format. """ msg_decoded_list = message.split(' ') - self.rcv_msg_queue.append(message) + self.rcv_msg_queue.put(message) if '#!-req' in msg_decoded_list[0]: self.process_command(sender, msg_decoded_list) elif '#!-up' in msg_decoded_list[0]: diff --git a/deps/wazuh_testing/wazuh_testing/tools/monitoring.py b/deps/wazuh_testing/wazuh_testing/tools/monitoring.py index 809b5cf2b5..095d1bd993 100644 --- a/deps/wazuh_testing/wazuh_testing/tools/monitoring.py +++ b/deps/wazuh_testing/wazuh_testing/tools/monitoring.py @@ -474,7 +474,7 @@ class Queue(queue.Queue): def peek(self, *args, position=0, **kwargs): """Peek any given position without modifying the queue status. - The difference between `peek` and `get` is `peek` pops the item and `get` does not. + The difference between `peek` and `get` is `get` pops the item and `peek` does not. Args: position (int, optional) : Element of the queue to return. Default `0` diff --git a/docs/tests/integration/test_remoted/test_manager_messages/test_manager_ack.md b/docs/tests/integration/test_remoted/test_manager_messages/test_manager_ack.md new file mode 100644 index 0000000000..0de48e181b --- /dev/null +++ b/docs/tests/integration/test_remoted/test_manager_messages/test_manager_ack.md @@ -0,0 +1,63 @@ +# Test manager ACK + +## Overview + +These tests will check if the manager sends the ACK message after receiving the `start-up` message from agent. + +## Objective + +The objective is to check that the manager sends the ACK message using the different protocols. + +## General info + +|Tier | Number of tests | Time spent | +|:--:|:--:|:--:| +| 0 | 4 | 1m 15s | + +## Expected behavior + +Success if the agent receives the ACK message from the manager after sending the `start-up` message. Failure otherwise. + +## Testing + +The testing is based on configuring the manager to receive messages via `TCP`, `UDP`, `TCP-UDP` and `UDP-TCP`. + +First, the simulated agent will send the `start-up` message to the manager, and then, the agent will save all the +incoming messages from the agent in a buffer. + +The `start-up` message sent by the agent is as follows: + +``` +#!-agent startup +``` + +Next, the test will search the ACK message in the agent buffer (it contains the string `#!-agent ack`). + +An example of the `ACK` message is as follows: + +``` +4112dbb63510267c613d5b6da095b4ea274310000000010:2203:#!-agent ack +``` + +### Checks + +- Manager sends the ACK message using `TCP` protocol. +- Manager sends the ACK message using `UDP` protocol. +- Manager sends the ACK message using `TCP,UDP` configuration. +- Manager sends the ACK message using `UDP,TCP` configuration. + +## Comments + +An important aspect to take into account is the time needed by wazuh-remoted to reload the `client.keys`. +By default it is **10 seconds**, but this option is configurable in the `internal_options.conf`, using the +following directive: + +``` +remoted.keyupdate_interval=2 +``` + +The test itself waits until the info is loaded, so reducing this time will also reduce the test time. +It is recommended to set this time between 2 and 5 seconds. + +## Code documentation +::: tests.integration.test_remoted.test_manager_messages.test_manager_ack diff --git a/mkdocs.yml b/mkdocs.yml index 2b698d5a93..d2e36a47f3 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -404,6 +404,8 @@ nav: - Test configuration allowed and denied ips valids: tests/integration/test_remoted/test_configuration/test_basic_configuration_syslog_allowed_denied_ips_valid.md - Test configuration denied ips: tests/integration/test_remoted/test_configuration/test_basic_configuration_syslog_denied_ips.md - Test configuration syslog no allowed ips provided: tests/integration/test_remoted/test_configuration/test_basic_configuration_syslog_no_allowed_ips.md + - Test manager messages: + - tests/integration/test_remoted/test_manager_messages/test_manager_ack.md - Test communications through the sockets: - tests/integration/test_remoted/test_socket_communication/test_ping_pong_message.md - tests/integration/test_remoted/test_socket_communication/test_syslog_message.md diff --git a/tests/integration/test_remoted/test_manager_messages/data/wazuh_manager_ack.yaml b/tests/integration/test_remoted/test_manager_messages/data/wazuh_manager_ack.yaml new file mode 100644 index 0000000000..c528cc15ce --- /dev/null +++ b/tests/integration/test_remoted/test_manager_messages/data/wazuh_manager_ack.yaml @@ -0,0 +1,13 @@ +- tags: + - test_manager_ack + apply_to_modules: + - test_manager_ack + sections: + - section: remote + elements: + - connection: + value: secure + - port: + value: 1514 + - protocol: + value: PROTOCOL diff --git a/tests/integration/test_remoted/test_manager_messages/test_manager_ack.py b/tests/integration/test_remoted/test_manager_messages/test_manager_ack.py new file mode 100644 index 0000000000..e4e4b22309 --- /dev/null +++ b/tests/integration/test_remoted/test_manager_messages/test_manager_ack.py @@ -0,0 +1,103 @@ +import pytest +import os +import wazuh_testing.tools.agent_simulator as ag + +from time import sleep +from wazuh_testing import remote as rd +from wazuh_testing import is_tcp_udp +from wazuh_testing.tools import LOG_FILE_PATH +from wazuh_testing.tools.configuration import load_wazuh_configurations +from wazuh_testing.tools.monitoring import FileMonitor + + +# Marks +pytestmark = pytest.mark.tier(level=1) + +# Variables +current_test_path = os.path.dirname(os.path.realpath(__file__)) +test_data_path = os.path.join(current_test_path, 'data') +configurations_path = os.path.join(test_data_path, 'wazuh_manager_ack.yaml') + +wazuh_log_monitor = FileMonitor(LOG_FILE_PATH) + +# Set configuration +parameters = [ + {'PROTOCOL': 'tcp'}, + {'PROTOCOL': 'udp'}, + {'PROTOCOL': 'tcp,udp'}, + {'PROTOCOL': 'udp,tcp'}, +] + +metadata = [ + {'protocol': 'tcp'}, + {'protocol': 'udp'}, + {'protocol': 'tcp,udp'}, + {'protocol': 'udp,tcp'}, +] + +agent_info = { + 'manager_address': '127.0.0.1', + 'os': 'debian7', + 'version': '4.2.0', + 'disable_all_modules': True +} + +configuration_ids = [item['PROTOCOL'].upper() for item in parameters] + +# Configuration data +configurations = load_wazuh_configurations(configurations_path, __name__, params=parameters, metadata=metadata) + + +def check_manager_ack(protocol): + """Allow to check if the manager sends the ACK message after receiving the start-up message from agent. + + Args: + protocol (str): It can be UDP or TCP. + + Raises: + TimeoutError: If agent does not receive the manager ACK message in the expected time. + """ + + # Create agent and sender object with default parameters + agent = ag.Agent(**agent_info) + + # Sleep to avoid ConnectionRefusedError + sleep(1) + + sender = ag.Sender(agent_info['manager_address'], protocol=protocol) + + # Activate receives_messages modules in simulated agent. + agent.set_module_status('receive_messages', 'enabled') + + # Run injector with only receive messages module enabled + injector = ag.Injector(sender, agent) + try: + injector.run() + + # Wait until remoted has loaded the new agent key + rd.wait_to_remoted_key_update(wazuh_log_monitor) + + # Send the start-up message + sender.send_event(agent.startup_msg) + + # Check ACK manager message + rd.check_agent_received_message(agent.rcv_msg_queue, '#!-agent ack') + finally: + injector.stop_receive() + + +@pytest.fixture(scope='module', params=configurations, ids=configuration_ids) +def get_configuration(request): + """Get configurations from the module.""" + return request.param + + +def test_manager_ack(get_configuration, configure_environment, restart_remoted): + """Check if the manager sends the ACK message after receiving the start-up message from the agent.""" + protocol = get_configuration['metadata']['protocol'] + + if is_tcp_udp(protocol): + check_manager_ack(rd.TCP) + check_manager_ack(rd.UDP) + else: + check_manager_ack(protocol)