add pasword tool to the ami customizer service

This commit is contained in:
Enrique Araque 2025-11-17 17:08:19 +01:00
parent 2221c47413
commit 8009f42548
No known key found for this signature in database
GPG Key ID: 29A3281A91360096
17 changed files with 267 additions and 80 deletions

View File

@ -7,7 +7,7 @@ import paramiko
from configurer.ami.ami_post_configurer.create_service_directory import create_directory_structure, generate_yaml from configurer.ami.ami_post_configurer.create_service_directory import create_directory_structure, generate_yaml
from generic import exec_command, modify_file, remote_connection from generic import exec_command, modify_file, remote_connection
from models import Inventory from models import Inventory
from utils import CertificatesComponent, Logger, RemoteDirectories from utils import CertificatesComponent, Logger, PasswordToolComponent, RemoteDirectories
logger = Logger("AmiPostConfigurer") logger = Logger("AmiPostConfigurer")
@ -15,21 +15,21 @@ logger = Logger("AmiPostConfigurer")
@dataclass @dataclass
class AmiPostConfigurer: class AmiPostConfigurer:
inventory: Inventory inventory: Inventory
environment_name: str = "certs-env" environment_name: str = "customizer-env"
enviroment_python_version: str = "3.11" enviroment_python_version: str = "3.11"
custom_env_dependencies: ClassVar[list[str]] = [ custom_env_dependencies: ClassVar[list[str]] = [
"pydantic", "pydantic",
"pyyaml", "pyyaml",
"paramiko", "paramiko",
] ]
custom_dir_name: str = "wazuh-ami-certs-customize" custom_dir_name: str = "wazuh-ami-customizer"
custom_dir_base_path: str = "/etc" custom_dir_base_path: str = "/etc"
cloud_instances_path: Path = Path("/var/lib/cloud/instances") cloud_instances_path: Path = Path("/var/lib/cloud/instances")
journal_logs_path: Path = Path("/var/log/journal") journal_logs_path: Path = Path("/var/log/journal")
journald__config_file_path: Path = Path("/etc/systemd/journald.conf") journald__config_file_path: Path = Path("/etc/systemd/journald.conf")
log_directory_path: Path = Path("/var/log") log_directory_path: Path = Path("/var/log")
wazuh_indexer_log_path: Path = Path("/var/log/wazuh-indexer") wazuh_indexer_log_path: Path = Path("/var/log/wazuh-indexer")
wazuh_server_log_path: Path = Path("/var/log/wazuh-server") wazuh_server_log_path: Path = Path("/var/ossec/logs")
wazuh_dashboard_log_path: Path = Path("/var/log/wazuh-dashboard") wazuh_dashboard_log_path: Path = Path("/var/log/wazuh-dashboard")
@remote_connection @remote_connection
@ -105,6 +105,8 @@ class AmiPostConfigurer:
"remote_certs_path": RemoteDirectories.CERTS, "remote_certs_path": RemoteDirectories.CERTS,
"certs_tool": CertificatesComponent.CERTS_TOOL, "certs_tool": CertificatesComponent.CERTS_TOOL,
"certs_config": CertificatesComponent.CONFIG, "certs_config": CertificatesComponent.CONFIG,
"password_tool_path": RemoteDirectories.PASSWORD_TOOL,
"password_tool": PasswordToolComponent.PASSWORD_TOOL,
} }
directory_template = generate_yaml( directory_template = generate_yaml(
context=context, context=context,
@ -181,7 +183,7 @@ class AmiPostConfigurer:
None None
""" """
self.stop_service("wazuh-server", client=client) self.stop_service("wazuh-manager", client=client)
def remove_wazuh_indexes(self, client: paramiko.SSHClient) -> None: def remove_wazuh_indexes(self, client: paramiko.SSHClient) -> None:
""" """

View File

@ -1,13 +1,22 @@
name: "wazuh-ami-certs-customize" # The structure contains name, files, and directories:
# - name: name of the directory to be created
# - files: list of files to be created.
# - path: path where the file to be copied is located. (not the destination path, but the source. The destination path is constructed automatically)
# - local: indicates whether the file is on the local machine (true) or on the remote instance (false).
# - directories: list of subdirectories to be created within the current directory. Each subdirectory can contain its own files and subdirectories.
name: "wazuh-ami-customizer"
files: files:
- path: "{{ remote_certs_path }}/{{ certs_tool }}" - path: "{{ remote_certs_path }}/{{ certs_tool }}"
local: false local: false
- path: "{{ remote_certs_path }}/{{ certs_config }}" - path: "{{ remote_certs_path }}/{{ certs_config }}"
local: false local: false
- path: "{{ password_tool_path }}/{{ password_tool }}"
local: false
directories: directories:
- name: "custom_certificates" - name: "src"
files: files:
- path: "configurer/ami/ami_post_configurer/custom_certificates.py" - path: "configurer/ami/ami_post_configurer/wazuh-ami-customizer.py"
local: true local: true
directories: directories:
- name: "configurer" - name: "configurer"

View File

@ -7,10 +7,12 @@ from configurer.core.utils import ComponentCertsDirectory
from generic import exec_command from generic import exec_command
from utils import Component, Logger from utils import Component, Logger
LOGFILE = Path("/var/log/wazuh-ami-custom-certificates.log") LOGFILE = Path("/var/log/wazuh-ami-customizer.log")
TEMP_DIR = Path("/etc/wazuh-ami-certs-customize") TEMP_DIR = Path("/etc/wazuh-ami-customizer")
CERTS_TOOL_PATH = Path(f"{TEMP_DIR}/certs-tool.sh") CERTS_TOOL_PATH = Path(f"{TEMP_DIR}/certs-tool.sh")
CERTS_TOOL_CONFIG_PATH = Path(f"{TEMP_DIR}/config.yml") CERTS_TOOL_CONFIG_PATH = Path(f"{TEMP_DIR}/config.yml")
PASSWORD_TOOL_PATH = Path(f"{TEMP_DIR}/password-tool.sh")
PASWORDS_FILE_NAME = Path("/etc/wazuh-ami-customizer/passwords.txt")
SERVICE_PATH = "/etc/systemd/system" SERVICE_PATH = "/etc/systemd/system"
SERVICE_NAME = f"{SERVICE_PATH}/wazuh-ami-customizer.service" SERVICE_NAME = f"{SERVICE_PATH}/wazuh-ami-customizer.service"
SERVICE_TIMER_NAME = f"{SERVICE_PATH}/wazuh-ami-customizer.timer" SERVICE_TIMER_NAME = f"{SERVICE_PATH}/wazuh-ami-customizer.timer"
@ -63,7 +65,7 @@ def stop_service(name: str) -> None:
logger.debug(f"{name} service stopped") logger.debug(f"{name} service stopped")
def verify_component_connection(component: Component, command: str, retries: int = 5, wait_time: int = 5) -> None: def verify_component_connection(component: Component, command: str, retries: int = 5, wait_time: int = 10) -> None:
""" """
Verifies the component connection by sending a request to the component's endpoint. Verifies the component connection by sending a request to the component's endpoint.
Args: Args:
@ -194,13 +196,13 @@ def stop_components_services() -> None:
logger.debug("Stopping Wazuh components services...") logger.debug("Stopping Wazuh components services...")
stop_service("wazuh-indexer") stop_service("wazuh-indexer")
stop_service("wazuh-server") stop_service("wazuh-manager")
stop_service("wazuh-dashboard") stop_service("wazuh-dashboard")
logger.debug("Wazuh components services stopped") logger.debug("Wazuh components services stopped")
def verify_indexer_connection() -> None: def verify_indexer_connection(password: str = "admin") -> None:
""" """
Verifies the connection to the Wazuh indexer. Verifies the connection to the Wazuh indexer.
This function sends a request to the Wazuh indexer endpoint and checks the response. This function sends a request to the Wazuh indexer endpoint and checks the response.
@ -210,7 +212,7 @@ def verify_indexer_connection() -> None:
None None
""" """
command = 'curl -XGET https://localhost:9200/ -uadmin:admin -k --max-time 120 --silent -w "%{http_code}" --output /dev/null' command = f"curl -XGET https://localhost:9200/ -uadmin:{password} -k --max-time 120 --silent -w \"%{{http_code}}\" --output /dev/null"
verify_component_connection(Component.WAZUH_INDEXER, command) verify_component_connection(Component.WAZUH_INDEXER, command)
@ -259,8 +261,8 @@ def start_components_services() -> None:
run_indexer_security_init() run_indexer_security_init()
verify_indexer_connection() verify_indexer_connection()
enable_service("wazuh-server") enable_service("wazuh-manager")
start_service("wazuh-server") start_service("wazuh-manager")
enable_service("wazuh-dashboard") enable_service("wazuh-dashboard")
start_service("wazuh-dashboard") start_service("wazuh-dashboard")
@ -269,6 +271,70 @@ def start_components_services() -> None:
logger.debug("Wazuh components services started") logger.debug("Wazuh components services started")
def get_instance_id() -> str:
"""
Retrieves the instance ID of the current machine capitalized.
Returns:
str: The instance ID of the current machine.
"""
logger.debug("Retrieving instance ID")
command = "ec2-metadata | grep 'instance-id' | cut -d':' -f2"
output, error_output = exec_command(command=command)
if error_output:
logger.error(f"Error retrieving instance ID: {error_output}")
raise RuntimeError("Error retrieving instance ID")
return output.strip().capitalize()
def generate_password_file() -> None:
"""
Generates a password file using the password tool.
Args:
path (Path): The path where the password file will be created.
Returns:
None
"""
logger.debug("Generating password file")
command = f"bash {PASSWORD_TOOL_PATH} -gf {PASWORDS_FILE_NAME}"
_, error_output = exec_command(command=command)
if error_output:
logger.error(f"Error generating password file: {error_output}")
raise RuntimeError("Error generating password file")
logger.debug("Password file generated")
def change_passwords() -> None:
logger.name = "CustomPasswords"
logger.debug("Changing passwords started")
logger.debug("Getting instance ID")
instance_id = get_instance_id()
generate_password_file()
logger.debug("Changing passwords to instance ID")
command = f"""
sudo sed -i 's/password:.*/password: {instance_id}/g' {PASWORDS_FILE_NAME}
bash {PASSWORD_TOOL_PATH} -a -A -au wazuh -ap wazuh -f {PASWORDS_FILE_NAME}
"""
_, error_output = exec_command(command=command)
if error_output:
logger.error(f"Error changing passwords: {error_output}")
raise RuntimeError("Error changing passwords")
logger.debug("Passwords changed. Verifying indexer connection with new password")
verify_indexer_connection(password=instance_id)
logger.debug("Changing passwords finished successfully")
def clean_up() -> None: def clean_up() -> None:
""" """
Cleans up temporary files and directories created during the process. Cleans up temporary files and directories created during the process.
@ -301,11 +367,14 @@ if __name__ == "__main__":
remove_certificates() remove_certificates()
create_certificates() create_certificates()
start_components_services() start_components_services()
stop_service("wazuh-dashboard")
change_passwords()
start_service("wazuh-dashboard")
start_ssh_service() start_ssh_service()
clean_up() clean_up()
except Exception as e: except Exception as e:
logger.error(f"An error occurred during the custom certificates configuration process: {e}") logger.error(f"An error occurred during the customization process: {e}")
start_ssh_service() start_ssh_service()
raise RuntimeError("An error occurred during the custom certificates configuration process") from e raise RuntimeError("An error occurred during the customization process") from e
logger.info("Custom certificates configuration process finished") logger.info("Customization process finished")

View File

@ -13,8 +13,8 @@ Wants=wazuh-ami-customizer.timer
[Service] [Service]
Type=oneshot Type=oneshot
WorkingDirectory=/etc/wazuh-ami-certs-customize/custom_certificates WorkingDirectory=/etc/wazuh-ami-customizer/src
ExecStart=/etc/wazuh-ami-certs-customize/certs-env/bin/python -m custom_certificates ExecStart=/etc/wazuh-ami-customizer/customizer-env/bin/python -m wazuh-ami-customizer
[Install] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target

View File

@ -95,6 +95,7 @@ def main(
Provisioner( Provisioner(
inventory=input.inventory_content, inventory=input.inventory_content,
certs=input.certificates_content, certs=input.certificates_content,
password_tool=input.password_tool_url,
components=components, components=components,
arch=input.arch, arch=input.arch,
package_type=input.package_type, package_type=input.package_type,

View File

@ -3,3 +3,4 @@ from .component_info import ComponentInfo
from .components_dependencies import ComponentsDependencies from .components_dependencies import ComponentsDependencies
from .input import Input from .input import Input
from .package_info import PackageInfo from .package_info import PackageInfo
from .password_tool_info import PasswordToolInfo

View File

@ -40,7 +40,7 @@ class CertsInfo(BaseModel):
""" """
logger.debug(f"Getting URL for {name}...") logger.debug(f"Getting URL for {name}...")
try: try:
url = AnyUrl(self.certs_url_content.get(name, None)) url = AnyUrl(self.certs_url_content.get(name, None)) # type: ignore
except pydantic_core._pydantic_core.ValidationError as err: except pydantic_core._pydantic_core.ValidationError as err:
raise ValueError(f"URL for {name} has an invalid format: {err}") from err raise ValueError(f"URL for {name} has an invalid format: {err}") from err

View File

@ -10,7 +10,8 @@ from utils import Component
from .certs_info import CertsInfo from .certs_info import CertsInfo
from .components_dependencies import ComponentsDependencies from .components_dependencies import ComponentsDependencies
from .package_info import PackageInfo from .package_info import PackageInfo
from .utils import format_certificates_urls_file, format_component_urls_file from .password_tool_info import PasswordToolInfo
from .utils import format_certificates_urls_file, format_component_urls_file, format_password_tool_urls_file
class Input(BaseModel): class Input(BaseModel):
@ -67,6 +68,16 @@ class Input(BaseModel):
except FileNotFoundError as err: except FileNotFoundError as err:
raise FileNotFoundError(f"Certificates file not found at {self.packages_url_path}") from err raise FileNotFoundError(f"Certificates file not found at {self.packages_url_path}") from err
@property
def password_tool_url(self) -> PasswordToolInfo:
try:
password_tool_data = format_password_tool_urls_file(self.packages_url_path)
if password_tool_data is None:
raise ValueError("Password tool URL not found in the packages URL file.")
return PasswordToolInfo(password_tool_url=password_tool_data)
except FileNotFoundError as err:
raise FileNotFoundError(f"Password tool file not found at {self.packages_url_path}") from err
@property @property
def inventory_content(self, host_name: str | None = None) -> Inventory | None: def inventory_content(self, host_name: str | None = None) -> Inventory | None:
return Inventory(self.inventory_path, host_name) if self.inventory_path else None return Inventory(self.inventory_path, host_name) if self.inventory_path else None

View File

@ -0,0 +1,30 @@
from pydantic import AnyUrl, BaseModel, field_validator
from provisioner.utils import AllowedUrlHost
from utils import Logger
from .utils import check_correct_url
logger = Logger("Password-tool provision")
class PasswordToolInfo(BaseModel):
"""
PasswordToolInfo is a model that holds information about password tool URL.
Attributes:
password_tool_url (str): A string containing the URL for the password-tool.
"""
password_tool_url: AnyUrl
@field_validator("password_tool_url")
def validate_password_tool_url(cls, url: AnyUrl) -> AnyUrl:
logger.debug("Validating password tool URL...")
if not check_correct_url(
url,
[AllowedUrlHost.RELEASE, AllowedUrlHost.PRE_RELEASE, AllowedUrlHost.INTERNAL],
):
raise ValueError("URL for password-tool is not for Wazuh packages.")
return url

View File

@ -1,2 +1,2 @@
from .file_formatter import format_certificates_urls_file, format_component_urls_file from .file_formatter import format_certificates_urls_file, format_component_urls_file, format_password_tool_urls_file
from .helpers import check_correct_url from .helpers import check_correct_url

View File

@ -1,12 +1,13 @@
from pathlib import Path from pathlib import Path
import yaml import yaml
from pydantic import AnyUrl
from provisioner.utils import ( from provisioner.utils import (
Component_arch, Component_arch,
Package_type, Package_type,
) )
from utils import CertificatesComponent, Component from utils import CertificatesComponent, Component, PasswordToolComponent
def file_to_dict(raw_urls_path: Path) -> dict: def file_to_dict(raw_urls_path: Path) -> dict:
@ -157,6 +158,31 @@ def format_certificates_urls_file(raw_urls_path: Path) -> dict:
return certificates_urls return certificates_urls
def format_password_tool_urls_file(raw_urls_path: Path) -> AnyUrl | None:
"""
Formats a file containing raw URLs into a string of password tool URL.
This function reads a file containing raw URLs and retrieves the URL
for the password tool.
>>> raw_urls_path = Path("password_tool_urls.yaml")
>>> format_password_tool_urls_file(raw_urls_path)
'https://packages.wazuh.com/password-tool-example/password_tool'
Args:
raw_urls_path (Path): The path to the file containing the raw URLs.
Returns:
str: The URL for the password tool.
"""
raw_urls_content = file_to_dict(raw_urls_path)
for component_name, url in raw_urls_content.items():
if PasswordToolComponent.PASSWORD_TOOL.name.lower() in component_name.lower():
return AnyUrl(url)
return None
def format_component_urls_file(raw_urls_path: Path) -> dict: def format_component_urls_file(raw_urls_path: Path) -> dict:
""" """
Formats the component URLs file by processing raw URLs and organizing them by component, architecture, and type. Formats the component URLs file by processing raw URLs and organizing them by component, architecture, and type.

View File

@ -5,9 +5,9 @@ from pydantic import AnyUrl
from generic import exec_command, remote_connection from generic import exec_command, remote_connection
from models import Inventory from models import Inventory
from provisioner.models import CertsInfo, ComponentInfo from provisioner.models import CertsInfo, ComponentInfo, PasswordToolInfo
from provisioner.utils import Component_arch, Package_manager, Package_type from provisioner.utils import Component_arch, Package_manager, Package_type
from utils import CertificatesComponent, Component, Logger, RemoteDirectories from utils import CertificatesComponent, Component, Logger, PasswordToolComponent, RemoteDirectories
logger = Logger("Provisioner") logger = Logger("Provisioner")
@ -30,6 +30,7 @@ class Provisioner:
inventory: Inventory | None inventory: Inventory | None
certs: CertsInfo certs: CertsInfo
password_tool: PasswordToolInfo
components: list[ComponentInfo] components: list[ComponentInfo]
arch: Component_arch = Component_arch.X86_64 arch: Component_arch = Component_arch.X86_64
package_type: Package_type = Package_type.RPM package_type: Package_type = Package_type.RPM
@ -49,7 +50,8 @@ class Provisioner:
1. Logs the start of the provisioning process. 1. Logs the start of the provisioning process.
2. Provisions the certs_tool using `certs_tool_provision`. 2. Provisions the certs_tool using `certs_tool_provision`.
3. Provision the config file using `certs_config_provision`. 3. Provision the config file using `certs_config_provision`.
4. Iterates over each component and performs the following: 4. Provisions the password tool using `password_tool_provision`.
5. Iterates over each component and performs the following:
a. Logs the start of provisioning for the component. a. Logs the start of provisioning for the component.
b. Provisions dependencies for the component using `dependencies_provision`. b. Provisions dependencies for the component using `dependencies_provision`.
c. Provisions packages for the component using `packages_provision`. c. Provisions packages for the component using `packages_provision`.
@ -63,6 +65,10 @@ class Provisioner:
self.certs_tool_provision(client) self.certs_tool_provision(client)
self.certs_config_provision(client) self.certs_config_provision(client)
logger.debug_title("Provisioning password tool")
self.password_tool_provision(client)
logger.debug_title("Provisioning special dependencies") logger.debug_title("Provisioning special dependencies")
self.special_dependencies_provision(client) self.special_dependencies_provision(client)
@ -77,20 +83,22 @@ class Provisioner:
Provisions the certs_tool on the specified client. Provisions the certs_tool on the specified client.
This method uses the provided SSH client to connect to a remote machine This method uses the provided SSH client to connect to a remote machine
and provision the certs_tool by calling the `certificates_provision` and provision the certs_tool by calling the `tool_provision`
method with the appropriate URL. method with the appropriate URL.
Args: Args:
client (paramiko.SSHClient): The SSH client used to connect to the remote machine. client (paramiko.SSHClient): The SSH client used to connect to the remote machine.
""" """
self.certificates_provision(self.certs.certs_tool_url, CertificatesComponent.CERTS_TOOL, client) self.tool_provision(
self.certs.certs_tool_url, f"{RemoteDirectories.CERTS}", CertificatesComponent.CERTS_TOOL, client
)
def certs_config_provision(self, client: paramiko.SSHClient | None = None) -> None: def certs_config_provision(self, client: paramiko.SSHClient | None = None) -> None:
""" """
Provisions the certs config file on the remote client. Provisions the certs config file on the remote client.
This method uses the provided SSH client to connect to a remote machine This method uses the provided SSH client to connect to a remote machine
and provision the certs config file by calling the `certificates_provision` and provision the certs config file by calling the `tool_provision`
method with the appropriate URL. method with the appropriate URL.
Args: Args:
@ -99,7 +107,25 @@ class Provisioner:
Returns: Returns:
None None
""" """
self.certificates_provision(self.certs.config_url, CertificatesComponent.CONFIG, client) self.tool_provision(self.certs.config_url, f"{RemoteDirectories.CERTS}", CertificatesComponent.CONFIG, client)
def password_tool_provision(self, client: paramiko.SSHClient | None = None) -> None:
"""
Provisions the password tool on the specified client.
This method uses the provided SSH client to connect to a remote machine
and provision the password tool by calling the `tool_provision`
method with the appropriate URL.
Args:
client (paramiko.SSHClient): The SSH client used to connect to the remote machine.
"""
self.tool_provision(
self.password_tool.password_tool_url,
f"{RemoteDirectories.PASSWORD_TOOL}",
PasswordToolComponent.PASSWORD_TOOL,
client,
)
def special_dependencies_provision(self, client: paramiko.SSHClient | None = None) -> None: def special_dependencies_provision(self, client: paramiko.SSHClient | None = None) -> None:
""" """
@ -177,39 +203,33 @@ class Provisioner:
component.name.replace("_", " ").capitalize(), component.name.replace("_", " ").capitalize(),
) )
def certificates_provision( def tool_provision(
self, certs_file_url: AnyUrl, filename: str, client: paramiko.SSHClient | None = None self, tool_url: AnyUrl, tool_dir: str, tool_filename: str, client: paramiko.SSHClient | None = None
) -> None: ) -> None:
""" """
Downloads a certificate file (certs_tool or config) from a given URL and saves it to a remote directory on a server. Provisions a tool (certs_tool, config file, or password tool) on the remote client.
Args: Args:
certs_file_url (AnyUrl): The URL of the certificate file to be downloaded. tool_url (AnyUrl): The URL of the tool to be provisioned.
client (paramiko.SSHClient): An active SSH client connected to the remote server. tool_dir (str): The directory on the remote client where the tool will be stored.
tool_filename (str): The filename of the tool to be provisioned.
Raises: client (paramiko.SSHClient): The SSH client used to connect to the remote machine.
Exception: If there is an error during the download process.
Logs:
Error message if the download fails.
Success message if the download is successful.
""" """
logger.debug(f"Provisioning {filename}") logger.debug(f"Provisioning {tool_filename}")
command_template = "mkdir -p {dir} && curl -s -o {path} '{certs_file_url}'" command_template = "mkdir -p {directory} && curl -s -o {path} '{tool_file_url}'"
command = command_template.format( command = command_template.format(
dir=f"{RemoteDirectories.CERTS}", directory=tool_dir,
path=f"{RemoteDirectories.CERTS}/{filename}", path=f"{tool_dir}/{tool_filename}",
certs_file_url=certs_file_url, tool_file_url=tool_url,
) )
output, error_output = exec_command(command=command, client=client) _, error_output = exec_command(command=command, client=client)
if error_output: if error_output:
logger.error(f"Error downloading {filename}: {error_output}") logger.error(f"Error downloading {tool_filename}: {error_output}")
raise RuntimeError(f"Error downloading {filename}") raise RuntimeError(f"Error downloading {tool_filename}")
logger.info_success(f"{filename} downloaded successfully") logger.info_success(f"{tool_filename} downloaded successfully")
def list_dependencies(self, elements: list[str], component_name: str) -> None: def list_dependencies(self, elements: list[str], component_name: str) -> None:
""" """

View File

@ -41,6 +41,7 @@ def mock_logger(autouse=True):
logger_paths = [ logger_paths = [
"provisioner.provisioner.logger", "provisioner.provisioner.logger",
"provisioner.models.certs_info.logger", "provisioner.models.certs_info.logger",
"provisioner.models.password_tool_info.logger",
"generic.remote_connection.logger", "generic.remote_connection.logger",
"configurer.core.models.wazuh_components_config_manager.logger", "configurer.core.models.wazuh_components_config_manager.logger",
"configurer.core.models.certificates_manager.logger", "configurer.core.models.certificates_manager.logger",

View File

@ -4,7 +4,7 @@ from unittest.mock import MagicMock, patch
import pytest import pytest
from configurer.ami.ami_post_configurer.ami_post_configurer import AmiPostConfigurer from configurer.ami.ami_post_configurer.ami_post_configurer import AmiPostConfigurer
from utils.enums import CertificatesComponent, RemoteDirectories from utils.enums import CertificatesComponent, PasswordToolComponent, RemoteDirectories
@pytest.fixture() @pytest.fixture()
@ -78,6 +78,8 @@ def test_create_custom_dir_success(mock_create_structure, mock_generate_yaml, mo
"remote_certs_path": RemoteDirectories.CERTS, "remote_certs_path": RemoteDirectories.CERTS,
"certs_tool": CertificatesComponent.CERTS_TOOL, "certs_tool": CertificatesComponent.CERTS_TOOL,
"certs_config": CertificatesComponent.CONFIG, "certs_config": CertificatesComponent.CONFIG,
"password_tool_path": RemoteDirectories.PASSWORD_TOOL,
"password_tool": PasswordToolComponent.PASSWORD_TOOL,
} }
mock_generate_yaml.return_value = {"template": "test_value"} mock_generate_yaml.return_value = {"template": "test_value"}
@ -145,11 +147,11 @@ def test_stop_service_fails(mock_ami_post_configurer, mock_exec_command, mock_pa
def test_stop_wazuh_server(mock_ami_post_configurer, mock_exec_command, mock_paramiko, mock_logger): def test_stop_wazuh_server(mock_ami_post_configurer, mock_exec_command, mock_paramiko, mock_logger):
mock_ami_post_configurer.stop_wazuh_server(mock_paramiko.return_value) mock_ami_post_configurer.stop_wazuh_server(mock_paramiko.return_value)
command = "sudo systemctl stop wazuh-server" command = "sudo systemctl stop wazuh-manager"
mock_exec_command.assert_called_once_with(command=command, client=mock_paramiko.return_value) mock_exec_command.assert_called_once_with(command=command, client=mock_paramiko.return_value)
mock_logger.debug.assert_called_once_with("Stopping wazuh-server service") mock_logger.debug.assert_called_once_with("Stopping wazuh-manager service")
mock_logger.info_success.assert_called_once_with("wazuh-server service stopped successfully") mock_logger.info_success.assert_called_once_with("wazuh-manager service stopped successfully")
def test_stop_wazuh_indexer(mock_ami_post_configurer, mock_exec_command, mock_paramiko, mock_logger): def test_stop_wazuh_indexer(mock_ami_post_configurer, mock_exec_command, mock_paramiko, mock_logger):
@ -176,7 +178,9 @@ def test_stop_wazuh_indexer(mock_ami_post_configurer, mock_exec_command, mock_pa
def test_remove_wazuh_indexes(mock_ami_post_configurer, mock_exec_command, mock_paramiko, mock_logger): def test_remove_wazuh_indexes(mock_ami_post_configurer, mock_exec_command, mock_paramiko, mock_logger):
mock_ami_post_configurer.remove_wazuh_indexes(mock_paramiko.return_value) mock_ami_post_configurer.remove_wazuh_indexes(mock_paramiko.return_value)
command = 'sudo curl -s -o /dev/null -w "%{http_code}" -X DELETE -u "admin:admin" -k "https://127.0.0.1:9200/wazuh-*"' command = (
'sudo curl -s -o /dev/null -w "%{http_code}" -X DELETE -u "admin:admin" -k "https://127.0.0.1:9200/wazuh-*"'
)
mock_exec_command.assert_called_once_with(command=command, client=mock_paramiko.return_value) mock_exec_command.assert_called_once_with(command=command, client=mock_paramiko.return_value)
@ -192,6 +196,7 @@ def test_remove_wazuh_indexes_fail(mock_ami_post_configurer, mock_exec_command,
mock_logger.error.assert_called_once_with("Error removing wazuh- indexes") mock_logger.error.assert_called_once_with("Error removing wazuh- indexes")
def test_run_security_init_script(mock_ami_post_configurer, mock_exec_command, mock_paramiko, mock_logger): def test_run_security_init_script(mock_ami_post_configurer, mock_exec_command, mock_paramiko, mock_logger):
mock_ami_post_configurer.run_security_init_script(mock_paramiko.return_value) mock_ami_post_configurer.run_security_init_script(mock_paramiko.return_value)

View File

@ -6,6 +6,7 @@ from pydantic import AnyUrl
from provisioner.models.certs_info import CertsInfo from provisioner.models.certs_info import CertsInfo
from provisioner.models.component_info import ComponentInfo from provisioner.models.component_info import ComponentInfo
from provisioner.models.password_tool_info import PasswordToolInfo
from provisioner.provisioner import Provisioner from provisioner.provisioner import Provisioner
from provisioner.utils import Package_manager, Package_type from provisioner.utils import Package_manager, Package_type
from utils.enums import Component from utils.enums import Component
@ -32,10 +33,13 @@ def component_info_valid(valid_inventory):
"config": "http://packages-dev.wazuh.com/example/config.yml", "config": "http://packages-dev.wazuh.com/example/config.yml",
} }
) )
password_tool = PasswordToolInfo(password_tool_url=AnyUrl("http://packages-dev.wazuh.com/example/password-tool.sh"))
package_type = Package_type.RPM package_type = Package_type.RPM
return Provisioner( return Provisioner(
inventory=valid_inventory, inventory=valid_inventory,
certs=certs, certs=certs,
password_tool=password_tool,
components=[component_server], components=[component_server],
package_type=package_type, package_type=package_type,
) )
@ -56,9 +60,10 @@ def test_provision_success(mock_paramiko, mock_logger, component_info_valid, moc
mock_client_instance = MagicMock() mock_client_instance = MagicMock()
mock_paramiko.return_value = mock_client_instance mock_paramiko.return_value = mock_client_instance
certs_expect_commands = [ tools_expect_commands = [
"mkdir -p ~/wazuh-configure/certs && curl -s -o ~/wazuh-configure/certs/certs-tool.sh 'http://packages-dev.wazuh.com/example/certs-tool.sh'", "mkdir -p ~/wazuh-configure/tools/certs && curl -s -o ~/wazuh-configure/tools/certs/certs-tool.sh 'http://packages-dev.wazuh.com/example/certs-tool.sh'",
"mkdir -p ~/wazuh-configure/certs && curl -s -o ~/wazuh-configure/certs/config.yml 'http://packages-dev.wazuh.com/example/config.yml'", "mkdir -p ~/wazuh-configure/tools/certs && curl -s -o ~/wazuh-configure/tools/certs/config.yml 'http://packages-dev.wazuh.com/example/config.yml'",
"mkdir -p ~/wazuh-configure/tools && curl -s -o ~/wazuh-configure/tools/password-tool.sh 'http://packages-dev.wazuh.com/example/password-tool.sh'"
] ]
dependencies_expect_commands = [ dependencies_expect_commands = [
@ -85,22 +90,22 @@ def test_provision_success(mock_paramiko, mock_logger, component_info_valid, moc
key_filename=str(component_info_valid.inventory.ansible_ssh_private_key_file), key_filename=str(component_info_valid.inventory.ansible_ssh_private_key_file),
) )
assert mock_exec_command.call_count == 7 # 3 for dependencies, 2 for certs, 1 download package, 1 install package assert mock_exec_command.call_count == 8 # 3 for dependencies, 3 for tools (certs-tool and password-tool), 1 download package, 1 install package
# certs # tools
assert certs_expect_commands[0] in mock_exec_command.call_args_list[0].kwargs["command"] assert tools_expect_commands[0] in mock_exec_command.call_args_list[0].kwargs["command"]
assert certs_expect_commands[1] in mock_exec_command.call_args_list[1].kwargs["command"] assert tools_expect_commands[1] in mock_exec_command.call_args_list[1].kwargs["command"]
assert tools_expect_commands[2] in mock_exec_command.call_args_list[2].kwargs["command"]
# dependencies # dependencies
assert dependencies_expect_commands[0] in mock_exec_command.call_args_list[2].kwargs["command"] assert dependencies_expect_commands[0] in mock_exec_command.call_args_list[3].kwargs["command"]
assert dependencies_expect_commands[1] in mock_exec_command.call_args_list[2].kwargs["command"] assert dependencies_expect_commands[1] in mock_exec_command.call_args_list[3].kwargs["command"]
assert dependencies_expect_commands[2] in mock_exec_command.call_args_list[3].kwargs["command"] assert dependencies_expect_commands[2] in mock_exec_command.call_args_list[4].kwargs["command"]
assert dependencies_expect_commands[3] in mock_exec_command.call_args_list[4].kwargs["command"] assert dependencies_expect_commands[3] in mock_exec_command.call_args_list[5].kwargs["command"]
# package # package
assert package_expect_commands[0] in mock_exec_command.call_args_list[5].kwargs["command"] assert package_expect_commands[0] in mock_exec_command.call_args_list[6].kwargs["command"]
assert package_expect_commands[1] in mock_exec_command.call_args_list[6].kwargs["command"] assert package_expect_commands[1] in mock_exec_command.call_args_list[7].kwargs["command"]
mock_logger.debug_title.assert_any_call("Starting provisioning") mock_logger.debug_title.assert_any_call("Starting provisioning")
mock_logger.debug_title.assert_any_call("Provisioning certificates files") mock_logger.debug_title.assert_any_call("Provisioning certificates files")
mock_logger.debug_title.assert_any_call("Starting provisioning for wazuh manager") mock_logger.debug_title.assert_any_call("Starting provisioning for wazuh manager")
@ -120,7 +125,7 @@ def test_certs_tool_provision_success(
getattr(component_info_valid, certs_method)(mock_client_instance) getattr(component_info_valid, certs_method)(mock_client_instance)
mock_exec_command.assert_called_once_with( mock_exec_command.assert_called_once_with(
command=f"mkdir -p ~/wazuh-configure/certs && curl -s -o ~/wazuh-configure/certs/{certs_component} 'http://packages-dev.wazuh.com/example/{certs_component}'", command=f"mkdir -p ~/wazuh-configure/tools/certs && curl -s -o ~/wazuh-configure/tools/certs/{certs_component} 'http://packages-dev.wazuh.com/example/{certs_component}'",
client=mock_client_instance, client=mock_client_instance,
) )
mock_logger.debug.assert_any_call(f"Provisioning {certs_component}") mock_logger.debug.assert_any_call(f"Provisioning {certs_component}")
@ -142,7 +147,7 @@ def test_certs_tool_provision_failure(
getattr(component_info_valid, certs_method)(mock_client_instance) getattr(component_info_valid, certs_method)(mock_client_instance)
mock_exec_command.assert_called_once_with( mock_exec_command.assert_called_once_with(
command=f"mkdir -p ~/wazuh-configure/certs && curl -s -o ~/wazuh-configure/certs/{certs_component} 'http://packages-dev.wazuh.com/example/{certs_component}'", command=f"mkdir -p ~/wazuh-configure/tools/certs && curl -s -o ~/wazuh-configure/tools/certs/{certs_component} 'http://packages-dev.wazuh.com/example/{certs_component}'",
client=mock_client_instance, client=mock_client_instance,
) )
mock_logger.debug.assert_any_call(f"Provisioning {certs_component}") mock_logger.debug.assert_any_call(f"Provisioning {certs_component}")
@ -345,9 +350,10 @@ def test_install_package(
if "installed successfully" in expected_log and "WARNING" not in error_output: if "installed successfully" in expected_log and "WARNING" not in error_output:
mock_logger.info_success.assert_called_once_with(f"{package_name} {expected_log}") mock_logger.info_success.assert_called_once_with(f"{package_name} {expected_log}")
elif "is already installed" in expected_log: elif "is already installed" in expected_log:
mock_logger.debug.assert_has_calls( mock_logger.debug.assert_has_calls([
[mock.call(f"Installing {package_name}"), mock.call(f"{package_name} {expected_log}")] mock.call(f"Installing {package_name}"),
) mock.call(f"{package_name} {expected_log}"),
])
elif "installed successfully" in expected_log and "WARNING" in error_output: elif "installed successfully" in expected_log and "WARNING" in error_output:
mock_logger.warning.assert_called_once_with(f"{error_output}") mock_logger.warning.assert_called_once_with(f"{error_output}")
mock_logger.info_success.assert_called_once_with(f"{package_name} {expected_log}") mock_logger.info_success.assert_called_once_with(f"{package_name} {expected_log}")

View File

@ -1,2 +1,2 @@
from .enums import CertificatesComponent, Component, RemoteDirectories from .enums import CertificatesComponent, Component, PasswordToolComponent, RemoteDirectories
from .logger import Logger from .logger import Logger

View File

@ -8,6 +8,10 @@ class Component(StrEnum):
ALL = auto() ALL = auto()
class PasswordToolComponent(StrEnum):
PASSWORD_TOOL = "password-tool.sh"
class CertificatesComponent(StrEnum): class CertificatesComponent(StrEnum):
CERTS_TOOL = "certs-tool.sh" CERTS_TOOL = "certs-tool.sh"
CONFIG = "config.yml" CONFIG = "config.yml"
@ -15,5 +19,7 @@ class CertificatesComponent(StrEnum):
class RemoteDirectories(StrEnum): class RemoteDirectories(StrEnum):
WAZUH_ROOT_DIR = "~/wazuh-configure" WAZUH_ROOT_DIR = "~/wazuh-configure"
TOOLS_DIR = f"{WAZUH_ROOT_DIR}/tools"
PACKAGES = f"{WAZUH_ROOT_DIR}/packages" PACKAGES = f"{WAZUH_ROOT_DIR}/packages"
CERTS = f"{WAZUH_ROOT_DIR}/certs" CERTS = f"{TOOLS_DIR}/certs"
PASSWORD_TOOL = f"{TOOLS_DIR}"