feat(redshift): add new check redshift_cluster_non_default_database_name (#5283)

Co-authored-by: Sergio <sergio@prowler.com>
This commit is contained in:
Daniel Barranquero
2024-10-03 17:28:54 +02:00
committed by GitHub
parent 2177704b4b
commit e99c58405c
6 changed files with 189 additions and 0 deletions
@@ -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 <cluster-id> --db-name <new-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": ""
}
@@ -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
@@ -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
@@ -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"}]
@@ -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):