Exemple #1
0
def create_group(group_id):
    """Creates a group.

    :param group_id: Group ID.
    :return: Confirmation message.
    """
    # Input Validation of group_id
    if not InputValidator().group(group_id):
        raise WazuhError(1722)

    group_path = path.join(common.shared_path, group_id)

    if group_id.lower() == "default" or path.exists(group_path):
        raise WazuhError(1711, extra_message=group_id)

    # Create group in /etc/shared
    group_def_path = path.join(common.shared_path, 'agent-template.conf')
    try:
        mkdir_with_mode(group_path)
        copyfile(group_def_path, path.join(group_path, 'agent.conf'))
        chown_r(group_path, common.wazuh_uid(), common.wazuh_gid())
        chmod_r(group_path, 0o660)
        chmod(group_path, 0o770)
        msg = f"Group '{group_id}' created."
    except Exception as e:
        raise WazuhInternalError(1005, extra_message=str(e))

    return WazuhResult({'message': msg})
Exemple #2
0
        def overwrite_or_create_files(filename: str, data: Dict):
            """Update a file coming from the master.

            Move a file which is inside the unzipped directory that comes from master to the path
            specified in 'filename'. If the file is 'merged' type, it is first split into files
            and then moved to their final directory.

            Parameters
            ----------
            filename : str
                Filename inside unzipped dir to update.
            data : dict
                File metadata such as modification time, whether it's a merged file or not, etc.
            """
            full_filename_path = os.path.join(common.wazuh_path, filename)
            if os.path.basename(filename) == 'client.keys':
                self._check_removed_agents(os.path.join(zip_path, filename),
                                           logger)

            if data['merged']:  # worker nodes can only receive agent-groups files
                # Split merged file into individual files inside zipdir (directory containing unzipped files),
                # and then move each one to the destination directory (<wazuh_path>/filename).
                for name, content, _ in wazuh.core.cluster.cluster.unmerge_info(
                        'agent-groups', zip_path, filename):
                    full_unmerged_name = os.path.join(common.wazuh_path, name)
                    tmp_unmerged_path = full_unmerged_name + '.tmp'
                    with open(tmp_unmerged_path, 'wb') as f:
                        f.write(content)
                    safe_move(tmp_unmerged_path,
                              full_unmerged_name,
                              permissions=self.cluster_items['files'][
                                  data['cluster_item_key']]['permissions'],
                              ownership=(common.wazuh_uid(),
                                         common.wazuh_gid()))
            else:
                # Create destination dir if it doesn't exist.
                if not os.path.exists(os.path.dirname(full_filename_path)):
                    utils.mkdir_with_mode(os.path.dirname(full_filename_path))
                # Move the file from zipdir (directory containing unzipped files) to <wazuh_path>/filename.
                safe_move(os.path.join(zip_path, filename),
                          full_filename_path,
                          permissions=self.cluster_items['files'][
                              data['cluster_item_key']]['permissions'],
                          ownership=(common.wazuh_uid(), common.wazuh_gid()))
Exemple #3
0
def generate_keypair():
    """Generate key files to keep safe or load existing public and private keys."""
    try:
        if not os.path.exists(_private_key_path) or not os.path.exists(
                _public_key_path):
            private_key, public_key = change_keypair()
            try:
                os.chown(_private_key_path, wazuh_uid(), wazuh_gid())
                os.chown(_public_key_path, wazuh_uid(), wazuh_gid())
            except PermissionError:
                pass
            os.chmod(_private_key_path, 0o640)
            os.chmod(_public_key_path, 0o640)
        else:
            with open(_private_key_path, mode='r') as key_file:
                private_key = key_file.read()
            with open(_public_key_path, mode='r') as key_file:
                public_key = key_file.read()
    except IOError:
        raise WazuhInternalError(6003)

    return private_key, public_key
Exemple #4
0
    def set_agent_group_file(agent_id, group_id):
        try:
            agent_group_path = path.join(common.groups_path, agent_id)
            new_file = not path.exists(agent_group_path)

            with open(agent_group_path, 'w') as f_group:
                f_group.write(group_id)

            if new_file:
                chown(agent_group_path, common.wazuh_uid(), common.wazuh_gid())
                chmod(agent_group_path, 0o660)
        except Exception as e:
            raise WazuhInternalError(1005, extra_message=str(e))
