diff --git a/prowler/providers/aws/services/mq/mq_broker_active_deployment_mode/__init__.py b/prowler/providers/aws/services/mq/mq_broker_active_deployment_mode/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/prowler/providers/aws/services/mq/mq_broker_active_deployment_mode/mq_broker_active_deployment_mode.metadata.json b/prowler/providers/aws/services/mq/mq_broker_active_deployment_mode/mq_broker_active_deployment_mode.metadata.json new file mode 100644 index 0000000000..ec2045747b --- /dev/null +++ b/prowler/providers/aws/services/mq/mq_broker_active_deployment_mode/mq_broker_active_deployment_mode.metadata.json @@ -0,0 +1,34 @@ +{ + "Provider": "aws", + "CheckID": "mq_broker_active_deployment_mode", + "CheckTitle": "Apache ActiveMQ brokers should be configured in active/standby mode.", + "CheckType": [ + "Software and Configuration Checks/Industry and Regulatory Standards/NIST 800-53 Controls" + ], + "ServiceName": "mq", + "SubServiceName": "", + "ResourceIdTemplate": "arn:aws:mq:region:account-id:broker:broker-id", + "Severity": "low", + "ResourceType": "AwsAmazonMQBroker", + "Description": "Ensure Amazon MQ Apache ActiveMQ brokers are configured in active/standby mode for high availability and fault tolerance.", + "Risk": "Apache ActiveMQ brokers not configured in active/standby mode lack high availability, increasing the risk of downtime and data loss during failures.", + "RelatedUrl": "https://docs.aws.amazon.com/amazon-mq/latest/developer-guide/amazon-mq-basic-elements.html", + "Remediation": { + "Code": { + "CLI": "aws mq create-broker --broker-name --engine-type ActiveMQ --deployment-mode ACTIVE_STANDBY_MULTI_AZ", + "NativeIaC": "", + "Other": "https://docs.aws.amazon.com/securityhub/latest/userguide/mq-controls.html#mq-5", + "Terraform": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/MQ/deployment-mode.html" + }, + "Recommendation": { + "Text": "Ensure Amazon MQ Apache ActiveMQ brokers use active/standby deployment mode for high availability and fault tolerance.", + "Url": "https://docs.aws.amazon.com/amazon-mq/latest/developer-guide/amazon-mq-broker-architecture.html#active-standby-broker-deployment" + } + }, + "Categories": [ + "redundancy" + ], + "DependsOn": [], + "RelatedTo": [], + "Notes": "" +} diff --git a/prowler/providers/aws/services/mq/mq_broker_active_deployment_mode/mq_broker_active_deployment_mode.py b/prowler/providers/aws/services/mq/mq_broker_active_deployment_mode/mq_broker_active_deployment_mode.py new file mode 100644 index 0000000000..8b4febf014 --- /dev/null +++ b/prowler/providers/aws/services/mq/mq_broker_active_deployment_mode/mq_broker_active_deployment_mode.py @@ -0,0 +1,24 @@ +from prowler.lib.check.models import Check, Check_Report_AWS +from prowler.providers.aws.services.mq.mq_client import mq_client +from prowler.providers.aws.services.mq.mq_service import DeploymentMode, EngineType + + +class mq_broker_active_deployment_mode(Check): + def execute(self): + findings = [] + for broker in mq_client.brokers.values(): + if broker.engine_type == EngineType.ACTIVEMQ: + report = Check_Report_AWS(self.metadata()) + report.region = broker.region + report.resource_id = broker.id + report.resource_arn = broker.arn + report.resource_tags = broker.tags + report.status = "FAIL" + report.status_extended = f"MQ Broker {broker.name} does not have an active/standby deployment mode." + if broker.deployment_mode == DeploymentMode.ACTIVE_STANDBY_MULTI_AZ: + report.status = "PASS" + report.status_extended = f"MQ Broker {broker.name} does have an active/standby deployment mode." + + findings.append(report) + + return findings diff --git a/prowler/providers/aws/services/mq/mq_service.py b/prowler/providers/aws/services/mq/mq_service.py index 63a6e8d39a..a88649bb3c 100644 --- a/prowler/providers/aws/services/mq/mq_service.py +++ b/prowler/providers/aws/services/mq/mq_service.py @@ -30,6 +30,7 @@ def _list_brokers(self, regional_client): id=broker["BrokerId"], region=regional_client.region, ) + except Exception as error: logger.error( f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" @@ -40,15 +41,17 @@ def _describe_broker(self, broker): describe_broker = self.regional_clients[broker.region].describe_broker( BrokerId=broker.id ) - broker.auto_minor_version_upgrade = describe_broker.get( - "AutoMinorVersionUpgrade", False - ) broker.engine_type = EngineType( - describe_broker.get("EngineType", "ACTIVEMQ") + describe_broker.get("EngineType", "ACTIVEMQ").upper() ) broker.deployment_mode = DeploymentMode( - describe_broker.get("DeploymentMode", "SINGLE_INSTANCE") + describe_broker.get("DeploymentMode", "SINGLE_INSTANCE").upper() + ) + broker.auto_minor_version_upgrade = describe_broker.get( + "AutoMinorVersionUpgrade", False ) + broker.tags = [describe_broker.get("Tags", {})] + except Exception as error: logger.error( f"{broker.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" @@ -77,6 +80,8 @@ class Broker(BaseModel): name: str id: str region: str + engine_type: EngineType = EngineType.ACTIVEMQ + deployment_mode: DeploymentMode = DeploymentMode.SINGLE_INSTANCE auto_minor_version_upgrade: bool = False engine_type: EngineType = EngineType.ACTIVEMQ deployment_mode: DeploymentMode = DeploymentMode.SINGLE_INSTANCE diff --git a/tests/providers/aws/services/mq/mq_broker_active_deployment_mode/mq_broker_active_deployment_mode_test.py b/tests/providers/aws/services/mq/mq_broker_active_deployment_mode/mq_broker_active_deployment_mode_test.py new file mode 100644 index 0000000000..adedafa34a --- /dev/null +++ b/tests/providers/aws/services/mq/mq_broker_active_deployment_mode/mq_broker_active_deployment_mode_test.py @@ -0,0 +1,183 @@ +from unittest import mock + +from boto3 import client +from moto import mock_aws + +from tests.providers.aws.utils import ( + AWS_ACCOUNT_NUMBER, + AWS_REGION_US_EAST_1, + set_mocked_aws_provider, +) + + +class Test_mq_activemq_broker_active_standby_mode: + @mock_aws + def test_no_brokers(self): + from prowler.providers.aws.services.mq.mq_service import MQ + + aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1]) + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ): + with mock.patch( + "prowler.providers.aws.services.mq.mq_broker_active_deployment_mode.mq_broker_active_deployment_mode.mq_client", + new=MQ(aws_provider), + ): + # Test Check + from prowler.providers.aws.services.mq.mq_broker_active_deployment_mode.mq_broker_active_deployment_mode import ( + mq_broker_active_deployment_mode, + ) + + check = mq_broker_active_deployment_mode() + result = check.execute() + + assert len(result) == 0 + + @mock_aws + def test_no_activemq_brokers(self): + from prowler.providers.aws.services.mq.mq_service import MQ + + mq_client = client("mq", region_name=AWS_REGION_US_EAST_1) + mq_client.create_broker( + BrokerName="test-broker", + EngineType="RABBITMQ", + EngineVersion="5.15.0", + HostInstanceType="mq.t2.micro", + Users=[ + { + "Username": "admin", + "Password": "admin", + }, + ], + DeploymentMode="ACTIVE_STANDBY_MULTI_AZ", + PubliclyAccessible=False, + AutoMinorVersionUpgrade=True, + )["BrokerId"] + + aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1]) + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ): + with mock.patch( + "prowler.providers.aws.services.mq.mq_broker_active_deployment_mode.mq_broker_active_deployment_mode.mq_client", + new=MQ(aws_provider), + ): + # Test Check + from prowler.providers.aws.services.mq.mq_broker_active_deployment_mode.mq_broker_active_deployment_mode import ( + mq_broker_active_deployment_mode, + ) + + check = mq_broker_active_deployment_mode() + result = check.execute() + + assert len(result) == 0 + + @mock_aws + def test_activemq_broker_active_standby_mode_enabled(self): + mq_client = client("mq", region_name=AWS_REGION_US_EAST_1) + broker_name = "test-broker" + broker_id = mq_client.create_broker( + BrokerName="test-broker", + EngineType="ACTIVEMQ", + EngineVersion="5.15.0", + HostInstanceType="mq.t2.micro", + Users=[ + { + "Username": "admin", + "Password": "admin", + }, + ], + DeploymentMode="ACTIVE_STANDBY_MULTI_AZ", + PubliclyAccessible=False, + AutoMinorVersionUpgrade=True, + )["BrokerId"] + + from prowler.providers.aws.services.mq.mq_service import MQ + + aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1]) + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ): + with mock.patch( + "prowler.providers.aws.services.mq.mq_broker_active_deployment_mode.mq_broker_active_deployment_mode.mq_client", + new=MQ(aws_provider), + ): + # Test Check + from prowler.providers.aws.services.mq.mq_broker_active_deployment_mode.mq_broker_active_deployment_mode import ( + mq_broker_active_deployment_mode, + ) + + check = mq_broker_active_deployment_mode() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == f"MQ Broker {broker_name} does have an active/standby deployment mode." + ) + assert result[0].resource_id == broker_id + assert ( + result[0].resource_arn + == f"arn:{aws_provider.identity.partition}:mq:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:broker:{broker_id}" + ) + assert result[0].region == AWS_REGION_US_EAST_1 + + @mock_aws + def test_activemq_broker_active_standby_mode_disabled(self): + mq_client = client("mq", region_name=AWS_REGION_US_EAST_1) + broker_name = "test-broker" + broker_id = mq_client.create_broker( + BrokerName=broker_name, + EngineType="ACTIVEMQ", + EngineVersion="5.15.0", + HostInstanceType="mq.t2.micro", + Users=[ + { + "Username": "admin", + "Password": "admin", + }, + ], + DeploymentMode="SINGLE_INSTANCE", + PubliclyAccessible=False, + AutoMinorVersionUpgrade=False, + )["BrokerId"] + + from prowler.providers.aws.services.mq.mq_service import MQ + + aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1]) + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ): + with mock.patch( + "prowler.providers.aws.services.mq.mq_broker_active_deployment_mode.mq_broker_active_deployment_mode.mq_client", + new=MQ(aws_provider), + ): + # Test Check + from prowler.providers.aws.services.mq.mq_broker_active_deployment_mode.mq_broker_active_deployment_mode import ( + mq_broker_active_deployment_mode, + ) + + check = mq_broker_active_deployment_mode() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"MQ Broker {broker_name} does not have an active/standby deployment mode." + ) + assert result[0].resource_id == broker_id + assert ( + result[0].resource_arn + == f"arn:{aws_provider.identity.partition}:mq:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:broker:{broker_id}" + ) + assert result[0].region == AWS_REGION_US_EAST_1 diff --git a/tests/providers/aws/services/mq/mq_service_test.py b/tests/providers/aws/services/mq/mq_service_test.py index 33efeea231..6b14ba8e09 100644 --- a/tests/providers/aws/services/mq/mq_service_test.py +++ b/tests/providers/aws/services/mq/mq_service_test.py @@ -74,7 +74,7 @@ def test_describe_broker(self): AutoMinorVersionUpgrade=True, BrokerName="my-broker", DeploymentMode="SINGLE_INSTANCE", - EngineType="ActiveMQ", + EngineType="ACTIVEMQ", EngineVersion="5.15.0", HostInstanceType="mq.t2.micro", PubliclyAccessible=True, @@ -86,6 +86,7 @@ def test_describe_broker(self): "Username": "user", } ], + Tags={"key": "value"}, ) broker_arn = broker["BrokerArn"] broker["BrokerId"] @@ -101,3 +102,5 @@ def test_describe_broker(self): assert mq.brokers[broker_arn].id == broker["BrokerId"] assert mq.brokers[broker_arn].engine_type == EngineType.ACTIVEMQ assert mq.brokers[broker_arn].deployment_mode == DeploymentMode.SINGLE_INSTANCE + assert mq.brokers[broker_arn].auto_minor_version_upgrade + assert mq.brokers[broker_arn].tags == [{"key": "value"}]