Skip to content

Commit

Permalink
feat(mq): add new check mq_broker_cluster_deployment_mode (#5481)
Browse files Browse the repository at this point in the history
Co-authored-by: Sergio <[email protected]>
  • Loading branch information
HugoPBrito and sergargar authored Oct 24, 2024
1 parent 6a09171 commit df37327
Show file tree
Hide file tree
Showing 6 changed files with 285 additions and 12 deletions.
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"Provider": "aws",
"CheckID": "mq_broker_cluster_deployment_mode",
"CheckTitle": "MQ RabbitMQ Brokers should use cluster deployment 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 that RabbitMQ Brokers use cluster deployment mode.",
"Risk": "Using a single-instance RabbitMQ broker limits fault tolerance and high availability. Without cluster deployment, broker failures could lead to significant downtime and potential data loss.",
"RelatedUrl": "https://docs.aws.amazon.com/amazon-mq/latest/developer-guide/rabbitmq-basic-elements.html",
"Remediation": {
"Code": {
"CLI": "aws mq create-broker --broker-name <your-broker-name> --engine-type RabbitMQ --deployment-mode CLUSTER_MULTI_AZ",
"NativeIaC": "",
"Other": "https://docs.aws.amazon.com/securityhub/latest/userguide/mq-controls.html#mq-6",
"Terraform": ""
},
"Recommendation": {
"Text": "Ensure RabbitMQ brokers are deployed in cluster mode to enhance resilience and prevent data loss during failures.",
"Url": "https://docs.aws.amazon.com/amazon-mq/latest/developer-guide/rabbitmq-broker-architecture.html#rabbitmq-broker-architecture-cluster"
}
},
"Categories": [],
"DependsOn": [],
"RelatedTo": [],
"Notes": ""
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from typing import List

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_cluster_deployment_mode(Check):
"""Ensure MQ RabbitMQ Broker has cluster deployment mode.
This check will fail if the RabbitMQ Broker does not have cluster deployment mode.
"""

def execute(self) -> List[Check_Report_AWS]:
"""Execute the check.
Returns:
List[Check_Report_AWS]: A list of reports for each RabbitMQ Broker that does not have cluster deployment mode.
"""
findings = []
for broker in mq_client.brokers.values():
if broker.engine_type == EngineType.RABBITMQ:
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 RabbitMQ Broker {broker.name} does not have a cluster deployment mode."
if broker.deployment_mode == DeploymentMode.CLUSTER_MULTI_AZ:
report.status = "PASS"
report.status_extended = f"MQ RabbitMQ Broker {broker.name} does have a cluster deployment mode."

findings.append(report)

return findings
31 changes: 27 additions & 4 deletions prowler/providers/aws/services/mq/mq_service.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from typing import Dict, List, Optional
from enum import Enum
from typing import Dict, List

from pydantic import BaseModel
from pydantic import BaseModel, Field

from prowler.lib.logger import logger
from prowler.lib.scan_filters.scan_filters import is_resource_filtered
Expand Down Expand Up @@ -42,13 +43,33 @@ def _describe_broker(self, broker):
broker.auto_minor_version_upgrade = describe_broker.get(
"AutoMinorVersionUpgrade", False
)
broker.tags = [describe_broker.get("Tags", {})]
broker.engine_type = EngineType(
describe_broker.get("EngineType", "ACTIVEMQ")
)
broker.deployment_mode = DeploymentMode(
describe_broker.get("DeploymentMode", "SINGLE_INSTANCE")
)
except Exception as error:
logger.error(
f"{broker.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)


class DeploymentMode(Enum):
"""Possible Deployment Modes for MQ"""

SINGLE_INSTANCE = "SINGLE_INSTANCE"
ACTIVE_STANDBY_MULTI_AZ = "ACTIVE_STANDBY_MULTI_AZ"
CLUSTER_MULTI_AZ = "CLUSTER_MULTI_AZ"


class EngineType(Enum):
"""Possible Engine Types for MQ"""

ACTIVEMQ = "ACTIVEMQ"
RABBITMQ = "RABBITMQ"


class Broker(BaseModel):
"""Broker model for MQ"""

Expand All @@ -57,4 +78,6 @@ class Broker(BaseModel):
id: str
region: str
auto_minor_version_upgrade: bool = False
tags: Optional[List[Dict[str, str]]]
engine_type: EngineType = EngineType.ACTIVEMQ
deployment_mode: DeploymentMode = DeploymentMode.SINGLE_INSTANCE
tags: List[Dict[str, str]] = Field(default_factory=list)
Original file line number Diff line number Diff line change
@@ -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_rabbitmq_broker_cluster_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_cluster_deployment_mode.mq_broker_cluster_deployment_mode.mq_client",
new=MQ(aws_provider),
):
# Test Check
from prowler.providers.aws.services.mq.mq_broker_cluster_deployment_mode.mq_broker_cluster_deployment_mode import (
mq_broker_cluster_deployment_mode,
)

check = mq_broker_cluster_deployment_mode()
result = check.execute()

assert len(result) == 0

@mock_aws
def test_no_rabbitmq_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="ACTIVEMQ",
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_cluster_deployment_mode.mq_broker_cluster_deployment_mode.mq_client",
new=MQ(aws_provider),
):
# Test Check
from prowler.providers.aws.services.mq.mq_broker_cluster_deployment_mode.mq_broker_cluster_deployment_mode import (
mq_broker_cluster_deployment_mode,
)