Exemple #5
0
def generate_secret():
    """Generate secret file to keep safe or load existing secret."""
    try:
        if not os.path.exists(_secret_file_path):
            jwt_secret = token_urlsafe(512)
            with open(_secret_file_path, mode='x') as secret_file:
                secret_file.write(jwt_secret)
            try:
                chown(_secret_file_path, wazuh_uid(), wazuh_gid())
            except PermissionError:
                pass
            os.chmod(_secret_file_path, 0o640)
        else:
            with open(_secret_file_path, mode='r') as secret_file:
                jwt_secret = secret_file.readline()
    except IOError:
        raise WazuhInternalError(6003)

    return jwt_secret
def start(foreground, root, config_file):
    """Run the Wazuh API.

    If another Wazuh API is running, this function fails.
    This function exits with 0 if successful or 1 if failed because the API was already running.

    Arguments
    ---------
    foreground : bool
        If the API must be daemonized or not
    root : bool
        If true, the daemon is run as root. Normally not recommended for security reasons
    config_file : str
        Path to the API config file
    """
    import asyncio
    import logging
    import os
    import ssl

    import connexion
    import uvloop
    from aiohttp_cache import setup_cache

    import wazuh.security
    from api import __path__ as api_path
    # noinspection PyUnresolvedReferences
    from api import validator
    from api.api_exception import APIError
    from api.constants import CONFIG_FILE_PATH
    from api.middlewares import set_user_name, security_middleware, response_postprocessing, request_logging, \
        set_secure_headers
    from api.uri_parser import APIUriParser
    from api.util import to_relative_path
    from wazuh.core import pyDaemonModule

    configuration.api_conf.update(
        configuration.read_yaml_config(config_file=config_file))
    api_conf = configuration.api_conf
    security_conf = configuration.security_conf
    log_path = api_conf['logs']['path']

    # Set up logger
    set_logging(log_path=log_path,
                debug_mode=api_conf['logs']['level'],
                foreground_mode=foreground)
    logger = logging.getLogger('wazuh-api')

    # Set correct permissions on api.log file
    if os.path.exists(os.path.join(common.wazuh_path, log_path)):
        os.chown(os.path.join(common.wazuh_path, log_path), common.wazuh_uid(),
                 common.wazuh_gid())
        os.chmod(os.path.join(common.wazuh_path, log_path), 0o660)

    # Configure https
    ssl_context = None
    if api_conf['https']['enabled']:
        try:
            # Generate SSL if it does not exist and HTTPS is enabled
            if not os.path.exists(
                    api_conf['https']['key']) or not os.path.exists(
                        api_conf['https']['cert']):
                logger.info(
                    'HTTPS is enabled but cannot find the private key and/or certificate. '
                    'Attempting to generate them')
                private_key = configuration.generate_private_key(
                    api_conf['https']['key'])
                logger.info(
                    f"Generated private key file in WAZUH_PATH/{to_relative_path(api_conf['https']['key'])}"
                )
                configuration.generate_self_signed_certificate(
                    private_key, api_conf['https']['cert'])
                logger.info(
                    f"Generated certificate file in WAZUH_PATH/{to_relative_path(api_conf['https']['cert'])}"
                )

            # Load SSL context
            allowed_ssl_ciphers = {
                'tls': ssl.PROTOCOL_TLS,
                'tlsv1': ssl.PROTOCOL_TLSv1,
                'tlsv1.1': ssl.PROTOCOL_TLSv1_1,
                'tlsv1.2': ssl.PROTOCOL_TLSv1_2
            }
            try:
                ssl_cipher = allowed_ssl_ciphers[api_conf['https']
                                                 ['ssl_cipher'].lower()]
            except (KeyError, AttributeError):
                # KeyError: invalid string value
                # AttributeError: invalid boolean value
                logger.error(
                    str(
                        APIError(
                            2003,
                            details='SSL cipher is not valid. Allowed values: '
                            'TLS, TLSv1, TLSv1.1, TLSv1.2')))
                sys.exit(1)
            ssl_context = ssl.SSLContext(protocol=ssl_cipher)
            if api_conf['https']['use_ca']:
                ssl_context.verify_mode = ssl.CERT_REQUIRED
                ssl_context.load_verify_locations(api_conf['https']['ca'])
            ssl_context.load_cert_chain(certfile=api_conf['https']['cert'],
                                        keyfile=api_conf['https']['key'])
        except ssl.SSLError:
            logger.error(
                str(
                    APIError(
                        2003,
                        details=
                        'Private key does not match with the certificate')))
            sys.exit(1)
        except IOError as e:
            if e.errno == 22:
                logger.error(
                    str(APIError(2003, details='PEM phrase is not correct')))
            elif e.errno == 13:
                logger.error(
                    str(
                        APIError(
                            2003,
                            details=
                            'Ensure the certificates have the correct permissions'
                        )))
            else:
                print(
                    'Wazuh API SSL ERROR. Please, ensure if path to certificates is correct in the configuration '
                    f'file WAZUH_PATH/{to_relative_path(CONFIG_FILE_PATH)}')
            sys.exit(1)

    # Drop privileges to wazuh
    if not root:
        if api_conf['drop_privileges']:
            os.setgid(common.wazuh_gid())
            os.setuid(common.wazuh_uid())
    else:
        print(f"Starting API as root")

    # Foreground/Daemon
    if not foreground:
        pyDaemonModule.pyDaemon()
        pyDaemonModule.create_pid('wazuh-apid', os.getpid())
    else:
        print(f"Starting API in foreground")

    # Load the SPEC file into memory to use as a reference for future calls
    wazuh.security.load_spec()

    # Set up API
    asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
    app = connexion.AioHttpApp(__name__,
                               host=api_conf['host'],
                               port=api_conf['port'],
                               specification_dir=os.path.join(
                                   api_path[0], 'spec'),
                               options={
                                   "swagger_ui": False,
                                   'uri_parser_class': APIUriParser
                               },
                               only_one_api=True)
    app.add_api('spec.yaml',
                arguments={
                    'title': 'Wazuh API',
                    'protocol':
                    'https' if api_conf['https']['enabled'] else 'http',
                    'host': api_conf['host'],
                    'port': api_conf['port']
                },
                strict_validation=True,
                validate_responses=False,
                pass_context_arg_name='request',
                options={
                    "middlewares": [
                        response_postprocessing, set_user_name,
                        security_middleware, request_logging,
                        set_secure_headers
                    ]
                })

    # Enable CORS
    if api_conf['cors']['enabled']:
        import aiohttp_cors
        cors = aiohttp_cors.setup(
            app.app,
            defaults={
                api_conf['cors']['source_route']:
                aiohttp_cors.ResourceOptions(
                    expose_headers=api_conf['cors']['expose_headers'],
                    allow_headers=api_conf['cors']['allow_headers'],
                    allow_credentials=api_conf['cors']['allow_credentials'])
            })
        # Configure CORS on all endpoints.
        for route in list(app.app.router.routes()):
            cors.add(route)

    # Enable cache plugin
    if api_conf['cache']['enabled']:
        setup_cache(app.app)

    # API configuration logging
    logger.debug(f'Loaded API configuration: {api_conf}')
    logger.debug(f'Loaded security API configuration: {security_conf}')

    # Start API
    app.run(port=api_conf['port'],
            host=api_conf['host'],
            ssl_context=ssl_context,
            access_log_class=alogging.AccessLogger,
            use_default_access_log=True)
