Exemple #1
0
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
    from api import alogging, configuration

    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.signals import modify_response_headers
    from api.uri_parser import APIUriParser
    from api.util import to_relative_path
    from wazuh.core import pyDaemonModule

    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
    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')

    # `use_only_authd` deprecated on v4.3.0. To be removed
    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"
        )

    # 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_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:
                    logger.error(
                        str(
                            APIError(
                                2003,
                                details='SSL ciphers cannot be selected')))
                    sys.exit(1)

        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
    utils.check_pid('wazuh-apid')
    if not foreground:
        pyDaemonModule.pyDaemon()
    pid = os.getpid()
    pyDaemonModule.create_pid('wazuh-apid', pid) or register(
        pyDaemonModule.delete_pid, 'wazuh-apid', pid)
    if foreground:
        print(f"Starting API in foreground (pid: {pid})")

    # 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
                    ]
                })

    # 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
    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 #2
0
        main_logger.error("Cluster is already running.")
        sys.exit(1)

    # clean
    wazuh.core.cluster.cluster.clean_up()

    # Foreground/Daemon
    if not args.foreground:
        pyDaemonModule.pyDaemon()

    # Drop privileges to wazuh
    if not args.root:
        os.setgid(common.wazuh_gid())
        os.setuid(common.wazuh_uid())

    check_pid('wazuh-clusterd')
    pid = os.getpid()
    pyDaemonModule.create_pid('wazuh-clusterd', pid)
    if args.foreground:
        print(f"Starting cluster in foreground (pid: {pid})")

    original_sig_handler = signal(SIGTERM, exit_handler)

    main_function = master_main if cluster_configuration[
        'node_type'] == 'master' else worker_main
    try:
        asyncio.run(
            main_function(args, cluster_configuration, cluster_items,
                          main_logger))
    except KeyboardInterrupt:
        main_logger.info("SIGINT received. Bye!")