check = mq_broker_cluster_deployment_mode()
result = check.execute()

assert len(result) == 0

@mock_aws
def test_rabbitmq_broker_cluster_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="RABBITMQ",
EngineVersion="5.15.0",
HostInstanceType="mq.t2.micro",
Users=[
{
"Username": "admin",
"Password": "admin",
},
],
DeploymentMode="CLUSTER_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_cluster_deployment_mode.mq_broker_cluster_deployment_mode.mq_client",
new=MQ(aws_provider),
):
# Test Check
from prowler.providers.aws.services.mq.mq_broker_cluster_deployment_mode.mq_broker_cluster_deployment_mode import (
mq_broker_cluster_deployment_mode,
)

check = mq_broker_cluster_deployment_mode()
result = check.execute()

assert len(result) == 1
assert result[0].status == "PASS"
assert (
result[0].status_extended
== f"MQ RabbitMQ Broker {broker_name} does have a cluster 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_rabbitmq_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="RABBITMQ",
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_cluster_deployment_mode.mq_broker_cluster_deployment_mode.mq_client",
new=MQ(aws_provider),
):
# Test Check
from prowler.providers.aws.services.mq.mq_broker_cluster_deployment_mode.mq_broker_cluster_deployment_mode import (
mq_broker_cluster_deployment_mode,
)

check = mq_broker_cluster_deployment_mode()
result = check.execute()

assert len(result) == 1
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== f"MQ RabbitMQ Broker {broker_name} does not have a cluster 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
15 changes: 7 additions & 8 deletions tests/providers/aws/services/mq/mq_service_test.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from boto3 import client
from moto import mock_aws

from prowler.providers.aws.services.mq.mq_service import MQ
from prowler.providers.aws.services.mq.mq_service import MQ, DeploymentMode, EngineType
from tests.providers.aws.utils import AWS_REGION_EU_WEST_1, set_mocked_aws_provider


Expand Down Expand Up @@ -34,7 +34,7 @@ def test__get_session__(self):
# Test MQ List Brokers
@mock_aws
def test_list_brokers(self):
# Generate moto MQ client
# Generate MQ client
mq_client = client("mq", region_name=AWS_REGION_EU_WEST_1)
broker = mq_client.create_broker(
AutoMinorVersionUpgrade=True,
Expand All @@ -55,7 +55,7 @@ def test_list_brokers(self):
)
broker_arn = broker["BrokerArn"]

# MQ client for this test class
# MQ Client for this test class
aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1])
mq = MQ(aws_provider)

Expand All @@ -68,7 +68,7 @@ def test_list_brokers(self):
# Test MQ Describe Broker
@mock_aws
def test_describe_broker(self):
# Generate moto MQ client
# Generate MQ client
mq_client = client("mq", region_name=AWS_REGION_EU_WEST_1)
broker = mq_client.create_broker(
AutoMinorVersionUpgrade=True,
Expand All @@ -86,12 +86,11 @@ def test_describe_broker(self):
"Username": "user",
}
],
Tags={"key": "value"},
)
broker_arn = broker["BrokerArn"]
broker["BrokerId"]

# MQ client for this test class
# MQ Client for this test class
aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1])
mq = MQ(aws_provider)

Expand All @@ -100,5 +99,5 @@ def test_describe_broker(self):
assert mq.brokers[broker_arn].name == "my-broker"
assert mq.brokers[broker_arn].region == AWS_REGION_EU_WEST_1
assert mq.brokers[broker_arn].id == broker["BrokerId"]
assert mq.brokers[broker_arn].auto_minor_version_upgrade
assert mq.brokers[broker_arn].tags == [{"key": "value"}]
assert mq.brokers[broker_arn].engine_type == EngineType.ACTIVEMQ
assert mq.brokers[broker_arn].deployment_mode == DeploymentMode.SINGLE_INSTANCE

0 comments on commit df37327

Please sign in to comment.