Exemple #7
0
    if args.version:
        print_version()
        sys.exit(0)

    # Set logger
    try:
        debug_mode = configuration.get_internal_options_value(
            'wazuh_clusterd', 'debug', 2, 0) or args.debug_level
    except Exception:
        debug_mode = 0

    # set correct permissions on cluster.log file
    if os.path.exists('{0}/logs/cluster.log'.format(common.wazuh_path)):
        os.chown('{0}/logs/cluster.log'.format(common.wazuh_path),
                 common.wazuh_uid(), common.wazuh_gid())
        os.chmod('{0}/logs/cluster.log'.format(common.wazuh_path), 0o660)

    main_logger = set_logging(foreground_mode=args.foreground,
                              debug_mode=debug_mode)

    cluster_configuration = cluster_utils.read_config(
        config_file=args.config_file)
    if cluster_configuration['disabled']:
        sys.exit(0)
    cluster_items = cluster_utils.get_cluster_items()
    try:
        wazuh.core.cluster.cluster.check_cluster_config(cluster_configuration)
    except Exception as e:
        main_logger.error(e)
        sys.exit(1)
Exemple #8
0
        async def update_file(name: str, data: Dict):
            """Update a local file with one received from a worker.

            The modification date is checked to decide whether to update ir or not.

            Parameters
            ----------
            name : str
                Relative path of the file.
            data : dict
                Metadata of the file (MD5, merged, etc).
            """
            # Full path
            full_path, error_updating_file = os.path.join(
                common.wazuh_path, name), False

            try:
                # Only valid client.keys is the local one (master).
                if os.path.basename(name) == 'client.keys':
                    self.logger.warning(
                        "Client.keys received in a master node")
                    raise exception.WazuhClusterError(3007)

                # If the file is merged, create individual files from it.
                if data['merged']:
                    for file_path, file_data, file_time in wazuh.core.cluster.cluster.unmerge_info(
                            data['merge_type'], decompressed_files_path,
                            data['merge_name']):
                        # Destination path.
                        full_unmerged_name = os.path.join(
                            common.wazuh_path, file_path)
                        # Path where to create the file before moving it to the destination path (with safe_move).
                        tmp_unmerged_path = os.path.join(
                            common.wazuh_path, 'queue', 'cluster', self.name,
                            os.path.basename(file_path))

                        try:
                            agent_id = os.path.basename(file_path)
                            # If the agent does not exist on the master, do not copy its file from the worker.
                            if agent_id not in agent_ids:
                                n_errors['warnings'][data['cluster_item_key']] = 1 \
                                    if n_errors['warnings'].get(data['cluster_item_key']) is None \
                                    else n_errors['warnings'][data['cluster_item_key']] + 1

                                self.logger.debug2(
                                    f"Received group of an non-existent agent '{agent_id}'"
                                )
                                continue

                            # Format the file_data specified inside the merged file.
                            try:
                                mtime = datetime.strptime(
                                    file_time, '%Y-%m-%d %H:%M:%S.%f')
                            except ValueError:
                                mtime = datetime.strptime(
                                    file_time, '%Y-%m-%d %H:%M:%S')

                            # If the file already existed, check if it is older than the one to be copied from worker.
                            if os.path.isfile(full_unmerged_name):
                                local_mtime = datetime.utcfromtimestamp(
                                    int(os.stat(full_unmerged_name).st_mtime))
                                if local_mtime > mtime:
                                    logger.debug2(
                                        f"Receiving an old file ({file_path})")
                                    continue

                            # Create file in temporal path and safe move it to the destination path.
                            with open(tmp_unmerged_path, 'wb') as f:
                                f.write(file_data)

                            mtime_epoch = timegm(mtime.timetuple())
                            utils.safe_move(
                                tmp_unmerged_path,
                                full_unmerged_name,
                                ownership=(common.wazuh_uid(),
                                           common.wazuh_gid()),
                                permissions=self.cluster_items['files'][
                                    data['cluster_item_key']]['permissions'],
                                time=(mtime_epoch, mtime_epoch))
                            self.integrity_sync_status[
                                'total_extra_valid'] += 1
                        except Exception as e:
                            self.logger.error(
                                f"Error updating agent group/status ({tmp_unmerged_path}): {e}"
                            )

                            n_errors['errors'][data['cluster_item_key']] = 1 \
                                if n_errors['errors'].get(data['cluster_item_key']) is None \
                                else n_errors['errors'][data['cluster_item_key']] + 1

                        # Let other tasks (DAPI, etc) that may arrive while processing extra-valid files to be run.
                        await asyncio.sleep(0)

                # If the file is not merged, move it directly to the destination path.
                else:
                    zip_path = os.path.join(decompressed_files_path, name)
                    utils.safe_move(zip_path,
                                    full_path,
                                    ownership=(common.wazuh_uid(),
                                               common.wazuh_gid()),
                                    permissions=self.cluster_items['files']
                                    [data['cluster_item_key']]['permissions'])

            except exception.WazuhException as e:
                logger.debug2(f"Warning updating file '{name}': {e}")
                error_tag = 'warnings'
                error_updating_file = True
            except Exception as e:
                logger.debug2(f"Error updating file '{name}': {e}")
                error_tag = 'errors'
                error_updating_file = True

            if error_updating_file:
                n_errors[error_tag][data['cluster_item_key']] = 1 if not n_errors[error_tag].get(
                    data['cluster_item_key']) \
                    else n_errors[error_tag][data['cluster_item_key']] + 1
