Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(aws): Add new RDS check for deletion protection enabled on clusters #4738

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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 <db_cluster_id> --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": ""
}
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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 == []
Loading