diff --git a/prowler/providers/aws/services/elasticbeanstalk/__init__.py b/prowler/providers/aws/services/elasticbeanstalk/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_client.py b/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_client.py new file mode 100644 index 0000000000..bea628d978 --- /dev/null +++ b/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_client.py @@ -0,0 +1,6 @@ +from prowler.providers.aws.services.elasticbeanstalk.elasticbeanstalk_service import ( + ElasticBeanstalk, +) +from prowler.providers.common.provider import Provider + +elasticbeanstalk_client = ElasticBeanstalk(Provider.get_global_provider()) diff --git a/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_service.py b/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_service.py new file mode 100644 index 0000000000..3f8faa0e64 --- /dev/null +++ b/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_service.py @@ -0,0 +1,106 @@ +from typing import Optional + +from pydantic import BaseModel + +from prowler.lib.logger import logger +from prowler.lib.scan_filters.scan_filters import is_resource_filtered +from prowler.providers.aws.lib.service.service import AWSService + + +class ElasticBeanstalk(AWSService): + def __init__(self, provider): + # Call AWSService's __init__ + super().__init__(__class__.__name__, provider) + self.environments = {} + self.__threading_call__(self._describe_environments) + self.__threading_call__( + self._describe_configuration_settings, self.environments.values() + ) + self.__threading_call__( + self._list_tags_for_resource, self.environments.values() + ) + + def _describe_environments(self, regional_client): + logger.info("ElasticBeanstalk - Describing environments...") + try: + describe_environment_paginator = regional_client.get_paginator( + "describe_environments" + ) + for page in describe_environment_paginator.paginate(): + for environment in page["Environments"]: + environment_arn = environment["EnvironmentArn"] + if not self.audit_resources or ( + is_resource_filtered(environment_arn, self.audit_resources) + ): + self.environments[environment_arn] = Environment( + id=environment["EnvironmentId"], + arn=environment_arn, + application_name=environment["ApplicationName"], + name=environment["EnvironmentName"], + region=regional_client.region, + ) + except Exception as error: + logger.error( + f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + + def _describe_configuration_settings(self, environment): + logger.info("ElasticBeanstalk - Describing configuration settings...") + try: + regional_client = self.regional_clients[environment.region] + configuration_settings = regional_client.describe_configuration_settings( + ApplicationName=environment.application_name, + EnvironmentName=environment.name, + ) + option_settings = configuration_settings["ConfigurationSettings"][0].get( + "OptionSettings", {} + ) + for option in option_settings: + if ( + option["Namespace"] == "aws:elasticbeanstalk:healthreporting:system" + and option["OptionName"] == "SystemType" + ): + environment.health_reporting = option.get("Value", "basic") + elif ( + option["Namespace"] == "aws:elasticbeanstalk:managedactions" + and option["OptionName"] == "ManagedActionsEnabled" + ): + environment.managed_platform_updates = option.get("Value", "false") + elif ( + option["Namespace"] == "aws:elasticbeanstalk:cloudwatch:logs" + and option["OptionName"] == "StreamLogs" + ): + environment.cloudwatch_stream_logs = option.get("Value", "false") + except Exception as error: + logger.error( + f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + + def _list_tags_for_resource(self, resource: any): + logger.info("ElasticBeanstalk - List Tags...") + try: + regional_client = self.regional_clients[resource.region] + response = regional_client.list_tags_for_resource(ResourceArn=resource.arn)[ + "ResourceTags" + ] + resource.tags = response + except Exception as error: + logger.error( + f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + except Exception as error: + logger.error( + f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + + +class Environment(BaseModel): + id: str + name: str + arn: str + region: str + application_name: str + health_reporting: Optional[str] + managed_platform_updates: Optional[str] + cloudwatch_stream_logs: Optional[str] + tags: Optional[list] = [] diff --git a/tests/providers/aws/services/elasticbeanstalk/elasticbeanstalk_service_test.py b/tests/providers/aws/services/elasticbeanstalk/elasticbeanstalk_service_test.py new file mode 100644 index 0000000000..e435bfec49 --- /dev/null +++ b/tests/providers/aws/services/elasticbeanstalk/elasticbeanstalk_service_test.py @@ -0,0 +1,165 @@ +from unittest.mock import patch + +import botocore +from boto3 import client +from moto import mock_aws + +from prowler.providers.aws.services.elasticbeanstalk.elasticbeanstalk_service import ( + ElasticBeanstalk, +) +from tests.providers.aws.utils import AWS_REGION_EU_WEST_1, set_mocked_aws_provider + +# Mocking Access Analyzer Calls +make_api_call = botocore.client.BaseClient._make_api_call + + +def mock_make_api_call(self, operation_name, kwarg): + if operation_name == "DescribeConfigurationSettings": + return { + "ConfigurationSettings": [ + { + "OptionSettings": [ + { + "Namespace": "aws:elasticbeanstalk:healthreporting:system", + "OptionName": "SystemType", + "Value": "enhanced", + }, + { + "Namespace": "aws:elasticbeanstalk:managedactions", + "OptionName": "ManagedActionsEnabled", + "Value": "true", + }, + { + "Namespace": "aws:elasticbeanstalk:cloudwatch:logs", + "OptionName": "StreamLogs", + "Value": "true", + }, + ], + } + ] + } + + return make_api_call(self, operation_name, kwarg) + + +# Mock generate_regional_clients() +def mock_generate_regional_clients(provider, service): + regional_client = provider._session.current_session.client( + service, region_name=AWS_REGION_EU_WEST_1 + ) + regional_client.region = AWS_REGION_EU_WEST_1 + return {AWS_REGION_EU_WEST_1: regional_client} + + +@patch( + "prowler.providers.aws.aws_provider.AwsProvider.generate_regional_clients", + new=mock_generate_regional_clients, +) +@patch("botocore.client.BaseClient._make_api_call", new=mock_make_api_call) +class Test_ElasticBeanstalk_Service: + # Test ElasticBeanstalk Client + @mock_aws + def test_get_client(self): + elasticbeanstalk = ElasticBeanstalk(set_mocked_aws_provider()) + assert ( + elasticbeanstalk.regional_clients[AWS_REGION_EU_WEST_1].__class__.__name__ + == "ElasticBeanstalk" + ) + + # Test ElasticBeanstalk Session + @mock_aws + def test__get_session__(self): + elasticbeanstalk = ElasticBeanstalk(set_mocked_aws_provider()) + assert elasticbeanstalk.session.__class__.__name__ == "Session" + + # Test ElasticBeanstalk Service + @mock_aws + def test__get_service__(self): + elasticbeanstalk = ElasticBeanstalk(set_mocked_aws_provider()) + assert elasticbeanstalk.service == "elasticbeanstalk" + + # Test _describe_environments + @mock_aws + def test_describe_environments(self): + # Create ElasticBeanstalk app and env + elasticbeanstalk_client = client( + "elasticbeanstalk", region_name=AWS_REGION_EU_WEST_1 + ) + elasticbeanstalk_client.create_application(ApplicationName="test-app") + environment = elasticbeanstalk_client.create_environment( + ApplicationName="test-app", + EnvironmentName="test-env", + ) + # ElasticBeanstalk Class + elasticbeanstalk = ElasticBeanstalk(set_mocked_aws_provider()) + + assert len(elasticbeanstalk.environments) == 1 + assert ( + elasticbeanstalk.environments[environment["EnvironmentArn"]].id + == environment["EnvironmentId"] + ) + assert ( + elasticbeanstalk.environments[environment["EnvironmentArn"]].name + == "test-env" + ) + assert ( + elasticbeanstalk.environments[environment["EnvironmentArn"]].region + == AWS_REGION_EU_WEST_1 + ) + assert ( + elasticbeanstalk.environments[ + environment["EnvironmentArn"] + ].application_name + == "test-app" + ) + + # Test _describe_configuration_settings + @mock_aws + def test_describe_configuration_settings(self): + # Create ElasticBeanstalk app and env + elasticbeanstalk_client = client( + "elasticbeanstalk", region_name=AWS_REGION_EU_WEST_1 + ) + elasticbeanstalk_client.create_application(ApplicationName="test-app") + environment = elasticbeanstalk_client.create_environment( + ApplicationName="test-app", + EnvironmentName="test-env", + ) + # ElasticBeanstalk Class + elasticbeanstalk = ElasticBeanstalk(set_mocked_aws_provider()) + assert ( + elasticbeanstalk.environments[ + environment["EnvironmentArn"] + ].health_reporting + == "enhanced" + ) + assert ( + elasticbeanstalk.environments[ + environment["EnvironmentArn"] + ].managed_platform_updates + == "true" + ) + assert ( + elasticbeanstalk.environments[ + environment["EnvironmentArn"] + ].cloudwatch_stream_logs + == "true" + ) + + @mock_aws + def test_list_tags_for_resource(self): + # Create ElasticBeanstalk app and env + elasticbeanstalk_client = client( + "elasticbeanstalk", region_name=AWS_REGION_EU_WEST_1 + ) + elasticbeanstalk_client.create_application(ApplicationName="test-app") + environment = elasticbeanstalk_client.create_environment( + ApplicationName="test-app", + EnvironmentName="test-env", + Tags=[{"Key": "test-key", "Value": "test-value"}], + ) + # ElasticBeanstalk Class + elasticbeanstalk = ElasticBeanstalk(set_mocked_aws_provider()) + assert elasticbeanstalk.environments[environment["EnvironmentArn"]].tags == [ + {"Key": "test-key", "Value": "test-value"} + ]