diff --git a/prowler/providers/aws/services/rds/rds_cluster_deletion_protection/__init__.py b/prowler/providers/aws/services/rds/rds_cluster_deletion_protection/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/prowler/providers/aws/services/rds/rds_cluster_deletion_protection/rds_cluster_deletion_protection.metadata.json b/prowler/providers/aws/services/rds/rds_cluster_deletion_protection/rds_cluster_deletion_protection.metadata.json new file mode 100644 index 00000000000..b68722b2b7c --- /dev/null +++ b/prowler/providers/aws/services/rds/rds_cluster_deletion_protection/rds_cluster_deletion_protection.metadata.json @@ -0,0 +1,30 @@ +{ + "Provider": "aws", + "CheckID": "rds_cluster_deletion_protection", + "CheckTitle": "Check if RDS clusters have deletion protection enabled.", + "CheckType": [], + "ServiceName": "rds", + "SubServiceName": "", + "ResourceIdTemplate": "arn:aws:rds:region:account-id:db-cluster", + "Severity": "low", + "ResourceType": "AwsRdsDbCluster", + "Description": "Check if RDS clusters have deletion protection enabled.", + "Risk": "You can only delete clusters that do not have deletion protection enabled.", + "RelatedUrl": "https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_DeleteInstance.html", + "Remediation": { + "Code": { + "CLI": "aws rds modify-db-cluster --db-cluster-identifier --deletion-protection --apply-immediately", + "NativeIaC": "", + "Other": "https://docs.aws.amazon.com/securityhub/latest/userguide/rds-controls.html#rds-7", + "Terraform": "https://docs.prowler.com/checks/aws/general-policies/ensure-that-rds-clusters-and-instances-have-deletion-protection-enabled#terraform" + }, + "Recommendation": { + "Text": "Enable deletion protection using the AWS Management Console for production DB clusters.", + "Url": "https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_DeleteInstance.html" + } + }, + "Categories": [], + "DependsOn": [], + "RelatedTo": [], + "Notes": "" +} diff --git a/prowler/providers/aws/services/rds/rds_cluster_deletion_protection/rds_cluster_deletion_protection.py b/prowler/providers/aws/services/rds/rds_cluster_deletion_protection/rds_cluster_deletion_protection.py new file mode 100644 index 00000000000..646e9da6cb8 --- /dev/null +++ b/prowler/providers/aws/services/rds/rds_cluster_deletion_protection/rds_cluster_deletion_protection.py @@ -0,0 +1,22 @@ +from prowler.lib.check.models import Check, Check_Report_AWS +from prowler.providers.aws.services.rds.rds_client import rds_client + + +class rds_cluster_deletion_protection(Check): + def execute(self): + findings = [] + for db_cluster in rds_client.db_clusters: + report = Check_Report_AWS(self.metadata()) + report.region = rds_client.db_clusters[db_cluster].region + report.resource_id = rds_client.db_clusters[db_cluster].id + report.resource_arn = db_cluster + report.resource_tags = rds_client.db_clusters[db_cluster].tags + report.status = "FAIL" + report.status_extended = f"RDS Cluster {rds_client.db_clusters[db_cluster].id} does not have deletion protection enabled." + if rds_client.db_clusters[db_cluster].deletion_protection: + report.status = "PASS" + report.status_extended = f"RDS Cluster {rds_client.db_clusters[db_cluster].id} has deletion protection enabled." + + findings.append(report) + + return findings diff --git a/tests/providers/aws/services/rds/rds_cluster_deletion_protection/rds_cluster_deletion_protection_test.py b/tests/providers/aws/services/rds/rds_cluster_deletion_protection/rds_cluster_deletion_protection_test.py new file mode 100644 index 00000000000..6fd89439413 --- /dev/null +++ b/tests/providers/aws/services/rds/rds_cluster_deletion_protection/rds_cluster_deletion_protection_test.py @@ -0,0 +1,143 @@ +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_rds_cluster_deletion_protection: + @mock_aws + def test_rds_no_clusters(self): + from prowler.providers.aws.services.rds.rds_service import RDS + + 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.rds.rds_cluster_deletion_protection.rds_cluster_deletion_protection.rds_client", + new=RDS(aws_provider), + ): + # Test Check + from prowler.providers.aws.services.rds.rds_cluster_deletion_protection.rds_cluster_deletion_protection import ( + rds_cluster_deletion_protection, + ) + + check = rds_cluster_deletion_protection() + result = check.execute() + + assert len(result) == 0 + + @mock_aws + def test_rds_clustered_with_deletion_protection(self): + conn = client("rds", region_name=AWS_REGION_US_EAST_1) + conn.create_db_parameter_group( + DBParameterGroupName="test", + DBParameterGroupFamily="default.aurora-postgresql14", + Description="test parameter group", + ) + conn.create_db_cluster( + DBClusterIdentifier="db-cluster-1", + AllocatedStorage=10, + Engine="aurora-postgresql", + DatabaseName="staging-postgres", + DeletionProtection=True, + DBClusterParameterGroupName="test", + MasterUsername="test", + MasterUserPassword="password", + Tags=[], + ) + + from prowler.providers.aws.services.rds.rds_service import RDS + + 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.rds.rds_cluster_deletion_protection.rds_cluster_deletion_protection.rds_client", + new=RDS(aws_provider), + ): + # Test Check + from prowler.providers.aws.services.rds.rds_cluster_deletion_protection.rds_cluster_deletion_protection import ( + rds_cluster_deletion_protection, + ) + + check = rds_cluster_deletion_protection() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == "RDS Cluster db-cluster-1 has deletion protection enabled." + ) + assert result[0].resource_id == "db-cluster-1" + assert result[0].region == AWS_REGION_US_EAST_1 + assert ( + result[0].resource_arn + == f"arn:aws:rds:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:cluster:db-cluster-1" + ) + assert result[0].resource_tags == [] + + @mock_aws + def test_rds_clustered_without_deletion_protection(self): + conn = client("rds", region_name=AWS_REGION_US_EAST_1) + conn.create_db_parameter_group( + DBParameterGroupName="test", + DBParameterGroupFamily="default.mysql8.0", + Description="test parameter group", + ) + conn.create_db_cluster( + DBClusterIdentifier="db-cluster-1", + AllocatedStorage=10, + Engine="aurora-mysql", + DatabaseName="staging-mysql", + DeletionProtection=False, + DBClusterParameterGroupName="test", + MasterUsername="test", + MasterUserPassword="password", + Tags=[], + ) + from prowler.providers.aws.services.rds.rds_service import RDS + + 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.rds.rds_cluster_deletion_protection.rds_cluster_deletion_protection.rds_client", + new=RDS(aws_provider), + ): + # Test Check + from prowler.providers.aws.services.rds.rds_cluster_deletion_protection.rds_cluster_deletion_protection import ( + rds_cluster_deletion_protection, + ) + + check = rds_cluster_deletion_protection() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == "RDS Cluster db-cluster-1 does not have deletion protection enabled." + ) + assert result[0].resource_id == "db-cluster-1" + assert result[0].region == AWS_REGION_US_EAST_1 + assert ( + result[0].resource_arn + == f"arn:aws:rds:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:cluster:db-cluster-1" + ) + assert result[0].resource_tags == []