Skip to content

Create a new Provider for Prowler

Here you can find how to create a new Provider in Prowler to give support for making all security checks needed and make your cloud safer!

Introduction

Providers are the foundation on which Prowler is built, a simple definition for a cloud provider could be "third-party company that offers a platform where any IT resource you need is available at any time upon request". The most well-known cloud providers are Amazon Web Services, Azure from Microsoft and Google Cloud which are already supported by Prowler.

To create a new provider that is not supported now by Prowler and add your security checks you must create a new folder to store all the related files within it (services, checks, etc.). It must be store in route prowler/providers/<new_provider_name>/.

Inside that folder, you MUST create the following files and folders:

  • A lib folder: to store all extra functions.
  • A services folder: to store all services to audit.
  • An empty __init__.py: to make Python treat this service folder as a package.
  • A <new_provider_name>_provider.py, containing all the provider's logic necessary to get authenticated in the provider, configurations and extra data useful for final report.
  • A models.py, containing all the models necessary for the new provider.

Provider

The structure for Prowler's providers is set up in such a way that they can be utilized through a generic service specific to each provider. This is achieved by passing the required parameters to the constructor, which in turn initializes all the necessary session values.

Base Class

All the providers in Prowler inherits from the same base class. It is an abstract base class that defines the interface for all provider classes. The code of the class is the next:

Provider Base Class
from abc import ABC, abstractmethod
from typing import Any

class Provider(ABC):
    """
    The Provider class is an abstract base class that defines the interface for all provider classes in the auditing system.

    Attributes:
        type (property): The type of the provider.
        identity (property): The identity of the provider for auditing.
        session (property): The session of the provider for auditing.
        audit_config (property): The audit configuration of the provider.
        output_options (property): The output configuration of the provider for auditing.

    Methods:
        print_credentials(): Displays the provider's credentials used for auditing in the command-line interface.
        setup_session(): Sets up the session for the provider.
        get_output_mapping(): Returns the output mapping between the provider and the generic model.
        validate_arguments(): Validates the arguments for the provider.
        get_checks_to_execute_by_audit_resources(): Returns a set of checks based on the input resources to scan.

    Note:
        This is an abstract base class and should not be instantiated directly. Each provider should implement its own
        version of the Provider class by inheriting from this base class and implementing the required methods and properties.
    """

    @property
    @abstractmethod
    def type(self) -> str:
        """
        type method stores the provider's type.

        This method needs to be created in each provider.
        """
        raise NotImplementedError()

    @property
    @abstractmethod
    def identity(self) -> str:
        """
        identity method stores the provider's identity to audit.

        This method needs to be created in each provider.
        """
        raise NotImplementedError()

    @abstractmethod
    def setup_session(self) -> Any:
        """
        setup_session sets up the session for the provider.

        This method needs to be created in each provider.
        """
        raise NotImplementedError()

    @property
    @abstractmethod
    def session(self) -> str:
        """
        session method stores the provider's session to audit.

        This method needs to be created in each provider.
        """
        raise NotImplementedError()

    @property
    @abstractmethod
    def audit_config(self) -> str:
        """
        audit_config method stores the provider's audit configuration.

        This method needs to be created in each provider.
        """
        raise NotImplementedError()

    @abstractmethod
    def print_credentials(self) -> None:
        """
        print_credentials is used to display in the CLI the provider's credentials used to audit.

        This method needs to be created in each provider.
        """
        raise NotImplementedError()

    @property
    @abstractmethod
    def output_options(self) -> str:
        """
        output_options method returns the provider's audit output configuration.

        This method needs to be created in each provider.
        """
        raise NotImplementedError()

    @output_options.setter
    @abstractmethod
    def output_options(self, value: str) -> Any:
        """
        output_options.setter sets the provider's audit output configuration.

        This method needs to be created in each provider.
        """
        raise NotImplementedError()

    @abstractmethod
    def get_output_mapping(self) -> dict:
        """
        get_output_mapping returns the output mapping between the provider and the generic model.

        This method needs to be created in each provider.
        """
        raise NotImplementedError()

    def validate_arguments(self) -> None:
        """
        validate_arguments validates the arguments for the provider.

        This method can be overridden in each provider if needed.
        """
        raise NotImplementedError()

    def get_checks_to_execute_by_audit_resources(self) -> set:
        """
        get_checks_to_execute_by_audit_resources returns a set of checks based on the input resources to scan.

        This is a fallback that returns None if the service has not implemented this function.
        """
        return set()

    @property
    @abstractmethod
    def mutelist(self):
        """
        mutelist method returns the provider's mutelist.

        This method needs to be created in each provider.
        """
        raise NotImplementedError()

    @mutelist.setter
    @abstractmethod
    def mutelist(self, path: str):
        """
        mutelist.setter sets the provider's mutelist.

        This method needs to be created in each provider.
        """
        raise NotImplementedError()

Provider Class

Due to the complexity and differences of each provider use the rest of the providers as a template for the implementation.

To facilitate understanding here is a pseudocode of how the most basic provider could be with examples.

Provider Example Class
# Library imports to authenticate in the Provider

from prowler.config.config import load_and_validate_config_file
from prowler.lib.logger import logger
from prowler.lib.mutelist.mutelist import parse_mutelist_file
from prowler.lib.utils.utils import print_boxes
from prowler.providers.common.models import Audit_Metadata
from prowler.providers.common.provider import Provider
from prowler.providers.<new_provider_name>.models import (
    # All providers models needed
    ProvierSessionModel,
    ProvierIdentityModel,
    ProvierOutputOptionsModel
)

class NewProvider(Provider):
    # All properties from the class, some of this are properties in the base class
    _type: str = "<provider_name>"
    _session: <ProvierSessionModel>
    _identity: <ProvierIdentityModel>
    _audit_config: dict
    _output_options: ProvierOutputOptionsModel
    _mutelist: dict
    audit_metadata: Audit_Metadata

    def __init__(self, arguments):
        """
        Initializes the NewProvider instance.
        Args:
            arguments (dict): A dictionary containing configuration arguments.
        """
        logger.info("Setting <NewProviderName> provider ...")
        # First get from arguments the necesary from the cloud acount (subscriptions or projects or whatever the provider use for storing services)

        # Set the session with the method enforced by parent class
        self._session = self.setup_session(credentials_file)

        # Set the Identity class normaly the provider class give by Python provider library
        self._identity = <ProvierIdentityModel>()

        # Set the provider configuration
        self._audit_config = load_and_validate_config_file(
            self._type, arguments.config_file
        )

    # All enforced properties by the parent class
    @property
    def identity(self):
        return self._identity

    @property
    def session(self):
        return self._session

    @property
    def type(self):
        return self._type

    @property
    def audit_config(self):
        return self._audit_config

    @property
    def output_options(self):
        return self._output_options

    def setup_session(self, <all_needed_for_auth>):
        """
        Sets up the Provider session.

        Args:
            <all_needed_for_auth> Can include all necessary arguments to setup the session

        Returns:
            Credentials necesary to communicate with the provider.
        """
        pass

    """
    This method is enforced by parent class and is used to print all relevant
    information during the prowler execution as a header of execution.
    Normally the Account ID, User name or stuff like this is displayed in colors using the colorama module (Fore).
    """
    def print_credentials(self):
        pass