Exemple #9
0
def start(foreground: bool, root: bool, config_file: str):
    """Run the Wazuh API.

    If another Wazuh API is running, this function fails.
    This function exits with 0 if successful or 1 if failed because the API was already running.

    Arguments
    ---------
    foreground : bool
        If the API must be daemonized or not
    root : bool
        If true, the daemon is run as root. Normally not recommended for security reasons
    config_file : str
        Path to the API config file
    """
    import logging
    import os
    from api import alogging, configuration
    from wazuh.core import pyDaemonModule, common, utils

    def set_logging(log_path='logs/api.log',
                    foreground_mode=False,
                    debug_mode='info'):
        for logger_name in ('connexion.aiohttp_app',
                            'connexion.apis.aiohttp_api', 'wazuh-api'):
            api_logger = alogging.APILogger(
                log_path=log_path,
                foreground_mode=foreground_mode,
                logger_name=logger_name,
                debug_level='info' if logger_name != 'wazuh-api'
                and debug_mode != 'debug2' else debug_mode)
            api_logger.setup_logger()

    if config_file is not None:
        configuration.api_conf.update(
            configuration.read_yaml_config(config_file=config_file))
    api_conf = configuration.api_conf
    security_conf = configuration.security_conf

    # Set up logger
    set_logging(log_path=API_LOG_FILE_PATH,
                debug_mode=api_conf['logs']['level'],
                foreground_mode=foreground)
    logger = logging.getLogger('wazuh-api')

    import asyncio
    import ssl

    import connexion
    import uvloop
    from aiohttp_cache import setup_cache
    from api import __path__ as api_path
    # noinspection PyUnresolvedReferences
    from api import validator
    from api.api_exception import APIError
    from api.constants import CONFIG_FILE_PATH
    from api.middlewares import set_user_name, security_middleware, response_postprocessing, request_logging, \
        set_secure_headers
    from api.signals import modify_response_headers
    from api.uri_parser import APIUriParser
    from api.util import to_relative_path
    from wazuh.rbac.orm import create_rbac_db

    # Check deprecated options. To delete after expected versions
    if 'use_only_authd' in api_conf:
        del api_conf['use_only_authd']
        logger.warning(
            "'use_only_authd' option was deprecated on v4.3.0. Wazuh Authd will always be used"
        )

    if 'path' in api_conf['logs']:
        del api_conf['logs']['path']
        logger.warning(
            "Log 'path' option was deprecated on v4.3.0. Default path will always be used: "
            f"{API_LOG_FILE_PATH}")

    # Set correct permissions on api.log file
    if os.path.exists(os.path.join(common.wazuh_path, API_LOG_FILE_PATH)):
        os.chown(os.path.join(common.wazuh_path, API_LOG_FILE_PATH),
                 common.wazuh_uid(), common.wazuh_gid())
        os.chmod(os.path.join(common.wazuh_path, API_LOG_FILE_PATH), 0o660)

    # Configure https
    ssl_context = None
    if api_conf['https']['enabled']:
        try:
            # Generate SSL if it does not exist and HTTPS is enabled
            if not os.path.exists(
                    api_conf['https']['key']) or not os.path.exists(
                        api_conf['https']['cert']):
                logger.info(
                    'HTTPS is enabled but cannot find the private key and/or certificate. '
                    'Attempting to generate them')
                private_key = configuration.generate_private_key(
                    api_conf['https']['key'])
                logger.info(
                    f"Generated private key file in WAZUH_PATH/{to_relative_path(api_conf['https']['key'])}"
                )
                configuration.generate_self_signed_certificate(
                    private_key, api_conf['https']['cert'])
                logger.info(
                    f"Generated certificate file in WAZUH_PATH/{to_relative_path(api_conf['https']['cert'])}"
                )

            # Load SSL context
            allowed_ssl_protocols = {
                'tls': ssl.PROTOCOL_TLS,
                'tlsv1': ssl.PROTOCOL_TLSv1,
                'tlsv1.1': ssl.PROTOCOL_TLSv1_1,
                'tlsv1.2': ssl.PROTOCOL_TLSv1_2
            }

            ssl_protocol = allowed_ssl_protocols[api_conf['https']
                                                 ['ssl_protocol'].lower()]
            ssl_context = ssl.SSLContext(protocol=ssl_protocol)

            if api_conf['https']['use_ca']:
                ssl_context.verify_mode = ssl.CERT_REQUIRED
                ssl_context.load_verify_locations(api_conf['https']['ca'])

            ssl_context.load_cert_chain(certfile=api_conf['https']['cert'],
                                        keyfile=api_conf['https']['key'])

            # Load SSL ciphers if any has been specified
            if api_conf['https']['ssl_ciphers']:
                ssl_ciphers = api_conf['https']['ssl_ciphers'].upper()
                try:
                    ssl_context.set_ciphers(ssl_ciphers)
                except ssl.SSLError:
                    error = APIError(2003,
                                     details='SSL ciphers cannot be selected')
                    logger.error(error)
                    raise error

        except ssl.SSLError:
            error = APIError(
                2003,
                details='Private key does not match with the certificate')
            logger.error(error)
            raise error
        except IOError as exc:
            if exc.errno == 22:
                error = APIError(2003, details='PEM phrase is not correct')
                logger.error(error)
                raise error
            elif exc.errno == 13:
                error = APIError(
                    2003,
                    details=
                    'Ensure the certificates have the correct permissions')
                logger.error(error)
                raise error
            else:
                msg = f'Wazuh API SSL ERROR. Please, ensure if path to certificates is correct in the configuration ' \
                      f'file WAZUH_PATH/{to_relative_path(CONFIG_FILE_PATH)}'
                print(msg)
                logger.error(msg)
                raise exc

    utils.check_pid('wazuh-apid')
    # Drop privileges to ossec
    if not root:
        if api_conf['drop_privileges']:
            os.setgid(common.wazuh_gid())
            os.setuid(common.wazuh_uid())
    else:
        print(f"Starting API as root")

    # Foreground/Daemon
    if not foreground:
        pyDaemonModule.pyDaemon()
        pid = os.getpid()
        pyDaemonModule.create_pid('wazuh-apid', pid) or register(
            pyDaemonModule.delete_pid, 'wazuh-apid', pid)
    else:
        print(f"Starting API in foreground")
    create_rbac_db()

    # Spawn child processes with their own needed imports
    if 'thread_pool' not in common.mp_pools.get():
        loop = asyncio.get_event_loop()
        loop.run_until_complete(
            asyncio.wait([
                loop.run_in_executor(
                    pool, getattr(sys.modules[__name__], f'spawn_{name}'))
                for name, pool in common.mp_pools.get().items()
            ]))

    # Set up API
    asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
    app = connexion.AioHttpApp(__name__,
                               host=api_conf['host'],
                               port=api_conf['port'],
                               specification_dir=os.path.join(
                                   api_path[0], 'spec'),
                               options={
                                   "swagger_ui": False,
                                   'uri_parser_class': APIUriParser
                               },
                               only_one_api=True)
    app.add_api('spec.yaml',
                arguments={
                    'title': 'Wazuh API',
                    'protocol':
                    'https' if api_conf['https']['enabled'] else 'http',
                    'host': api_conf['host'],
                    'port': api_conf['port']
                },
                strict_validation=True,
                validate_responses=False,
                pass_context_arg_name='request',
                options={
                    "middlewares": [
                        response_postprocessing, set_user_name,
                        security_middleware, request_logging,
                        set_secure_headers
                    ]
                })

    # Maximum body size that the API can accept (bytes)
    app.app._client_max_size = configuration.api_conf['max_upload_size']

    # Enable CORS
    if api_conf['cors']['enabled']:
        import aiohttp_cors
        cors = aiohttp_cors.setup(
            app.app,
            defaults={
                api_conf['cors']['source_route']:
                aiohttp_cors.ResourceOptions(
                    expose_headers=api_conf['cors']['expose_headers'],
                    allow_headers=api_conf['cors']['allow_headers'],
                    allow_credentials=api_conf['cors']['allow_credentials'])
            })
        # Configure CORS on all endpoints.
        for route in list(app.app.router.routes()):
            cors.add(route)

    # Enable cache plugin
    if api_conf['cache']['enabled']:
        setup_cache(app.app)

    # Add application signals
    app.app.on_response_prepare.append(modify_response_headers)

    # API configuration logging
    logger.debug(f'Loaded API configuration: {api_conf}')
    logger.debug(f'Loaded security API configuration: {security_conf}')

    # Start API
    try:
        app.run(port=api_conf['port'],
                host=api_conf['host'],
                ssl_context=ssl_context,
                access_log_class=alogging.AccessLogger,
                use_default_access_log=True)
    except OSError as exc:
        if exc.errno == 98:
            error = APIError(2010)
            logger.error(error)
            raise error
        else:
            logger.error(exc)
            raise exc
