From e99c58405ce08010fd0f3c85c4acc11c5f16bc78 Mon Sep 17 00:00:00 2001 From: Daniel Barranquero <74871504+danibarranqueroo@users.noreply.github.com> Date: Thu, 3 Oct 2024 17:28:54 +0200 Subject: [PATCH] feat(redshift): add new check `redshift_cluster_non_default_database_name` (#5283) Co-authored-by: Sergio --- .../__init__.py | 0 ...er_non_default_database_name.metadata.json | 32 +++++ ...shift_cluster_non_default_database_name.py | 22 +++ .../aws/services/redshift/redshift_service.py | 2 + ..._cluster_non_default_database_name_test.py | 132 ++++++++++++++++++ .../redshift/redshift_service_test.py | 1 + 6 files changed, 189 insertions(+) create mode 100644 prowler/providers/aws/services/redshift/redshift_cluster_non_default_database_name/__init__.py create mode 100644 prowler/providers/aws/services/redshift/redshift_cluster_non_default_database_name/redshift_cluster_non_default_database_name.metadata.json create mode 100644 prowler/providers/aws/services/redshift/redshift_cluster_non_default_database_name/redshift_cluster_non_default_database_name.py create mode 100644 tests/providers/aws/services/redshift/redshift_cluster_non_default_database_name/redshift_cluster_non_default_database_name_test.py diff --git a/prowler/providers/aws/services/redshift/redshift_cluster_non_default_database_name/__init__.py b/prowler/providers/aws/services/redshift/redshift_cluster_non_default_database_name/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/prowler/providers/aws/services/redshift/redshift_cluster_non_default_database_name/redshift_cluster_non_default_database_name.metadata.json b/prowler/providers/aws/services/redshift/redshift_cluster_non_default_database_name/redshift_cluster_non_default_database_name.metadata.json new file mode 100644 index 0000000000..96952a5be4 --- /dev/null +++ b/prowler/providers/aws/services/redshift/redshift_cluster_non_default_database_name/redshift_cluster_non_default_database_name.metadata.json @@ -0,0 +1,32 @@ +{ + "Provider": "aws", + "CheckID": "redshift_cluster_non_default_database_name", + "CheckTitle": "Check if Redshift clusters are using the default database name.", + "CheckType": [ + "Software and Configuration Checks/AWS Security Best Practices" + ], + "ServiceName": "redshift", + "SubServiceName": "", + "ResourceIdTemplate": "arn:aws:redshift:region:account-id:cluster/cluster-name", + "Severity": "medium", + "ResourceType": "AwsRedshiftCluster", + "Description": "This control checks whether an Amazon Redshift cluster has changed the database name from its default value. The control fails if the database name is set to 'dev'.", + "Risk": "Using the default database name 'dev' increases the risk of unintended access, as it is publicly known and could be used in IAM policy conditions to inadvertently allow access.", + "RelatedUrl": "https://docs.aws.amazon.com/redshift/latest/gsg/getting-started.html", + "Remediation": { + "Code": { + "CLI": "aws redshift create-cluster --cluster-identifier --db-name ", + "NativeIaC": "", + "Other": "https://docs.aws.amazon.com/securityhub/latest/userguide/redshift-controls.html#redshift-9", + "Terraform": "" + }, + "Recommendation": { + "Text": "Create a new Redshift cluster with a unique database name to replace the default 'dev' database name.", + "Url": "https://docs.aws.amazon.com/redshift/latest/gsg/getting-started.html" + } + }, + "Categories": [], + "DependsOn": [], + "RelatedTo": [], + "Notes": "" +} diff --git a/prowler/providers/aws/services/redshift/redshift_cluster_non_default_database_name/redshift_cluster_non_default_database_name.py b/prowler/providers/aws/services/redshift/redshift_cluster_non_default_database_name/redshift_cluster_non_default_database_name.py new file mode 100644 index 0000000000..f236e90990 --- /dev/null +++ b/prowler/providers/aws/services/redshift/redshift_cluster_non_default_database_name/redshift_cluster_non_default_database_name.py @@ -0,0 +1,22 @@ +from prowler.lib.check.models import Check, Check_Report_AWS +from prowler.providers.aws.services.redshift.redshift_client import redshift_client + + +class redshift_cluster_non_default_database_name(Check): + def execute(self): + findings = [] + for cluster in redshift_client.clusters: + report = Check_Report_AWS(self.metadata()) + report.region = cluster.region + report.resource_id = cluster.id + report.resource_arn = cluster.arn + report.resource_tags = cluster.tags + report.status = "PASS" + report.status_extended = f"Redshift Cluster {cluster.id} does not have the default database name." + if cluster.database_name == "dev": + report.status = "FAIL" + report.status_extended = f"Redshift Cluster {cluster.id} has the default database name: {cluster.database_name}." + + findings.append(report) + + return findings diff --git a/prowler/providers/aws/services/redshift/redshift_service.py b/prowler/providers/aws/services/redshift/redshift_service.py index 09593d26d7..e42cab6417 100644 --- a/prowler/providers/aws/services/redshift/redshift_service.py +++ b/prowler/providers/aws/services/redshift/redshift_service.py @@ -40,6 +40,7 @@ class Redshift(AWSService): region=regional_client.region, tags=cluster.get("Tags"), master_username=cluster.get("MasterUsername", ""), + database_name=cluster.get("DBName", ""), ) self.clusters.append(cluster_to_append) except Exception as error: @@ -92,6 +93,7 @@ class Cluster(BaseModel): public_access: bool = False encrypted: bool = False master_username: str = None + database_name: str = None endpoint_address: str = None allow_version_upgrade: bool = False logging_enabled: bool = False diff --git a/tests/providers/aws/services/redshift/redshift_cluster_non_default_database_name/redshift_cluster_non_default_database_name_test.py b/tests/providers/aws/services/redshift/redshift_cluster_non_default_database_name/redshift_cluster_non_default_database_name_test.py new file mode 100644 index 0000000000..cdeb0dd422 --- /dev/null +++ b/tests/providers/aws/services/redshift/redshift_cluster_non_default_database_name/redshift_cluster_non_default_database_name_test.py @@ -0,0 +1,132 @@ +from unittest import mock +from uuid import uuid4 + +from boto3 import client +from moto import mock_aws + +from tests.providers.aws.utils import ( + AWS_ACCOUNT_NUMBER, + AWS_REGION_EU_WEST_1, + set_mocked_aws_provider, +) + +CLUSTER_ID = str(uuid4()) +CLUSTER_ARN = ( + f"arn:aws:redshift:{AWS_REGION_EU_WEST_1}:{AWS_ACCOUNT_NUMBER}:cluster:{CLUSTER_ID}" +) + + +class Test_redshift_cluster_non_default_database_name: + def test_no_clusters(self): + from prowler.providers.aws.services.redshift.redshift_service import Redshift + + aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1]) + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ): + with mock.patch( + "prowler.providers.aws.services.redshift.redshift_cluster_non_default_database_name.redshift_cluster_non_default_database_name.redshift_client", + new=Redshift(aws_provider), + ): + from prowler.providers.aws.services.redshift.redshift_cluster_non_default_database_name.redshift_cluster_non_default_database_name import ( + redshift_cluster_non_default_database_name, + ) + + check = redshift_cluster_non_default_database_name() + result = check.execute() + + assert len(result) == 0 + + @mock_aws + def test_cluster_default_database_name(self): + redshift_client = client("redshift", region_name=AWS_REGION_EU_WEST_1) + redshift_client.create_cluster( + DBName="dev", + ClusterIdentifier=CLUSTER_ID, + ClusterType="single-node", + NodeType="ds2.xlarge", + MasterUsername="awsuser", + MasterUserPassword="password", + PubliclyAccessible=True, + Tags=[ + {"Key": "test", "Value": "test"}, + ], + Port=9439, + Encrypted=False, + ) + from prowler.providers.aws.services.redshift.redshift_service import Redshift + + aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1]) + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ): + with mock.patch( + "prowler.providers.aws.services.redshift.redshift_cluster_non_default_database_name.redshift_cluster_non_default_database_name.redshift_client", + new=Redshift(aws_provider), + ): + from prowler.providers.aws.services.redshift.redshift_cluster_non_default_database_name.redshift_cluster_non_default_database_name import ( + redshift_cluster_non_default_database_name, + ) + + check = redshift_cluster_non_default_database_name() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "FAIL" + assert result[0].status_extended == ( + f"Redshift Cluster {CLUSTER_ID} has the default database name: dev." + ) + assert result[0].resource_id == CLUSTER_ID + assert result[0].resource_arn == CLUSTER_ARN + assert result[0].region == AWS_REGION_EU_WEST_1 + assert result[0].resource_tags == [{"Key": "test", "Value": "test"}] + + @mock_aws + def test_cluster_non_default_database_name(self): + redshift_client = client("redshift", region_name=AWS_REGION_EU_WEST_1) + redshift_client.create_cluster( + DBName="test", + ClusterIdentifier=CLUSTER_ID, + ClusterType="single-node", + NodeType="ds2.xlarge", + MasterUsername="user", + MasterUserPassword="password", + PubliclyAccessible=True, + Tags=[ + {"Key": "test", "Value": "test"}, + ], + Port=9439, + Encrypted=True, + ) + from prowler.providers.aws.services.redshift.redshift_service import Redshift + + aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1]) + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ): + with mock.patch( + "prowler.providers.aws.services.redshift.redshift_cluster_non_default_database_name.redshift_cluster_non_default_database_name.redshift_client", + new=Redshift(aws_provider), + ): + from prowler.providers.aws.services.redshift.redshift_cluster_non_default_database_name.redshift_cluster_non_default_database_name import ( + redshift_cluster_non_default_database_name, + ) + + check = redshift_cluster_non_default_database_name() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "PASS" + assert result[0].status_extended == ( + f"Redshift Cluster {CLUSTER_ID} does not have the default database name." + ) + assert result[0].resource_id == CLUSTER_ID + assert result[0].resource_arn == CLUSTER_ARN + assert result[0].region == AWS_REGION_EU_WEST_1 + assert result[0].resource_tags == [{"Key": "test", "Value": "test"}] diff --git a/tests/providers/aws/services/redshift/redshift_service_test.py b/tests/providers/aws/services/redshift/redshift_service_test.py index 81d4f4432f..1ab387db75 100644 --- a/tests/providers/aws/services/redshift/redshift_service_test.py +++ b/tests/providers/aws/services/redshift/redshift_service_test.py @@ -115,6 +115,7 @@ class Test_Redshift_Service: ] assert redshift.clusters[0].encrypted assert redshift.clusters[0].master_username == "user" + assert redshift.clusters[0].database_name == "test" @mock_aws def test_describe_logging_status(self):