diff --git a/.gitignore b/.gitignore
index 9518d0ec7d..441a732a6f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,8 +3,9 @@ __pycache__
.pytest_cache
venv
-wazuh_testing.egg-info
+*.egg-info
dist
+build
# Python bytecode files
*.pyc
diff --git a/deployability/deps/requirements.txt b/deployability/deps/requirements.txt
index 5213aad50d..b71cb57648 100755
--- a/deployability/deps/requirements.txt
+++ b/deployability/deps/requirements.txt
@@ -12,4 +12,4 @@ pytest==7.4.4
paramiko==3.4.0
requests==2.31.0
chardet==5.2.0
-pywinrm==0.3.0
+pywinrm==0.4.0
diff --git a/deployability/modules/generic/ansible.py b/deployability/modules/generic/ansible.py
index 0829c84b3e..d13aacac7a 100755
--- a/deployability/modules/generic/ansible.py
+++ b/deployability/modules/generic/ansible.py
@@ -7,6 +7,7 @@
from pathlib import Path
from pydantic import BaseModel, IPvAnyAddress
+from typing import Optional
from modules.generic.utils import Utils
from modules.generic.logger import Logger
@@ -16,7 +17,8 @@ class Inventory(BaseModel):
ansible_host: str | IPvAnyAddress
ansible_user: str
ansible_port: int
- ansible_ssh_private_key_file: str
+ ansible_ssh_private_key_file: Optional[str] = None
+ ansible_password: Optional[str] = None
class Ansible:
@@ -118,7 +120,9 @@ def generate_inventory(self) -> dict:
self.ansible_data.ansible_host: {
'ansible_port': self.ansible_data.ansible_port,
'ansible_user': self.ansible_data.ansible_user,
- 'ansible_ssh_private_key_file': self.ansible_data.ansible_ssh_private_key_file
+ **({'ansible_ssh_private_key_file': self.ansible_data.ansible_ssh_private_key_file}
+ if hasattr(self.ansible_data, 'ansible_ssh_private_key_file')
+ else {'ansible_password': self.ansible_data.ansible_password})
}
}
}
diff --git a/deployability/modules/testing/tests/helpers/agent.py b/deployability/modules/testing/tests/helpers/agent.py
index 3815bbd4ea..0714d648a8 100644
--- a/deployability/modules/testing/tests/helpers/agent.py
+++ b/deployability/modules/testing/tests/helpers/agent.py
@@ -5,8 +5,8 @@
import yaml
from typing import List, Optional
-from .constants import WAZUH_CONF, WAZUH_ROOT
-from .executor import Executor, WazuhAPI
+from .constants import WAZUH_CONF, WAZUH_ROOT, WAZUH_WINDOWS_CONF
+from .executor import Executor, WazuhAPI, ConnectionManager
from .generic import HostInformation, CheckFiles
from modules.testing.utils import logger
@@ -15,32 +15,33 @@ class WazuhAgent:
@staticmethod
def install_agent(inventory_path, agent_name, wazuh_version, wazuh_revision, live) -> None:
- if live == True:
+ if live:
s3_url = 'packages'
- release = wazuh_version[0:3]
+ release = wazuh_version[:1] + ".x"
else:
s3_url = 'packages-dev'
release = 'pre-release'
os_type = HostInformation.get_os_type(inventory_path)
commands = []
+
if 'linux' in os_type:
distribution = HostInformation.get_linux_distribution(inventory_path)
architecture = HostInformation.get_architecture(inventory_path)
- if distribution == 'rpm' and 'x86_64' in architecture:
+ if distribution == 'rpm' and 'amd64' in architecture:
commands.extend([
f"curl -o wazuh-agent-{wazuh_version}-1.x86_64.rpm https://{s3_url}.wazuh.com/{release}/yum/wazuh-agent-{wazuh_version}-1.x86_64.rpm && sudo WAZUH_MANAGER='MANAGER_IP' WAZUH_AGENT_NAME='{agent_name}' rpm -ihv wazuh-agent-{wazuh_version}-1.x86_64.rpm"
])
- elif distribution == 'rpm' and 'aarch64' in architecture:
+ elif distribution == 'rpm' and 'arm64' in architecture:
commands.extend([
f"curl -o wazuh-agent-{wazuh_version}-1aarch64.rpm https://{s3_url}.wazuh.com/{release}/yum/wazuh-agent-{wazuh_version}-1.aarch64.rpm && sudo WAZUH_MANAGER='MANAGER_IP' WAZUH_AGENT_NAME='{agent_name}' rpm -ihv wazuh-agent-{wazuh_version}-1.aarch64.rpm"
])
- elif distribution == 'deb' and 'x86_64' in architecture:
+ elif distribution == 'deb' and 'amd64' in architecture:
commands.extend([
f"wget https://{s3_url}.wazuh.com/{release}/apt/pool/main/w/wazuh-agent/wazuh-agent_{wazuh_version}-1_amd64.deb && sudo WAZUH_MANAGER='MANAGER_IP' WAZUH_AGENT_NAME='{agent_name}' dpkg -i ./wazuh-agent_{wazuh_version}-1_amd64.deb"
])
- elif distribution == 'deb' and 'aarch64' in architecture:
+ elif distribution == 'deb' and 'arm64' in architecture:
commands.extend([
f"wget https://{s3_url}.wazuh.com/{release}/apt/pool/main/w/wazuh-agent/wazuh-agent_{wazuh_version}-1_arm64.deb && sudo WAZUH_MANAGER='MANAGER_IP' WAZUH_AGENT_NAME='{agent_name}' dpkg -i ./wazuh-agent_{wazuh_version}-1arm64.deb"
])
@@ -54,15 +55,16 @@ def install_agent(inventory_path, agent_name, wazuh_version, wazuh_revision, liv
commands.extend(system_commands)
elif 'windows' in os_type :
commands.extend([
- f"Invoke-WebRequest -Uri https://packages.wazuh.com/{release}/windows/wazuh-agent-{wazuh_version}-1.msi"
- "-OutFile ${env.tmp}\wazuh-agent;"
- "msiexec.exe /i ${env.tmp}\wazuh-agent /q"
- f"WAZUH_MANAGER='MANAGER_IP'"
- f"WAZUH_AGENT_NAME='{agent_name}'"
- f"WAZUH_REGISTRATION_SERVER='MANAGER_IP'",
- "NET START WazuhSvc",
- "NET STATUS WazuhSvc"
- ])
+ f"Invoke-WebRequest -Uri https://packages.wazuh.com/{release}/windows/wazuh-agent-{wazuh_version}-1.msi "
+ "-OutFile $env:TEMP\wazuh-agent.msi"
+ ])
+ commands.extend([
+ "msiexec.exe /i $env:TEMP\wazuh-agent.msi /q "
+ f"WAZUH_MANAGER='MANAGER_IP' "
+ f"WAZUH_AGENT_NAME='{agent_name}' "
+ f"WAZUH_REGISTRATION_SERVER='MANAGER_IP' "
+ ])
+ commands.extend(["NET START WazuhSvc"])
elif 'macos' in os_type:
if 'intel' in architecture:
commands.extend([
@@ -79,7 +81,7 @@ def install_agent(inventory_path, agent_name, wazuh_version, wazuh_revision, liv
commands.extend(system_commands)
logger.info(f'Installing Agent in {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}')
- Executor.execute_commands(inventory_path, commands)
+ ConnectionManager.execute_commands(inventory_path, commands)
@staticmethod
@@ -97,24 +99,53 @@ def register_agent(inventory_path, manager_path):
internal_ip = HostInformation.get_internal_ip_from_aws_dns(host) if 'amazonaws' in host else host
- commands = [
- f"sed -i 's/
MANAGER_IP<\/address>/{internal_ip}<\/address>/g' {WAZUH_CONF}",
- "systemctl restart wazuh-agent"
- ]
+ os_type = HostInformation.get_os_type(inventory_path)
+ logger.info(f'os_type {os_type}')
+
+ if 'linux' in os_type:
+ commands = [
+ f"sed -i 's/MANAGER_IP<\/address>/{internal_ip}<\/address>/g' {WAZUH_CONF}",
+ "systemctl restart wazuh-agent"
+ ]
+
+ ConnectionManager.execute_commands(inventory_path, commands)
+ result = ConnectionManager.execute_commands(inventory_path, f'cat {WAZUH_CONF}')
+ assert internal_ip in result.get('output'), logger.error(f"""Error configuring the Manager IP ({internal_ip})in: {HostInformation.get_os_name_and_version_from_inventory(inventory_path)} agent""")
+ elif 'windows' in os_type :
+ try:
+ commands = [
+ f'(Get-Content -Path "{WAZUH_WINDOWS_CONF}" -Raw) -replace "MANAGER_IP", "{internal_ip}" | Set-Content -Path "{WAZUH_WINDOWS_CONF}"',
+ "NET START WazuhSvc"
+ ]
- Executor.execute_commands(inventory_path, commands)
- assert internal_ip in Executor.execute_command(inventory_path, f'cat {WAZUH_CONF}'), logger.error(f'Error configuring the Manager IP ({internal_ip}) in: {HostInformation.get_os_name_and_version_from_inventory(inventory_path)} agent')
+ ConnectionManager.execute_commands(inventory_path, commands)
+ except Exception as e:
+ raise Exception(f'Error registering agent. Error executing: {commands} with error: {e}')
+ result = ConnectionManager.execute_commands(inventory_path, f'Get-Content "{WAZUH_WINDOWS_CONF}"')
+ assert internal_ip in result.get('output'), logger.error(f'Error configuring the Manager IP ({internal_ip})in: {HostInformation.get_os_name_and_version_from_inventory(inventory_path)} agent')
@staticmethod
def set_protocol_agent_connection(inventory_path, protocol):
- commands = [
- f"sed -i 's/[^<]*<\/protocol>/{protocol}<\/protocol>/g' {WAZUH_CONF}",
- "systemctl restart wazuh-agent"
- ]
+ os_type = HostInformation.get_os_type(inventory_path)
+
+ if 'linux' in os_type:
+ commands = [
+ f"sed -i 's/[^<]*<\/protocol>/{protocol}<\/protocol>/g' {WAZUH_CONF}",
+ "systemctl restart wazuh-agent"
+ ]
- Executor.execute_commands(inventory_path, commands)
- assert protocol in Executor.execute_command(inventory_path, f'cat {WAZUH_CONF}'), logger.error(f'Error configuring the protocol ({protocol}) in: {HostInformation.get_os_name_and_version_from_inventory(inventory_path)} agent')
+ ConnectionManager.execute_commands(inventory_path, commands)
+ result = ConnectionManager.execute_commands(inventory_path, f'cat {WAZUH_CONF}')
+ assert protocol in result.get('output'), logger.error(f'Error configuring the protocol ({protocol}) in: {HostInformation.get_os_name_and_version_from_inventory(inventory_path)} agent')
+ elif 'windows' in os_type :
+ commands = [
+ f"(Get-Content -Path '{WAZUH_WINDOWS_CONF}') -replace '[^<]*<\/protocol>', '{protocol}' | Set-Content -Path '{WAZUH_WINDOWS_CONF}'"
+ ]
+
+ ConnectionManager.execute_commands(inventory_path, commands)
+ result = ConnectionManager.execute_commands(inventory_path, f'Get-Content -Path "{WAZUH_WINDOWS_CONF}"')
+ assert protocol in result.get('output'), logger.error(f'Error configuring the protocol ({protocol}) in: {HostInformation.get_os_name_and_version_from_inventory(inventory_path)} agent')
@staticmethod
@@ -150,7 +181,7 @@ def uninstall_agent(inventory_path, wazuh_version=None, wazuh_revision=None) ->
commands.extend(system_commands)
elif 'windows' in os_type:
commands.extend([
- f"msiexec.exe /x wazuh-agent-{wazuh_version}-1.msi /qn"
+ f"msiexec.exe /x $env:TEMP\wazuh-agent.msi /qn"
])
elif 'macos' in os_type:
commands.extend([
@@ -165,7 +196,7 @@ def uninstall_agent(inventory_path, wazuh_version=None, wazuh_revision=None) ->
])
logger.info(f'Uninstalling Agent in {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}')
- Executor.execute_commands(inventory_path, commands)
+ ConnectionManager.execute_commands(inventory_path, commands)
@staticmethod
@@ -188,7 +219,7 @@ def _uninstall_agent_callback(wazuh_params, agent_params):
def perform_action_and_scan(agent_params, action_callback) -> dict:
"""
Takes scans using filters, the callback action and compares the result
-
+
Args:
agent_params (str): agent parameters
callbak (cb): callback (action)
@@ -250,7 +281,7 @@ def perform_action_and_scan(agent_params, action_callback) -> dict:
def perform_install_and_scan_for_agent(agent_params, agent_name, wazuh_params) -> None:
"""
Coordinates the action of install the agent and compares the checkfiles
-
+
Args:
agent_params (str): agent parameters
wazuh_params (str): wazuh parameters
@@ -259,14 +290,14 @@ def perform_install_and_scan_for_agent(agent_params, agent_name, wazuh_params) -
action_callback = lambda: WazuhAgent._install_agent_callback(wazuh_params, agent_name, agent_params)
result = WazuhAgent.perform_action_and_scan(agent_params, action_callback)
logger.info(f'Pre and post install checkfile comparison in {HostInformation.get_os_name_and_version_from_inventory(agent_params)}: {result}')
- WazuhAgent.assert_results(result)
+ WazuhAgent.assert_results(result, agent_params)
@staticmethod
def perform_uninstall_and_scan_for_agent(agent_params, wazuh_params) -> None:
"""
Coordinates the action of uninstall the agent and compares the checkfiles
-
+
Args:
agent_params (str): agent parameters
wazuh_params (str): wazuh parameters
@@ -275,23 +306,29 @@ def perform_uninstall_and_scan_for_agent(agent_params, wazuh_params) -> None:
action_callback = lambda: WazuhAgent._uninstall_agent_callback(wazuh_params, agent_params)
result = WazuhAgent.perform_action_and_scan(agent_params, action_callback)
logger.info(f'Pre and post uninstall checkfile comparison in {HostInformation.get_os_name_and_version_from_inventory(agent_params)}: {result}')
- WazuhAgent.assert_results(result)
+ WazuhAgent.assert_results(result, agent_params)
@staticmethod
- def assert_results(result) -> None:
+ def assert_results(result, params = None) -> None:
"""
Gets the status of an agent given its name.
-
+
Args:
result (dict): result of comparison between pre and post action scan
"""
- categories = ['/root', '/usr/bin', '/usr/sbin', '/boot']
+ os_type = HostInformation.get_os_type(params)
+
+ if os_type == 'linux':
+ categories = ['/root', '/usr/bin', '/usr/sbin', '/boot']
+ elif os_type == 'windows':
+ categories = ['C:\\Program Files', 'C:\\Program Files (x86)','C:\\Users\\vagrant']
actions = ['added', 'modified', 'removed']
# Testing the results
for category in categories:
for action in actions:
+
assert result[category][action] == [], logger.error(f'{result[category][action]} was found in: {category}{action}')
def areAgent_processes_active(agent_params):
@@ -304,7 +341,21 @@ def areAgent_processes_active(agent_params):
Returns:
str: Os name.
"""
- return bool([int(numero) for numero in Executor.execute_command(agent_params, 'pgrep wazuh').splitlines()])
+ os_type = HostInformation.get_os_type(agent_params)
+
+ if 'linux' in os_type:
+ result = ConnectionManager.execute_commands(agent_params, 'pgrep wazuh')
+ if result.get('success'):
+ return bool([int(numero) for numero in result.get('output').splitlines()])
+ else:
+ return False
+ elif 'windows' in os_type:
+ result = ConnectionManager.execute_commands(agent_params, 'Get-Process -Name "wazuh-agent" | Format-Table -HideTableHeaders ProcessName')
+ if result.get('success'):
+ return 'wazuh-agent' in result.get('output')
+ else:
+ return False
+
def isAgent_port_open(agent_params):
"""
@@ -316,7 +367,15 @@ def isAgent_port_open(agent_params):
Returns:
str: Os name.
"""
- return 'ESTAB' in Executor.execute_command(agent_params, 'ss -t -a -n | grep ":1514" | grep ESTAB')
+
+ os_type = HostInformation.get_os_type(agent_params)
+ if 'linux' in os_type:
+ result = ConnectionManager.execute_commands(agent_params, 'ss -t -a -n | grep ":1514" | grep ESTAB')
+ return result.get('success')
+ elif 'windows' in os_type :
+ result = ConnectionManager.execute_commands(agent_params, 'netstat -ano | Select-String -Pattern "TCP" | Select-String -Pattern "ESTABLISHED" | Select-String -Pattern ":1514"')
+ return 'ESTABLISHED' in result.get('output')
+
def get_agents_information(wazuh_api: WazuhAPI) -> list:
"""
@@ -337,17 +396,20 @@ def get_agents_information(wazuh_api: WazuhAPI) -> list:
def get_agent_status(wazuh_api: WazuhAPI, agent_name) -> str:
"""
Function to get the status of an agent given its name.
-
+
Args:
- agents_data (list): List of dictionaries containing agents' data.
- agent_name (str): Name of the agent whose status is to be obtained.
-
+
Returns:
- str: Status of the agent if found in the data, otherwise returns None.
"""
response = requests.get(f"{wazuh_api.api_url}/agents", headers=wazuh_api.headers, verify=False)
+
for agent in eval(response.text)['data']['affected_items']:
if agent.get('name') == agent_name:
+
+
return agent.get('status')
return None
diff --git a/deployability/modules/testing/tests/helpers/constants.py b/deployability/modules/testing/tests/helpers/constants.py
index ea0c6a8098..b7f6619d8c 100755
--- a/deployability/modules/testing/tests/helpers/constants.py
+++ b/deployability/modules/testing/tests/helpers/constants.py
@@ -10,6 +10,13 @@
CONFIGURATIONS_DIR = Path(WAZUH_ROOT, "etc")
WAZUH_CONF = Path(CONFIGURATIONS_DIR, "ossec.conf")
CLIENT_KEYS = Path(CONFIGURATIONS_DIR, "client.keys")
+
+WINDOWS_ROOT_DIR = Path("C:", "Program Files (x86)", "ossec-agent")
+WINDOWS_CONFIGURATIONS_DIR = Path(WINDOWS_ROOT_DIR, "etc")
+WAZUH_WINDOWS_CONF = Path(WINDOWS_ROOT_DIR, "ossec.conf")
+WINDOWS_CLIENT_KEYS = Path(WINDOWS_ROOT_DIR, "client.keys")
+WINDOWS_VERSION = Path(WINDOWS_ROOT_DIR, "VERSION")
+WINDOWS_REVISION = Path(WINDOWS_ROOT_DIR, "REVISION")
# Binaries paths
BINARIES_DIR = Path(WAZUH_ROOT, "bin")
WAZUH_CONTROL = Path(BINARIES_DIR, "wazuh-control")
diff --git a/deployability/modules/testing/tests/helpers/executor.py b/deployability/modules/testing/tests/helpers/executor.py
index 6d204d6ba8..25f9ba191b 100644
--- a/deployability/modules/testing/tests/helpers/executor.py
+++ b/deployability/modules/testing/tests/helpers/executor.py
@@ -7,46 +7,106 @@
import subprocess
import urllib3
import yaml
+import winrm
from base64 import b64encode
-
-class Executor:
+class ConectionInventory():
+ host: str
+ port: int
+ password: str | None = None
+ username: str
+ private_key_path: str | None = None
@staticmethod
- def execute_command(inventory_path, command) -> str:
-
+ def _get_inventory_data(inventory_path) -> dict:
with open(inventory_path, 'r') as yaml_file:
inventory_data = yaml.safe_load(yaml_file)
- host = inventory_data.get('ansible_host')
- port = inventory_data.get('ansible_port')
- private_key_path = inventory_data.get('ansible_ssh_private_key_file')
- username = inventory_data.get('ansible_user')
+ return {
+ 'host': inventory_data.get('ansible_host'),
+ 'port': inventory_data.get('ansible_port'),
+ 'password': inventory_data.get('ansible_password', None),
+ 'username': inventory_data.get('ansible_user'),
+ 'private_key_path': inventory_data.get('ansible_ssh_private_key_file', None)
+ }
+
+class ConnectionManager:
+ @staticmethod
+ def _get_executor(inventory_path) -> type:
+ from .generic import HostInformation
+
+ os_type = HostInformation.get_os_type(inventory_path)
+ if os_type == "windows":
+ return WindowsExecutor
+ else:
+ return UnixExecutor
+
+ @staticmethod
+ def execute_commands(inventory_path, commands) -> dict:
+ executor = ConnectionManager._get_executor(inventory_path)
+ if isinstance(commands, str):
+ try:
+ result = executor._execute_command(ConectionInventory._get_inventory_data(inventory_path), commands)
+ except Exception as e:
+ raise Exception(f'Error executing command: {commands} with error: {e}')
+ return result
+ else:
+ results = {}
+ for command in commands:
+ result = executor._execute_command(ConectionInventory._get_inventory_data(inventory_path), command)
+ results[command] = result
+ return results
+
+class WindowsExecutor():
+ @staticmethod
+ def _execute_command(data: ConectionInventory, command) -> dict:
+ if data.get('port') == 5986:
+ protocol = 'https'
+ else:
+ protocol = 'http'
+
+ endpoint_url = f"{protocol}://{data.get('host')}:{data.get('port')}"
+
+ try:
+ session = winrm.Session(endpoint_url, auth=(data.get('username'), data.get('password')),transport='ntlm', server_cert_validation='ignore')
+ ret = session.run_ps(command)
+
+ if ret.status_code == 0:
+ return {'success': True, 'output': ret.std_out.decode('utf-8').strip()}
+ else:
+ return {'success': False, 'output': ret.std_err.decode('utf-8').strip()}
+ except Exception as e:
+ raise Exception(f'Error executing command: {command} with error: {e}')
+
+class UnixExecutor():
+ @staticmethod
+ def _execute_command(data, command) -> dict:
ssh_command = [
"ssh",
- "-i", private_key_path,
+ "-i", data.get('private_key_path'),
"-o", "StrictHostKeyChecking=no",
"-o", "UserKnownHostsFile=/dev/null",
- "-p", str(port),
- f"{username}@{host}",
- "sudo",
+ "-p", str(data.get('port')),
+ f"{data.get('username')}@{data.get('host')}",
+ "sudo",
command
]
- result = subprocess.run(ssh_command, stdout=subprocess.PIPE, text=True)
-
- return result.stdout
+ try:
+ ret = subprocess.run(ssh_command, stdout=subprocess.PIPE, text=True)
+ if ret.stdout:
+ return {'success': True, 'output': ret.stdout.replace('\n', '')}
+ if ret.stderr:
+ return {'success': False, 'output': ret.stderr.replace('\n', '')}
+ return {'success': False, 'output': None}
- @staticmethod
- def execute_commands(inventory_path, commands=[]) -> dict:
-
- results = {}
- for command in commands:
- results[command] = Executor.execute_command(inventory_path, command)
+ except Exception as e:
+ #return {'success': False, 'output': ret.stderr}
+ raise Exception(f'Error executing command: {command} with error: {e}')
- return results
+# ------------------------------------------------------
class WazuhAPI:
@@ -62,14 +122,17 @@ def _authenticate(self):
inventory_data = yaml.safe_load(yaml_file)
user = 'wazuh'
-
+
#----Patch issue https://github.com/wazuh/wazuh-packages/issues/2883-------------
- file_path = Executor.execute_command(self.inventory_path, 'pwd').replace("\n","") + '/wazuh-install-files/wazuh-passwords.txt'
- if not 'true' in Executor.execute_command(self.inventory_path, f'test -f {file_path} && echo "true" || echo "false"'):
- Executor.execute_command(self.inventory_path, 'tar -xvf wazuh-install-files.tar')
- password = Executor.execute_command(self.inventory_path, "grep api_password wazuh-install-files/wazuh-passwords.txt | head -n 1 | awk '{print $NF}'").replace("'","").replace("\n","")
+ result = ConnectionManager.execute_commands(self.inventory_path, 'pwd')
+ file_path = result.get('output') + '/wazuh-install-files/wazuh-passwords.txt'
+ result = ConnectionManager.execute_commands(self.inventory_path, f'test -f {file_path} && echo "true" || echo "false"')
+ if not 'true' in result.get('output'):
+ ConnectionManager.execute_commands(self.inventory_path, 'tar -xvf wazuh-install-files.tar')
+ result = ConnectionManager.execute_commands(self.inventory_path, "grep api_password wazuh-install-files/wazuh-passwords.txt | head -n 1 | awk '{print $NF}'")
+ password = result.get('output')[1:-1]
#--------------------------------------------------------------------------------
-
+
login_endpoint = 'security/user/authenticate'
host = inventory_data.get('ansible_host')
port = '55000'
diff --git a/deployability/modules/testing/tests/helpers/generic.py b/deployability/modules/testing/tests/helpers/generic.py
index 7c989028bf..65c1a13beb 100644
--- a/deployability/modules/testing/tests/helpers/generic.py
+++ b/deployability/modules/testing/tests/helpers/generic.py
@@ -12,9 +12,8 @@
import yaml
from pathlib import Path
-from .constants import WAZUH_CONTROL, CLIENT_KEYS
-from .executor import Executor
-from .utils import Utils
+from .constants import WAZUH_CONTROL, CLIENT_KEYS, WINDOWS_CLIENT_KEYS, WINDOWS_VERSION, WINDOWS_REVISION
+from .executor import Executor, ConnectionManager
from modules.testing.utils import logger
@@ -32,7 +31,14 @@ def dir_exists(inventory_path, dir_path) -> str:
Returns:
bool: True or False
"""
- return 'true' in Executor.execute_command(inventory_path, f'test -d {dir_path} && echo "true" || echo "false"')
+ os_type = HostInformation.get_os_type(inventory_path)
+
+ if os_type == 'linux':
+ result = ConnectionManager.execute_commands(inventory_path, f'test -d {dir_path} && echo "True" || echo "False"')
+
+ return result.get('output')
+ elif os_type == 'windows':
+ return ConnectionManager.execute_commands(inventory_path, f'Test-Path -Path "{dir_path}"').get('success')
@staticmethod
@@ -47,7 +53,12 @@ def file_exists(inventory_path, file_path) -> bool:
Returns:
bool: True or False
"""
- return 'true' in Executor.execute_command(inventory_path, f'test -f {file_path} && echo "true" || echo "false"')
+ os_type = HostInformation.get_os_type(inventory_path)
+ if os_type == 'linux':
+ result = ConnectionManager.execute_commands(inventory_path, f'test -f {file_path} && echo "True" || echo "False"')
+ return result.get('output')
+ elif os_type == 'windows':
+ return ConnectionManager.execute_commands(inventory_path, f'Test-Path -Path "{file_path}"').get('output')
@staticmethod
@@ -61,8 +72,19 @@ def get_os_type(inventory_path) -> str:
Returns:
str: type of host (windows, linux, macos)
"""
- system = Executor.execute_command(inventory_path, 'uname')
- return system.lower()
+ try:
+ with open(inventory_path.replace('inventory', 'track'), 'r') as file:
+ data = yaml.safe_load(file)
+ if 'platform' in data:
+ return data['platform']
+ else:
+ raise KeyError("The 'platform' key was not found in the YAML file.")
+ except FileNotFoundError:
+ logger.error(f"The YAML file '{inventory_path}' was not found.")
+ except yaml.YAMLError as e:
+ logger.error(f"Error while loading the YAML file: {e}")
+ except Exception as e:
+ logger.error(f"An unexpected error occurred: {e}")
@staticmethod
@@ -74,9 +96,21 @@ def get_architecture(inventory_path) -> str:
inventory_path: host's inventory path
Returns:
- str: architecture (aarch64, x86_64, intel, apple)
+ str: architecture (amd64, arm64, intel, apple)
"""
- return Executor.execute_command(inventory_path, 'uname -m')
+ try:
+ with open(inventory_path.replace('inventory', 'track'), 'r') as file:
+ data = yaml.safe_load(file)
+ if 'platform' in data:
+ return data['arch']
+ else:
+ raise KeyError("The 'platform' key was not found in the YAML file.")
+ except FileNotFoundError:
+ logger.error(f"The YAML file '{inventory_path}' was not found.")
+ except yaml.YAMLError as e:
+ logger.error(f"Error while loading the YAML file: {e}")
+ except Exception as e:
+ logger.error(f"An unexpected error occurred: {e}")
@staticmethod
@@ -115,9 +149,9 @@ def get_os_name_from_inventory(inventory_path) -> str:
str: linux os name (debian, ubuntu, opensuse, amazon, centos, redhat)
"""
if 'manager' in inventory_path:
- os_name = re.search(r'/manager-linux-([^-]+)-', inventory_path).group(1)
+ os_name = re.search(r'/manager-[^-]+-([^-]+)-', inventory_path).group(1)
elif 'agent' in inventory_path:
- os_name = re.search(r'/agent-linux-([^-]+)-', inventory_path).group(1)
+ os_name = re.search(r'/agent-[^-]+-([^-]+)-', inventory_path).group(1)
return os_name
@@ -133,9 +167,9 @@ def get_os_name_and_version_from_inventory(inventory_path) -> tuple:
tuple: linux os name and version (e.g., ('ubuntu', '22.04'))
"""
if 'manager' in inventory_path:
- match = re.search(r'/manager-linux-([^-]+)-([^-]+)-', inventory_path)
+ match = re.search(r'/manager-[^-]+-([^-]+)-([^-]+)-', inventory_path)
elif 'agent' in inventory_path:
- match = re.search(r'/agent-linux-([^-]+)-([^-]+)-', inventory_path)
+ match = re.search(r'/agent-[^-]+-([^-]+)-([^-]+)-', inventory_path)
if match:
os_name = match.group(1)
version = match.group(2)
@@ -145,13 +179,22 @@ def get_os_name_and_version_from_inventory(inventory_path) -> tuple:
@staticmethod
def get_os_version_from_inventory(inventory_path) -> str:
- if 'manager' in inventory_path:
- os_version = re.search(r".*?/manager-linux-.*?-(.*?)-.*?/inventory.yaml", inventory_path).group(1)
- elif 'agent' in inventory_path:
- os_version = re.search(r".*?/agent-linux-.*?-(.*?)-.*?/inventory.yaml", inventory_path).group(1)
- return os_version
- else:
- return None
+ os_type = HostInformation.get_os_type(inventory_path)
+
+ if os_type == 'linux':
+ if 'manager' in inventory_path:
+ os_version = re.search(r".*?/manager-.*?-.*?-(.*?)-.*?/inventory.yaml", inventory_path).group(1)
+ elif 'agent' in inventory_path:
+ os_version = re.search(r".*?/agent-.*?-.*?-(.*?)-.*?/inventory.yaml", inventory_path).group(1)
+ return os_version
+ else:
+ return None
+ elif os_type == 'windows':
+ if 'agent' in inventory_path:
+ os_version = re.search(r".*?/agent-.*?-.*?-(.*?)-.*?/inventory.yaml", inventory_path)[1:3]
+ return os_version
+ else:
+ return None
@staticmethod
def get_current_dir(inventory_path) -> str:
@@ -164,8 +207,13 @@ def get_current_dir(inventory_path) -> str:
Returns:
str: current directory
"""
+ os_type = HostInformation.get_os_type(inventory_path)
- return Executor.execute_command(inventory_path, 'pwd').replace("\n","")
+ if os_type == 'linux':
+ result = ConnectionManager.execute_commands(inventory_path, 'pwd')
+ return result.get('output').replace("\n","")
+ elif os_type == 'windows':
+ return ConnectionManager.execute_commands(inventory_path, '(Get-Location).Path').get('output')
@staticmethod
def get_internal_ip_from_aws_dns(dns_name):
@@ -198,7 +246,14 @@ def get_client_keys(inventory_path) -> list[dict]:
list: List of dictionaries with the client keys.
"""
clients = []
- client_key = Executor.execute_command(inventory_path, f'cat {CLIENT_KEYS}')
+
+ os_type = HostInformation.get_os_type(inventory_path)
+
+ if os_type == 'linux':
+ client_key = ConnectionManager.execute_commands(inventory_path, f'cat {CLIENT_KEYS}').get('output')
+ elif os_type == 'windows':
+ client_key = ConnectionManager.execute_commands(inventory_path, f'Get-Content "{WINDOWS_CLIENT_KEYS}"').get('output')
+
lines = client_key.split('\n')[:-1]
for line in lines:
_id, name, address, password = line.strip().split()
@@ -225,7 +280,7 @@ def sshd_config(inventory_path) -> None:
"""
commands = ["sudo sed -i '/^PasswordAuthentication/s/^/#/' /etc/ssh/sshd_config", "sudo sed -i '/^PermitRootLogin no/s/^/#/' /etc/ssh/sshd_config", 'echo -e "PasswordAuthentication yes\nPermitRootLogin yes" | sudo tee -a /etc/ssh/sshd_config', 'sudo systemctl restart sshd', 'cat /etc/ssh/sshd_config']
- Executor.execute_commands(inventory_path, commands)
+ ConnectionManager.execute_commands(inventory_path, commands)
@staticmethod
@@ -237,17 +292,25 @@ def disable_firewall(inventory_path) -> None:
inventory_path: host's inventory path
"""
- commands = ["sudo systemctl stop firewalld", "sudo systemctl disable firewalld"]
- if GeneralComponentActions.isComponentActive(inventory_path, 'firewalld'):
- Executor.execute_commands(inventory_path, commands)
+ commands = []
- logger.info(f'Firewall disabled on {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}')
- else:
- logger.info(f'No Firewall to disable on {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}')
+ os_type = HostInformation.get_os_type(inventory_path)
+ if os_type == 'linux':
+ commands = ["sudo systemctl stop firewalld", "sudo systemctl disable firewalld"]
+ if GeneralComponentActions.isComponentActive(inventory_path, 'firewalld'):
+ ConnectionManager.execute_commands(inventory_path, commands)
+
+ logger.info(f'Firewall disabled on {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}')
+ else:
+ logger.info(f'No Firewall to disable on {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}')
+ elif os_type == 'windows':
+ commands = ["Set-NetFirewallProfile -Profile Domain,Public,Private -Enabled False"]
+ ConnectionManager.execute_commands(inventory_path, commands)
def _extract_hosts(paths, is_aws):
+ from .utils import Utils
if is_aws:
return [HostInformation.get_internal_ip_from_aws_dns(Utils.extract_ansible_host(path)) for path in paths]
else:
@@ -266,6 +329,8 @@ def certs_create(wazuh_version, master_path, dashboard_path, indexer_paths=[], w
workers_paths (list): wazuh worker paths list
"""
+ from .utils import Utils
+
current_directory = HostInformation.get_current_dir(master_path)
wazuh_version = '.'.join(wazuh_version.split('.')[:2])
@@ -326,7 +391,7 @@ def certs_create(wazuh_version, master_path, dashboard_path, indexer_paths=[], w
commands.extend(certs_creation)
- Executor.execute_commands(master_path, commands)
+ ConnectionManager.execute_commands(master_path, commands)
current_from_directory = HostInformation.get_current_dir(master_path)
@@ -364,7 +429,7 @@ def scp_to(from_inventory_path, to_inventory_path, file_name) -> None:
# Allowing handling permissions
if file_name == 'wazuh-install-files.tar':
- Executor.execute_command(from_inventory_path, f'chmod +rw {file_name}')
+ ConnectionManager.execute_commands(from_inventory_path, f'chmod +rw {file_name}')
logger.info('File permissions modified to be handled')
# SCP
@@ -381,8 +446,8 @@ def scp_to(from_inventory_path, to_inventory_path, file_name) -> None:
# Restoring permissions
if file_name == 'wazuh-install-files.tar':
- Executor.execute_command(from_inventory_path, f'chmod 600 {file_name}')
- Executor.execute_command(to_inventory_path, f'chmod 600 {file_name}')
+ ConnectionManager.execute_commands(from_inventory_path, f'chmod 600 {file_name}')
+ ConnectionManager.execute_commands(to_inventory_path, f'chmod 600 {file_name}')
logger.info('File permissions were restablished')
# Deleting file from localhost
@@ -461,18 +526,52 @@ def file_monitor(monitored_file: str, target_string: str, timeout: int = 30) ->
class CheckFiles:
@staticmethod
- def _checkfiles(inventory_path, os_type, directory, filter= None, hash_algorithm='sha256') -> dict:
+ def _checkfiles(inventory_path, os_type, directory, filters_keywords= None, hash_algorithm='sha256') -> dict:
"""
It captures a structure of a directory
Returns:
Dict: dict of directories:hash
"""
if 'linux' in os_type or 'macos' in os_type:
- command = f'sudo find {directory} -type f -exec sha256sum {{}} + {filter}'
- result = Executor.execute_command(inventory_path, command)
+ filters = f"| grep -v {filters_keywords[0]}"
+ for filter_ in filters_keywords[1:]:
+ filters += f" | grep -v {filter_}"
+ command = f'sudo find {directory} -type f -exec sha256sum {{}} + {filters}'
+ result = ConnectionManager.execute_commands(inventory_path, command).get('output')
elif 'windows' in os_type:
- command = 'dir /a-d /b /s | findstr /v /c:"\\.$" /c:"\\..$"| find /c ":"'
+
+ quoted_filters = ['"{}"'.format(keyword) for keyword in filters_keywords]
+ filter_files = ",".join(quoted_filters)
+ command = f"$includedDirectories = @('{directory}') "
+ command += f"\n$excludedPatterns = @({filter_files})"
+ command += """
+ try {
+ foreach ($dir in $includedDirectories) {
+ Get-ChildItem -Path "$dir" -Recurse -File -ErrorAction SilentlyContinue | ForEach-Object {
+ $fileName = $_.FullName
+ $hash = Get-FileHash -Path $fileName -Algorithm SHA256 -ErrorAction SilentlyContinue
+ if ($hash) {
+ $exclude = $false
+ foreach ($pattern in $excludedPatterns) {
+ if ($fileName -like "*$pattern*") {
+ $exclude = $true
+ break
+ }
+ }
+ if (-not $exclude) {
+ Write-Output "$($hash.Hash) $fileName"
+ }
+ }
+ }
+ }
+ } catch {
+ Write-Host "Error: $_"
+ }
+
+ """
+
+ result = ConnectionManager.execute_commands(inventory_path, command).get('output')
else:
logger.info(f'Unsupported operating system')
return None
@@ -485,9 +584,9 @@ def _checkfiles(inventory_path, os_type, directory, filter= None, hash_algorithm
@staticmethod
- def _perform_scan(inventory_path, os_type, directories, filters):
+ def _perform_scan(inventory_path, os_type, directories, filters_keywords):
logger.info(f'Generating Snapshot for Checkfile in {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}')
- return {directory: CheckFiles._checkfiles(inventory_path, os_type, directory, filters) for directory in directories}
+ return {directory: CheckFiles._checkfiles(inventory_path, os_type, directory, filters_keywords) for directory in directories}
@staticmethod
@@ -514,16 +613,16 @@ def perform_action_and_scan(inventory_path, callback) -> dict:
"""
os_type = HostInformation.get_os_type(inventory_path)
- directories = ['/boot', '/usr/bin', '/root', '/usr/sbin']
- filters_keywords = ['grep', 'tar', 'coreutils', 'sed', 'procps', 'gawk', 'lsof', 'curl', 'openssl', 'libcap', 'apt-transport-https', 'libcap2-bin', 'software-properties-common', 'gnupg', 'gpg']
- filters = f"| grep -v {filters_keywords[0]}"
+ if os_type == 'linux':
+ directories = ['/boot', '/usr/bin', '/root', '/usr/sbin']
+ filters_keywords = ['grep', 'tar', 'coreutils', 'sed', 'procps', 'gawk', 'lsof', 'curl', 'openssl', 'libcap', 'apt-transport-https', 'libcap2-bin', 'software-properties-common', 'gnupg', 'gpg']
+ elif os_type == 'windows':
+ directories = ['C:\\Program Files', 'C:\\Program Files (x86)','C:\\Users\\vagrant']
+ filters_keywords = ['log','tmp','ossec-agent', 'EdgeUpdate']
- for filter_ in filters_keywords[1:]:
- filters+= f" | grep -v {filter_}"
-
- initial_scans = CheckFiles._perform_scan(inventory_path, os_type, directories, filters)
+ initial_scans = CheckFiles._perform_scan(inventory_path, os_type, directories, filters_keywords)
callback()
- second_scans = CheckFiles._perform_scan(inventory_path, os_type, directories, filters)
+ second_scans = CheckFiles._perform_scan(inventory_path, os_type, directories, filters_keywords)
changes = {directory: CheckFiles._calculate_changes(initial_scans[directory], second_scans[directory]) for directory in directories}
return changes
@@ -543,8 +642,14 @@ def get_component_status(inventory_path, host_role) -> str:
str: Role status
"""
logger.info(f'Getting status of {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}')
+ os_type = HostInformation.get_os_type(inventory_path)
- return Executor.execute_command(inventory_path, f'systemctl status {host_role}')
+ if os_type == 'linux':
+ return ConnectionManager.execute_commands(inventory_path, f'systemctl status {host_role}').get('output')
+ elif os_type == 'windows':
+ result = ConnectionManager.execute_commands(inventory_path, "Get-Service -Name 'Wazuh' | Format-Table -HideTableHeaders Status")
+ if result.get('success'):
+ return result.get('output')
@staticmethod
@@ -558,7 +663,12 @@ def component_stop(inventory_path, host_role) -> None:
"""
logger.info(f'Stopping {host_role} in {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}')
- Executor.execute_command(inventory_path, f'systemctl stop {host_role}')
+ os_type = HostInformation.get_os_type(inventory_path)
+
+ if os_type == 'linux':
+ ConnectionManager.execute_commands(inventory_path, f'systemctl stop {host_role}')
+ elif os_type == 'windows':
+ ConnectionManager.execute_commands(inventory_path, f'NET STOP Wazuh')
@staticmethod
@@ -572,7 +682,13 @@ def component_restart(inventory_path, host_role) -> None:
"""
logger.info(f'Restarting {host_role} in {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}')
- Executor.execute_command(inventory_path, f'systemctl restart {host_role}')
+ os_type = HostInformation.get_os_type(inventory_path)
+
+ if os_type == 'linux':
+ ConnectionManager.execute_commands(inventory_path, f'systemctl restart {host_role}')
+ elif os_type == 'windows':
+ ConnectionManager.execute_commands(inventory_path, 'NET STOP Wazuh')
+ ConnectionManager.execute_commands(inventory_path, 'NET START Wazuh')
@staticmethod
@@ -586,7 +702,13 @@ def component_start(inventory_path, host_role) -> None:
"""
logger.info(f'Starting {host_role} in {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}')
- Executor.execute_command(inventory_path, f'systemctl restart {host_role}')
+
+ os_type = HostInformation.get_os_type(inventory_path)
+
+ if os_type == 'linux':
+ ConnectionManager.execute_commands(inventory_path, f'systemctl start {host_role}')
+ elif os_type == 'windows':
+ ConnectionManager.execute_commands(inventory_path, 'NET START Wazuh')
@staticmethod
@@ -600,7 +722,12 @@ def get_component_version(inventory_path) -> str:
Returns:
str: version
"""
- return Executor.execute_command(inventory_path, f'{WAZUH_CONTROL} info -v')
+ os_type = HostInformation.get_os_type(inventory_path)
+
+ if os_type == 'linux':
+ return ConnectionManager.execute_commands(inventory_path, f'{WAZUH_CONTROL} info -v').get('output')
+ elif os_type == 'windows':
+ return ConnectionManager.execute_commands(inventory_path, f'Get-Content "{WINDOWS_VERSION}"').get('output')#.replace("\n", ""))
@staticmethod
@@ -614,7 +741,13 @@ def get_component_revision(inventory_path) -> str:
Returns:
str: revision number
"""
- return Executor.execute_command(inventory_path, f'{WAZUH_CONTROL} info -r')
+
+ os_type = HostInformation.get_os_type(inventory_path)
+
+ if os_type == 'linux':
+ return ConnectionManager.execute_commands(inventory_path, f'{WAZUH_CONTROL} info -r').get('output')
+ elif os_type == 'windows':
+ return ConnectionManager.execute_commands(inventory_path, f'Get-Content "{WINDOWS_REVISION}"').get('output')
@staticmethod
@@ -628,8 +761,17 @@ def hasAgentClientKeys(inventory_path) -> bool:
Returns:
bool: True/False
"""
- return 'true' in Executor.execute_command(inventory_path, f'[ -f {CLIENT_KEYS} ] && echo true || echo false')
+ os_type = HostInformation.get_os_type(inventory_path)
+
+ if os_type == 'linux':
+ result = ConnectionManager.execute_commands(inventory_path, f'[ -f {CLIENT_KEYS} ] && echo true || echo false')
+ return 'true' in result.get('output')
+ elif os_type == 'windows':
+ result = ConnectionManager.execute_commands(inventory_path, f'Test-Path -Path "{WINDOWS_CLIENT_KEYS}"')
+ if result.get('success'):
+ return result.get('output', '')
+ return False
@staticmethod
def isComponentActive(inventory_path, host_role) -> bool:
@@ -643,7 +785,15 @@ def isComponentActive(inventory_path, host_role) -> bool:
Returns:
bool: True/False
"""
- return 'active' == Executor.execute_command(inventory_path, f'systemctl is-active {host_role}').replace("\n", "")
+
+ os_type = HostInformation.get_os_type(inventory_path)
+
+ if os_type == 'linux':
+
+ return 'active' == ConnectionManager.execute_commands(inventory_path, f'systemctl is-active {host_role}').get('output').replace("\n", "")
+ elif os_type == 'windows':
+ result = ConnectionManager.execute_commands(inventory_path, "Get-Service -Name 'Wazuh'")
+ return result.get('success')
class Waits:
diff --git a/deployability/modules/testing/tests/helpers/manager.py b/deployability/modules/testing/tests/helpers/manager.py
index 2ac8811ab7..b1c38ff251 100644
--- a/deployability/modules/testing/tests/helpers/manager.py
+++ b/deployability/modules/testing/tests/helpers/manager.py
@@ -6,7 +6,7 @@
import socket
from .constants import CLUSTER_CONTROL, AGENT_CONTROL, WAZUH_CONF, WAZUH_ROOT
-from .executor import Executor, WazuhAPI
+from .executor import Executor, WazuhAPI, ConnectionManager
from .generic import HostInformation, CheckFiles
from modules.testing.utils import logger
from .utils import Utils
@@ -39,7 +39,7 @@ def install_manager(inventory_path, node_name, wazuh_version) -> None:
f"bash wazuh-install.sh --wazuh-server {node_name} --ignore-check"
]
logger.info(f'Installing Manager in {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}')
- Executor.execute_commands(inventory_path, commands)
+ ConnectionManager.execute_commands(inventory_path, commands)
@staticmethod
@@ -88,7 +88,7 @@ def uninstall_manager(inventory_path) -> None:
commands.extend(system_commands)
logger.info(f'Uninstalling Manager in {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}')
- Executor.execute_commands(inventory_path, commands)
+ ConnectionManager.execute_commands(inventory_path, commands)
@staticmethod
@@ -188,7 +188,7 @@ def perform_install_and_scan_for_manager(manager_params, manager_name, wazuh_par
action_callback = lambda: WazuhManager._install_manager_callback(wazuh_params, manager_name, manager_params)
result = WazuhManager.perform_action_and_scan(manager_params, action_callback)
logger.info(f'Pre and post install checkfile comparison in {HostInformation.get_os_name_and_version_from_inventory(manager_params)}: {result}')
- WazuhManager.assert_results(result)
+ WazuhManager.assert_results(result, manager_params)
@staticmethod
@@ -204,7 +204,7 @@ def perform_uninstall_and_scan_for_manager(manager_params) -> None:
action_callback = lambda: WazuhManager._uninstall_manager_callback(manager_params)
result = WazuhManager.perform_action_and_scan(manager_params, action_callback)
logger.info(f'Pre and post uninstall checkfile comparison in {HostInformation.get_os_name_and_version_from_inventory(manager_params)}: {result}')
- WazuhManager.assert_results(result)
+ WazuhManager.assert_results(result, manager_params)
@staticmethod
@@ -236,7 +236,7 @@ def get_cluster_info(inventory_path) -> None:
str: Cluster status
"""
- return Executor.execute_command(inventory_path, f'{CLUSTER_CONTROL} -l')
+ return ConnectionManager.execute_commands(inventory_path, f'{CLUSTER_CONTROL} -l').get('output')
@staticmethod
@@ -250,8 +250,10 @@ def get_agent_control_info(inventory_path) -> None:
Returns:
str: Agents status
"""
+ result = ConnectionManager.execute_commands(inventory_path, f'{AGENT_CONTROL} -l')
- return Executor.execute_command(inventory_path, f'{AGENT_CONTROL} -l')
+
+ return result.get('output')
@staticmethod
@@ -278,8 +280,8 @@ def configuring_clusters(inventory_path, node_name, node_type, node_to_connect_i
"systemctl restart wazuh-manager"
]
- Executor.execute_commands(inventory_path, commands)
- if node_name in Executor.execute_command(inventory_path, f'cat {WAZUH_CONF}'):
+ ConnectionManager.execute_commands(inventory_path, commands)
+ if node_name in ConnectionManager.execute_commands(inventory_path, f'cat {WAZUH_CONF}').get('output'):
logger.info(f'Cluster configured in: {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}')
else:
logger.error(f'Error configuring cluster information in: {HostInformation.get_os_name_and_version_from_inventory(inventory_path)}')
diff --git a/deployability/modules/testing/tests/helpers/utils.py b/deployability/modules/testing/tests/helpers/utils.py
index aea456c151..f57ea7b77c 100644
--- a/deployability/modules/testing/tests/helpers/utils.py
+++ b/deployability/modules/testing/tests/helpers/utils.py
@@ -7,16 +7,17 @@
import yaml
import logging
import time
+import winrm
from modules.testing.utils import logger
-
+from .generic import HostInformation
paramiko_logger = logging.getLogger("paramiko")
paramiko_logger.setLevel(logging.CRITICAL)
class Utils:
-
+
@staticmethod
def extract_ansible_host(file_path) -> str:
with open(file_path, 'r') as yaml_file:
@@ -26,9 +27,9 @@ def extract_ansible_host(file_path) -> str:
@staticmethod
def check_inventory_connection(inventory_path, attempts=10, sleep=30) -> bool:
if 'manager' in inventory_path:
- match = re.search(r'/manager-linux-([^-]+)-([^-]+)-', inventory_path)
+ match = re.search(r'/manager-[^-]+-([^-]+)-([^-]+)-', inventory_path)
elif 'agent' in inventory_path:
- match = re.search(r'/agent-linux-([^-]+)-([^-]+)-', inventory_path)
+ match = re.search(r'/agent-[^-]+-([^-]+)-([^-]+)-', inventory_path)
if match:
os_name = match.group(1)+ '-' + match.group(2)
logger.info(f'Checking connection to {os_name}')
@@ -39,31 +40,56 @@ def check_inventory_connection(inventory_path, attempts=10, sleep=30) -> bool:
raise FileNotFoundError(logger.error(f'File not found in {os_name}'))
except yaml.YAMLError:
raise ValueError(logger.error(f'Invalid inventory information in {os_name}'))
-
+
host = inventory_data.get('ansible_host')
port = inventory_data.get('ansible_port')
- private_key_path = inventory_data.get('ansible_ssh_private_key_file')
+ private_key_path = inventory_data.get('ansible_ssh_private_key_file', None)
username = inventory_data.get('ansible_user')
+ password = inventory_data.get('ansible_password', None)
- ssh = paramiko.SSHClient()
- ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
- private_key = paramiko.RSAKey.from_private_key_file(private_key_path)
- for attempt in range(1, attempts + 1):
+ os_type = HostInformation.get_os_type(inventory_path)
+
+ if os_type == 'linux':
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
private_key = paramiko.RSAKey.from_private_key_file(private_key_path)
- try:
- ssh.connect(hostname=host, port=port, username=username, pkey=private_key)
- logger.info(f'Connection established successfully in {os_name}')
- ssh.close()
- return True
- except paramiko.AuthenticationException:
- logger.error(f'Authentication error. Check SSH credentials in {os_name}')
- return False
- except Exception as e:
- logger.warning(f'Error on attempt {attempt} of {attempts}: {e}')
- time.sleep(sleep)
+
+ for attempt in range(1, attempts + 1):
+ ssh = paramiko.SSHClient()
+ ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
+ private_key = paramiko.RSAKey.from_private_key_file(private_key_path)
+ try:
+ ssh.connect(hostname=host, port=port, username=username, pkey=private_key)
+ logger.info(f'Connection established successfully in {os_name}')
+ ssh.close()
+ return True
+ except paramiko.AuthenticationException:
+ logger.error(f'Authentication error. Check SSH credentials in {os_name}')
+ return False
+ except Exception as e:
+ logger.warning(f'Error on attempt {attempt} of {attempts}: {e}')
+ time.sleep(sleep)
+ elif os_type == 'windows':
+ if port == 5986:
+ protocol = 'https'
+ else:
+ protocol = 'http'
+ endpoint_url = f'{protocol}://{host}:{port}'
+
+ for attempt in range(1, attempts + 1):
+ try:
+ session = winrm.Session(endpoint_url, auth=(username, password),transport='ntlm', server_cert_validation='ignore')
+ cmd = session.run_cmd('ipconfig')
+ if cmd.status_code == 0:
+ logger.info("WinRM connection successful.")
+ return True
+ else:
+ logger.error(f'WinRM connection failed. Check the credentials in the inventory file.')
+ return False
+ except Exception as e:
+ logger.warning(f'Error on attempt {attempt} of {attempts}: {e}')
+ time.sleep(sleep)
logger.error(f'Connection attempts failed after {attempts} tries. Connection timeout in {os_name}')
return False
diff --git a/deployability/modules/testing/tests/test_agent/test_basic_info.py b/deployability/modules/testing/tests/test_agent/test_basic_info.py
index 3b81a0765f..5066ee6f4e 100644
--- a/deployability/modules/testing/tests/test_agent/test_basic_info.py
+++ b/deployability/modules/testing/tests/test_agent/test_basic_info.py
@@ -6,7 +6,7 @@
import re
from ..helpers.agent import WazuhAgent, WazuhAPI
-from ..helpers.constants import WAZUH_ROOT
+from ..helpers.constants import WAZUH_ROOT, WINDOWS_ROOT_DIR
from ..helpers.generic import HostConfiguration, HostInformation, GeneralComponentActions, Waits
from modules.testing.utils import logger
from ..helpers.manager import WazuhManager
@@ -67,10 +67,22 @@ def setup_test_environment(wazuh_params):
def test_wazuh_os_version(wazuh_params):
wazuh_api = WazuhAPI(wazuh_params['master'])
for agent_names, agent_params in wazuh_params['agents'].items():
- assert HostInformation.dir_exists(agent_params, WAZUH_ROOT), logger.error(f'The {WAZUH_ROOT} is not present in {HostInformation.get_os_name_and_version_from_inventory(agent_params)}')
+ path_to_check = ''
+ os_type = HostInformation.get_os_type(agent_params)
+
+ if os_type == 'linux':
+ path_to_check = WAZUH_ROOT
+ elif os_type == 'windows':
+ path_to_check = WINDOWS_ROOT_DIR
+
+ assert HostInformation.dir_exists(agent_params, path_to_check), logger.error(f'The {path_to_check} is not present in {HostInformation.get_os_name_and_version_from_inventory(agent_params)}')
+
expected_condition_func = lambda: 'active' == WazuhAgent.get_agent_status(wazuh_api, agent_names)
Waits.dynamic_wait(expected_condition_func, cycles=20, waiting_time=30)
- assert HostInformation.get_os_version_from_inventory(agent_params) in WazuhAgent.get_agent_os_version_by_name(wazuh_api, agent_names), logger.error('There is a mismatch between the OS version and the OS version of the installed agent')
+
+ if not os_type == 'windows':
+ assert HostInformation.get_os_version_from_inventory(agent_params) in WazuhAgent.get_agent_os_version_by_name(wazuh_api, agent_names), logger.error('There is a mismatch between the OS version and the OS version of the installed agent')
+
assert HostInformation.get_os_name_from_inventory(agent_params) in WazuhAgent.get_agent_os_name_by_name(wazuh_api, agent_names).replace(' ', ''), logger.error('There is a mismatch between the OS name and the OS name of the installed agent')
diff --git a/deployability/modules/testing/tests/test_agent/test_connection.py b/deployability/modules/testing/tests/test_agent/test_connection.py
index 4a7297596a..ec720925ce 100644
--- a/deployability/modules/testing/tests/test_agent/test_connection.py
+++ b/deployability/modules/testing/tests/test_agent/test_connection.py
@@ -73,7 +73,8 @@ def test_connection(wazuh_params):
def test_status(wazuh_params):
for agent in wazuh_params['agents'].values():
- assert 'active' in GeneralComponentActions.get_component_status(agent, 'wazuh-agent'), logger.error(f'The {HostInformation.get_os_name_and_version_from_inventory(agent)} is not active')
+ status = GeneralComponentActions.get_component_status(agent, 'wazuh-agent')
+ assert 'active' in status or 'connected' in status or "Running" in status, logger.error(f'The {HostInformation.get_os_name_and_version_from_inventory(agent)} is not active')
def test_service(wazuh_params):
diff --git a/deployability/modules/testing/tests/test_agent/test_install.py b/deployability/modules/testing/tests/test_agent/test_install.py
index 059d46273f..eea9bef899 100644
--- a/deployability/modules/testing/tests/test_agent/test_install.py
+++ b/deployability/modules/testing/tests/test_agent/test_install.py
@@ -6,7 +6,7 @@
import re
from ..helpers.agent import WazuhAgent
-from ..helpers.constants import WAZUH_ROOT
+from ..helpers.constants import WAZUH_ROOT, WINDOWS_ROOT_DIR
from ..helpers.generic import HostConfiguration, HostInformation, GeneralComponentActions
from modules.testing.utils import logger
from ..helpers.manager import WazuhManager
@@ -81,15 +81,22 @@ def test_installation(wazuh_params):
assert HostInformation.dir_exists(wazuh_params['master'], WAZUH_ROOT), logger.error(f'The {WAZUH_ROOT} is not present in {HostInformation.get_os_name_and_version_from_inventory(wazuh_params["master"])}')
# Agent installation
- for agent_names, agent_params in wazuh_params['agents'].items():
- WazuhAgent.perform_install_and_scan_for_agent(agent_params, agent_names, wazuh_params)
+ for agent_name, agent_params in wazuh_params['agents'].items():
+ WazuhAgent.perform_install_and_scan_for_agent(agent_params, agent_name, wazuh_params)
+ #WazuhAgent.install_agent(agent_params, agent_name, wazuh_params['wazuh_version'], wazuh_params['wazuh_revision'], wazuh_params['live'])
# Testing installation directory
for agent in wazuh_params['agents'].values():
- assert HostInformation.dir_exists(agent, WAZUH_ROOT), logger.error(f'The {WAZUH_ROOT} is not present in {HostInformation.get_os_name_and_version_from_inventory(agent)}')
+ os_type = HostInformation.get_os_type(agent)
+ if os_type == 'linux':
+ path_to_check = WAZUH_ROOT
+ elif os_type == 'windows':
+ path_to_check = WINDOWS_ROOT_DIR
+
+ assert HostInformation.dir_exists(agent, path_to_check), logger.error(f'The {path_to_check} is not present in {HostInformation.get_os_name_and_version_from_inventory(agent)}')
def test_status(wazuh_params):
for agent in wazuh_params['agents'].values():
agent_status = GeneralComponentActions.get_component_status(agent, 'wazuh-agent')
- assert 'loaded' in agent_status, logger.error(f'The {HostInformation.get_os_name_and_version_from_inventory(agent)} status is not loaded')
+ assert 'loaded' in agent_status or 'Stopped' in agent_status, logger.error(f'The {HostInformation.get_os_name_and_version_from_inventory(agent)} status is not loaded')
diff --git a/deployability/modules/testing/tests/test_agent/test_registration.py b/deployability/modules/testing/tests/test_agent/test_registration.py
index dc756410d5..7b1461f7f8 100644
--- a/deployability/modules/testing/tests/test_agent/test_registration.py
+++ b/deployability/modules/testing/tests/test_agent/test_registration.py
@@ -68,14 +68,8 @@ def test_status(wazuh_params):
for agent_names, agent_params in wazuh_params['agents'].items():
WazuhAgent.register_agent(agent_params, wazuh_params['master'])
for agent in wazuh_params['agents'].values():
- assert 'active' in GeneralComponentActions.get_component_status(agent, 'wazuh-agent'), logger.error(f'The {HostInformation.get_os_name_and_version_from_inventory(agent)} is not active')
-
-
-def test_connection(wazuh_params):
- for agent_names, agent_params in wazuh_params['agents'].items():
- assert agent_names in WazuhManager.get_agent_control_info(wazuh_params['master']), f'The {agent_names} is not present in the master by command'
- wazuh_api = WazuhAPI(wazuh_params['master'])
- assert any(d.get('name') == agent_names for d in WazuhAgent.get_agents_information(wazuh_api)), logger.error(f'The {agent_names} is not present in the master by API')
+ status = GeneralComponentActions.get_component_status(agent, 'wazuh-agent')
+ assert 'active' in status or 'Running' in status, logger.error(f'The {HostInformation.get_os_name_and_version_from_inventory(agent)} is not active')
def test_service(wazuh_params):
@@ -86,6 +80,12 @@ def test_service(wazuh_params):
expected_condition_func = lambda: 'active' == WazuhAgent.get_agent_status(wazuh_api, agent_names)
Waits.dynamic_wait(expected_condition_func, cycles=20, waiting_time=30)
+def test_connection(wazuh_params):
+ for agent_names, agent_params in wazuh_params['agents'].items():
+ wazuh_api = WazuhAPI(wazuh_params['master'])
+ assert agent_names in WazuhManager.get_agent_control_info(wazuh_params['master']), f'The {agent_names} is not present in the master by command'
+ assert any(d.get('name') == agent_names for d in WazuhAgent.get_agents_information(wazuh_api)), logger.error(f'The {agent_names} is not present in the master by API')
+
def test_clientKeys(wazuh_params):
for agent_names, agent_params in wazuh_params['agents'].items():
diff --git a/deployability/modules/testing/tests/test_agent/test_restart.py b/deployability/modules/testing/tests/test_agent/test_restart.py
index 734e4791e3..9a4abb9866 100644
--- a/deployability/modules/testing/tests/test_agent/test_restart.py
+++ b/deployability/modules/testing/tests/test_agent/test_restart.py
@@ -69,8 +69,8 @@ def test_restart(wazuh_params):
def test_status(wazuh_params):
for agent_names, agent_params in wazuh_params['agents'].items():
- assert 'active' in GeneralComponentActions.get_component_status(agent_params, 'wazuh-agent'), logger.error(f'{agent_names} is not active by command')
-
+ status = GeneralComponentActions.get_component_status(agent_params, 'wazuh-agent')
+ assert 'active' in status or 'Running' in status, logger.error(f'{agent_names} is not active by command')
def test_connection(wazuh_params):
for agent_names, agent_params in wazuh_params['agents'].items():
diff --git a/deployability/modules/testing/tests/test_agent/test_stop.py b/deployability/modules/testing/tests/test_agent/test_stop.py
index a347bce95f..b673726002 100644
--- a/deployability/modules/testing/tests/test_agent/test_stop.py
+++ b/deployability/modules/testing/tests/test_agent/test_stop.py
@@ -71,8 +71,8 @@ def test_service(wazuh_params):
GeneralComponentActions.component_stop(agent_params, 'wazuh-agent')
for agent_names, agent_params in wazuh_params['agents'].items():
- assert 'inactive' in GeneralComponentActions.get_component_status(agent_params, 'wazuh-agent'), logger.error(f'{agent_names} is still active by command')
- assert not GeneralComponentActions.isComponentActive(agent_params, 'wazuh-agent'), logger.error(f'{agent_names} is still active by command')
+ status = GeneralComponentActions.get_component_status(agent_params, 'wazuh-agent')
+ assert 'inactive' in status or 'Stopped' in status or 'StopPending' in status or 'not running' in status, logger.error(f'{agent_names} is still active by command')
expected_condition_func = lambda: 'disconnected' == WazuhAgent.get_agent_status(wazuh_api, agent_names)
Waits.dynamic_wait(expected_condition_func, cycles=20, waiting_time=30)
diff --git a/deployability/modules/testing/tests/test_agent/test_uninstall.py b/deployability/modules/testing/tests/test_agent/test_uninstall.py
index 953e73bc5e..c66e67cef1 100644
--- a/deployability/modules/testing/tests/test_agent/test_uninstall.py
+++ b/deployability/modules/testing/tests/test_agent/test_uninstall.py
@@ -6,7 +6,7 @@
import re
from ..helpers.agent import WazuhAgent
-from ..helpers.constants import WAZUH_ROOT
+from ..helpers.constants import WAZUH_ROOT, WINDOWS_CONFIGURATIONS_DIR, WINDOWS_ROOT_DIR
from ..helpers.generic import HostInformation, GeneralComponentActions, Waits
from ..helpers.manager import WazuhManager, WazuhAPI
from modules.testing.utils import logger
@@ -66,11 +66,19 @@ def setup_test_environment(wazuh_params):
def test_uninstall(wazuh_params):
for agent_names, agent_params in wazuh_params['agents'].items():
assert GeneralComponentActions.isComponentActive(agent_params, 'wazuh-agent'), logger.error(f'{agent_names} is not Active before the installation')
- assert HostInformation.dir_exists(agent_params, WAZUH_ROOT), logger.error(f'The {WAZUH_ROOT} is not present in the host {agent_names}')
+
+ os_type = HostInformation.get_os_type(agent_params)
+ if os_type == 'linux':
+ path_to_check = WAZUH_ROOT
+ elif os_type == 'windows':
+ path_to_check = WINDOWS_ROOT_DIR
+
+ assert HostInformation.dir_exists(agent_params, path_to_check), logger.error(f'The {path_to_check} is not present in the host {agent_names}')
# Agent installation
for agent_names, agent_params in wazuh_params['agents'].items():
WazuhAgent.perform_uninstall_and_scan_for_agent(agent_params,wazuh_params)
+ #WazuhAgent.uninstall_agent(agent_params, wazuh_params['wazuh_version'], wazuh_params['wazuh_revision'])
# Manager uninstallation status check
for agent_names, agent_params in wazuh_params['agents'].items():
@@ -79,7 +87,12 @@ def test_uninstall(wazuh_params):
def test_agent_uninstalled_directory(wazuh_params):
for agent_names, agent_params in wazuh_params['agents'].items():
- assert not HostInformation.dir_exists(agent_params, WAZUH_ROOT), logger.error(f'The {WAZUH_ROOT} is still present in the agent {agent_names}')
+ os_type = HostInformation.get_os_type(agent_params)
+ if os_type == 'linux':
+ path_to_check = WAZUH_ROOT
+ elif os_type == 'windows':
+ path_to_check = WINDOWS_CONFIGURATIONS_DIR
+ assert HostInformation.dir_exists(agent_params, path_to_check), logger.error(f'The {path_to_check} is still present in the agent {agent_names}')
def test_service(wazuh_params):
diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-windows-complete.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-windows-complete.yaml
new file mode 100755
index 0000000000..c65d56e65e
--- /dev/null
+++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-windows-complete.yaml
@@ -0,0 +1,119 @@
+version: 0.1
+description: This workflow is used to test agents' deployment for DDT1 PoC
+variables:
+ agent-os:
+ - linux-ubuntu-20.04-amd64
+ - linux-debian-12-amd64
+ - linux-oracle-9-amd64
+ - linux-centos-8-amd64
+ - linux-redhat-9-amd64
+ - windows-desktop-10-amd64
+ - windows-server-2012r2-amd64
+ - windows-server-2016-amd64
+ - windows-server-2019-amd64
+ - windows-server-2022-amd64
+ manager-os: linux-ubuntu-22.04-amd64
+ infra-provider: aws
+ working-dir: /tmp/dtt1-poc
+
+tasks:
+ # Unique manager allocate task
+ - task: "allocate-manager-{manager-os}"
+ description: "Allocate resources for the manager."
+ do:
+ this: process
+ with:
+ path: python3
+ args:
+ - modules/allocation/main.py
+ - action: create
+ - provider: "{infra-provider}"
+ - size: large
+ - composite-name: "{manager-os}"
+ - inventory-output: "{working-dir}/manager-{manager-os}/inventory.yaml"
+ - track-output: "{working-dir}/manager-{manager-os}/track.yaml"
+ - label-termination-date: "1d"
+ - label-team: "qa"
+ on-error: "abort-all"
+ cleanup:
+ this: process
+ with:
+ path: python3
+ args:
+ - modules/allocation/main.py
+ - action: delete
+ - track-output: "{working-dir}/manager-{manager-os}/track.yaml"
+
+ # Unique agent allocate task
+ - task: "allocate-agent-{agent}"
+ description: "Allocate resources for the agent."
+ do:
+ this: process
+ with:
+ path: python3
+ args:
+ - modules/allocation/main.py
+ - action: create
+ - provider: "{infra-provider}"
+ - size: medium
+ - composite-name: "{agent}"
+ - inventory-output: "{working-dir}/agent-{agent}/inventory.yaml"
+ - track-output: "{working-dir}/agent-{agent}/track.yaml"
+ - label-termination-date: "1d"
+ - label-team: "qa"
+ on-error: "abort-all"
+ foreach:
+ - variable: agent-os
+ as: agent
+ cleanup:
+ this: process
+ with:
+ path: python3
+ args:
+ - modules/allocation/main.py
+ - action: delete
+ - track-output: "{working-dir}/agent-{agent}/track.yaml"
+ depends-on:
+ - "provision-manager-{manager-os}"
+
+ # Unique manager provision task
+ - task: "provision-manager-{manager-os}"
+ description: "Provision the manager."
+ do:
+ this: process
+ with:
+ path: python3
+ args:
+ - modules/provision/main.py
+ - inventory: "{working-dir}/manager-{manager-os}/inventory.yaml"
+ - install:
+ - component: wazuh-manager
+ type: assistant
+ version: 4.7.3
+ live: True
+ depends-on:
+ - "allocate-manager-{manager-os}"
+ on-error: "abort-all"
+
+ # Generic agent test task
+ - task: "run-agent-{agent}-tests"
+ description: "Run tests install for the agent {agent}."
+ do:
+ this: process
+ with:
+ path: python3
+ args:
+ - modules/testing/main.py
+ - targets:
+ - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml"
+ - agent: "{working-dir}/agent-{agent}/inventory.yaml"
+ - tests: "install,registration,connection,basic_info,restart,stop,uninstall"
+ - component: "agent"
+ - wazuh-version: "4.7.3"
+ - wazuh-revision: "40714"
+ - live: "True"
+ foreach:
+ - variable: agent-os
+ as: agent
+ depends-on:
+ - "allocate-agent-{agent}"
diff --git a/deployability/modules/workflow_engine/examples/agent/aws/test-agent-windows-install.yaml b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-windows-install.yaml
new file mode 100755
index 0000000000..1c8a8f1faf
--- /dev/null
+++ b/deployability/modules/workflow_engine/examples/agent/aws/test-agent-windows-install.yaml
@@ -0,0 +1,28 @@
+version: 0.1
+description: This workflow is used to test agents' deployment for DDT1 PoC
+variables:
+ agent-os:
+ - windows-server-2016-amd64
+ manager-os: linux-ubuntu-22.04-amd64
+ infra-provider: vagrant
+ working-dir: /tmp/dtt1-poc
+
+tasks:
+ # Generic agent test task
+ - task: "run-agent-windows-server-2016-amd64-tests"
+ description: "Run tests install for the agent windows-server-2016-amd64."
+ do:
+ this: process
+ with:
+ path: python3
+ args:
+ - modules/testing/main.py
+ - targets:
+ - wazuh-1: "{working-dir}/manager-{manager-os}/inventory.yaml"
+ - agent: "{working-dir}/agent-windows-server-2016-amd64/inventory.yaml"
+ - tests: "install"
+ - component: "agent"
+ - wazuh-version: "4.7.3"
+ - wazuh-revision: "40714"
+ - live: "True"
+