Exemple #10
0
def test_wazuh_gid():
    with patch('wazuh.core.common.getgrnam', return_value=getgrnam("root")):
        wazuh_gid()
Exemple #11
0
    def process_files_from_worker(files_metadata: Dict,
                                  decompressed_files_path: str,
                                  cluster_items: dict, worker_name: str,
                                  timeout: int):
        """Iterate over received files from worker and updates the local ones.

        Parameters
        ----------
        files_metadata : dict
            Dictionary containing file metadata (each key is a filepath and each value its metadata).
        decompressed_files_path : str
            Filepath of the decompressed received zipfile.
        cluster_items : dict
            Object containing cluster internal variables from the cluster.json file.
        worker_name : str
            Name of the worker instance. Used to access the correct worker folder.
        timeout : int
            Seconds to wait before stopping the task.

        Returns
        -------
        result : dict
            Dict containing number of updated chunks and any error found in the process.
        """
        result = {
            'total_updated': 0,
            'errors_per_folder': defaultdict(list),
            'generic_errors': []
        }

        try:
            with utils.Timeout(timeout):
                for file_path, data in files_metadata.items():
                    full_path = os.path.join(common.wazuh_path, file_path)
                    item_key = data['cluster_item_key']

                    # Only valid client.keys is the local one (master).
                    if os.path.basename(file_path) == 'client.keys':
                        raise exception.WazuhClusterError(3007)

                    # If the file is merged, create individual files from it.
                    if data['merged']:
                        for unmerged_file_path, file_data, file_time in wazuh.core.cluster.cluster.unmerge_info(
                                data['merge_type'], decompressed_files_path,
                                data['merge_name']):
                            try:
                                # Destination path.
                                full_unmerged_name = os.path.join(
                                    common.wazuh_path, unmerged_file_path)
                                # Path where to create the file before moving it to the destination path.
                                tmp_unmerged_path = os.path.join(
                                    common.wazuh_path, 'queue', 'cluster',
                                    worker_name,
                                    os.path.basename(unmerged_file_path))

                                # Format the file_data specified inside the merged file.
                                try:
                                    mtime = datetime.strptime(
                                        file_time, '%Y-%m-%d %H:%M:%S.%f')
                                except ValueError:
                                    mtime = datetime.strptime(
                                        file_time, '%Y-%m-%d %H:%M:%S')

                                # If the file already existed, check if it is older than the one from worker.
                                if os.path.isfile(full_unmerged_name):
                                    local_mtime = datetime.utcfromtimestamp(
                                        int(
                                            os.stat(
                                                full_unmerged_name).st_mtime))
                                    if local_mtime > mtime:
                                        continue

                                # Create file in temporal path and safe move it to the destination path.
                                with open(tmp_unmerged_path, 'wb') as f:
                                    f.write(file_data)

                                mtime_epoch = timegm(mtime.timetuple())
                                utils.safe_move(
                                    tmp_unmerged_path,
                                    full_unmerged_name,
                                    ownership=(common.wazuh_uid(),
                                               common.wazuh_gid()),
                                    permissions=cluster_items['files']
                                    [item_key]['permissions'],
                                    time=(mtime_epoch, mtime_epoch))
                                result['total_updated'] += 1
                            except TimeoutError as e:
                                raise e
                            except Exception as e:
                                result['errors_per_folder'][item_key].append(
                                    str(e))

                    # If the file is not 'merged' type, move it directly to the destination path.
                    else:
                        try:
                            zip_path = os.path.join(decompressed_files_path,
                                                    file_path)
                            utils.safe_move(zip_path,
                                            full_path,
                                            ownership=(common.wazuh_uid(),
                                                       common.wazuh_gid()),
                                            permissions=cluster_items['files']
                                            [item_key]['permissions'])
                        except TimeoutError as e:
                            raise e
                        except Exception as e:
                            result['errors_per_folder'][item_key].append(
                                str(e))
        except TimeoutError:
            result['generic_errors'].append(
                "Timeout processing extra-valid files.")
        except Exception as e:
            result['generic_errors'].append(
                f"Error updating worker files (extra valid): '{str(e)}'.")

        return result