From c047b29140cd7d8406207fd13ac175c85740cd8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20De=20la=20Torre=20Vico?= Date: Fri, 27 Sep 2024 15:34:25 +0200 Subject: [PATCH] feat(rds): add missing tags to RDS checks (#5230) --- ...rds_cluster_critical_event_subscription.py | 4 +++ ...ds_instance_critical_event_subscription.py | 8 +++++ ...nce_event_subscription_parameter_groups.py | 2 ++ ...ance_event_subscription_security_groups.py | 4 +++ .../providers/aws/services/rds/rds_service.py | 14 ++++++++ ...luster_critical_event_subscription_test.py | 4 +++ ...stance_critical_event_subscription_test.py | 8 +++++ ...vent_subscription_parameter_groups_test.py | 3 ++ ...event_subscription_security_groups_test.py | 16 +++++++-- .../aws/services/rds/rds_service_test.py | 35 ++++++++++++++++++- 10 files changed, 95 insertions(+), 3 deletions(-) diff --git a/prowler/providers/aws/services/rds/rds_cluster_critical_event_subscription/rds_cluster_critical_event_subscription.py b/prowler/providers/aws/services/rds/rds_cluster_critical_event_subscription/rds_cluster_critical_event_subscription.py index 66bfd6ec14..ebfdaf5248 100644 --- a/prowler/providers/aws/services/rds/rds_cluster_critical_event_subscription/rds_cluster_critical_event_subscription.py +++ b/prowler/providers/aws/services/rds/rds_cluster_critical_event_subscription/rds_cluster_critical_event_subscription.py @@ -13,6 +13,7 @@ class rds_cluster_critical_event_subscription(Check): report.resource_id = rds_client.audited_account report.resource_arn = rds_client._get_rds_arn_template(db_event.region) report.region = db_event.region + report.resource_tags = db_event.tags if db_event.source_type == "db-cluster" and db_event.enabled: if db_event.event_list == [] or set(db_event.event_list) == { "maintenance", @@ -20,12 +21,14 @@ class rds_cluster_critical_event_subscription(Check): }: report.resource_id = db_event.id report.resource_arn = db_event.arn + report.resource_tags = db_event.tags report.status = "PASS" report.status_extended = "RDS cluster events are subscribed." elif db_event.event_list == ["maintenance"]: report.resource_id = db_event.id report.resource_arn = db_event.arn + report.resource_tags = db_event.tags report.status = "FAIL" report.status_extended = ( "RDS cluster event category of failure is not subscribed." @@ -34,6 +37,7 @@ class rds_cluster_critical_event_subscription(Check): elif db_event.event_list == ["failure"]: report.resource_id = db_event.id report.resource_arn = db_event.arn + report.resource_tags = db_event.tags report.status = "FAIL" report.status_extended = "RDS cluster event category of maintenance is not subscribed." diff --git a/prowler/providers/aws/services/rds/rds_instance_critical_event_subscription/rds_instance_critical_event_subscription.py b/prowler/providers/aws/services/rds/rds_instance_critical_event_subscription/rds_instance_critical_event_subscription.py index 1e9b416599..3d8edbae8b 100644 --- a/prowler/providers/aws/services/rds/rds_instance_critical_event_subscription/rds_instance_critical_event_subscription.py +++ b/prowler/providers/aws/services/rds/rds_instance_critical_event_subscription/rds_instance_critical_event_subscription.py @@ -13,6 +13,7 @@ class rds_instance_critical_event_subscription(Check): report.resource_id = rds_client.audited_account report.resource_arn = rds_client._get_rds_arn_template(db_event.region) report.region = db_event.region + report.resource_tags = db_event.tags if db_event.source_type == "db-instance" and db_event.enabled: if db_event.event_list == [] or set(db_event.event_list) == { "maintenance", @@ -21,21 +22,25 @@ class rds_instance_critical_event_subscription(Check): }: report.resource_id = db_event.id report.resource_arn = db_event.arn + report.resource_tags = db_event.tags report.status = "PASS" report.status_extended = "RDS instance events are subscribed." elif set(db_event.event_list) == {"maintenance"}: report.resource_id = db_event.id report.resource_arn = db_event.arn + report.resource_tags = db_event.tags report.status = "FAIL" report.status_extended = "RDS instance event categories of configuration change and failure are not subscribed." elif set(db_event.event_list) == {"configuration change"}: report.resource_id = db_event.id report.resource_arn = db_event.arn + report.resource_tags = db_event.tags report.status = "FAIL" report.status_extended = "RDS instance event categories of maintenance and failure are not subscribed." elif set(db_event.event_list) == {"failure"}: report.resource_id = db_event.id report.resource_arn = db_event.arn + report.resource_tags = db_event.tags report.status = "FAIL" report.status_extended = "RDS instance event categories of maintenance and configuration change are not subscribed." elif set(db_event.event_list) == { @@ -44,6 +49,7 @@ class rds_instance_critical_event_subscription(Check): }: report.resource_id = db_event.id report.resource_arn = db_event.arn + report.resource_tags = db_event.tags report.status = "FAIL" report.status_extended = ( "RDS instance event category of failure is not subscribed." @@ -54,6 +60,7 @@ class rds_instance_critical_event_subscription(Check): }: report.resource_id = db_event.id report.resource_arn = db_event.arn + report.resource_tags = db_event.tags report.status = "FAIL" report.status_extended = "RDS instance event category of configuration change is not subscribed." elif set(db_event.event_list) == { @@ -62,6 +69,7 @@ class rds_instance_critical_event_subscription(Check): }: report.resource_id = db_event.id report.resource_arn = db_event.arn + report.resource_tags = db_event.tags report.status = "FAIL" report.status_extended = "RDS instance event category of maintenance is not subscribed." findings.append(report) diff --git a/prowler/providers/aws/services/rds/rds_instance_event_subscription_parameter_groups/rds_instance_event_subscription_parameter_groups.py b/prowler/providers/aws/services/rds/rds_instance_event_subscription_parameter_groups/rds_instance_event_subscription_parameter_groups.py index 0e20c0674f..5628db91c2 100644 --- a/prowler/providers/aws/services/rds/rds_instance_event_subscription_parameter_groups/rds_instance_event_subscription_parameter_groups.py +++ b/prowler/providers/aws/services/rds/rds_instance_event_subscription_parameter_groups/rds_instance_event_subscription_parameter_groups.py @@ -13,12 +13,14 @@ class rds_instance_event_subscription_parameter_groups(Check): report.resource_id = rds_client.audited_account report.resource_arn = rds_client._get_rds_arn_template(db_event.region) report.region = db_event.region + report.resource_tags = [] if db_event.source_type == "db-parameter-group" and db_event.enabled: if db_event.event_list == [] or db_event.event_list == [ "configuration change", ]: report.resource_id = db_event.id report.resource_arn = db_event.arn + report.resource_tags = db_event.tags report.status = "PASS" report.status_extended = ( "RDS parameter group events are subscribed." diff --git a/prowler/providers/aws/services/rds/rds_instance_event_subscription_security_groups/rds_instance_event_subscription_security_groups.py b/prowler/providers/aws/services/rds/rds_instance_event_subscription_security_groups/rds_instance_event_subscription_security_groups.py index 0cee7f58d1..a6d3eebe56 100644 --- a/prowler/providers/aws/services/rds/rds_instance_event_subscription_security_groups/rds_instance_event_subscription_security_groups.py +++ b/prowler/providers/aws/services/rds/rds_instance_event_subscription_security_groups/rds_instance_event_subscription_security_groups.py @@ -13,6 +13,7 @@ class rds_instance_event_subscription_security_groups(Check): report.resource_id = rds_client.audited_account report.resource_arn = rds_client._get_rds_arn_template(db_event.region) report.region = db_event.region + report.resource_tags = [] if db_event.source_type == "db-security-group" and db_event.enabled: if db_event.event_list == [] or set(db_event.event_list) == { "failure", @@ -20,6 +21,7 @@ class rds_instance_event_subscription_security_groups(Check): }: report.resource_id = db_event.id report.resource_arn = db_event.arn + report.resource_tags = db_event.tags report.status = "PASS" report.status_extended = ( "RDS security group events are subscribed." @@ -28,12 +30,14 @@ class rds_instance_event_subscription_security_groups(Check): elif db_event.event_list == ["configuration change"]: report.resource_id = db_event.id report.resource_arn = db_event.arn + report.resource_tags = db_event.tags report.status = "FAIL" report.status_extended = "RDS security group event category of failure is not subscribed." elif db_event.event_list == ["failure"]: report.resource_id = db_event.id report.resource_arn = db_event.arn + report.resource_tags = db_event.tags report.status = "FAIL" report.status_extended = "RDS security group event category of configuration change is not subscribed." diff --git a/prowler/providers/aws/services/rds/rds_service.py b/prowler/providers/aws/services/rds/rds_service.py index c3c385a566..aa58ff7cc4 100644 --- a/prowler/providers/aws/services/rds/rds_service.py +++ b/prowler/providers/aws/services/rds/rds_service.py @@ -32,6 +32,7 @@ class RDS(AWSService): self.__threading_call__(self._describe_db_cluster_snapshot_attributes) self.__threading_call__(self._describe_db_engine_versions) self.__threading_call__(self._describe_db_event_subscriptions) + self.__threading_call__(self._list_tags, self.db_event_subscriptions) def _get_rds_arn_template(self, region): return ( @@ -463,6 +464,7 @@ class RDS(AWSService): event_list=[], enabled=False, region=regional_client.region, + tags=[], ) ) except Exception as error: @@ -470,6 +472,17 @@ class RDS(AWSService): f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" ) + def _list_tags(self, resource: any): + try: + resource.tags = self.regional_clients[ + resource.region + ].list_tags_for_resource(ResourceName=resource.arn)["TagList"] + + except Exception as error: + logger.error( + f"{resource.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + class Certificate(BaseModel): id: str @@ -578,3 +591,4 @@ class EventSubscription(BaseModel): event_list: list enabled: bool region: str + tags: Optional[list] diff --git a/tests/providers/aws/services/rds/rds_cluster_critical_event_subscription/rds_cluster_critical_event_subscription_test.py b/tests/providers/aws/services/rds/rds_cluster_critical_event_subscription/rds_cluster_critical_event_subscription_test.py index 7fdf7e7e08..cd0410212f 100644 --- a/tests/providers/aws/services/rds/rds_cluster_critical_event_subscription/rds_cluster_critical_event_subscription_test.py +++ b/tests/providers/aws/services/rds/rds_cluster_critical_event_subscription/rds_cluster_critical_event_subscription_test.py @@ -44,6 +44,7 @@ class Test_rds_cluster_critical_event_subscription: assert result[0].region == AWS_REGION_US_EAST_1 assert result[0].resource_id == AWS_ACCOUNT_NUMBER assert result[0].resource_arn == RDS_ACCOUNT_ARN + assert result[0].resource_tags == [] @mock_aws def test_rds_cluster_event_subscription_enabled(self): @@ -93,6 +94,7 @@ class Test_rds_cluster_critical_event_subscription: result[0].resource_arn == f"arn:aws:rds:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:es:TestSub" ) + assert result[0].resource_tags == [{"Key": "test", "Value": "testing"}] @mock_aws def test_rds_cluster_event_failure_only_subscription(self): @@ -145,6 +147,7 @@ class Test_rds_cluster_critical_event_subscription: result[0].resource_arn == f"arn:aws:rds:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:es:TestSub" ) + assert result[0].resource_tags == [{"Key": "test", "Value": "testing"}] @mock_aws def test_rds_cluster_event_maintenance_only_subscription(self): @@ -194,3 +197,4 @@ class Test_rds_cluster_critical_event_subscription: result[0].resource_arn == f"arn:aws:rds:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:es:TestSub" ) + assert result[0].resource_tags == [] diff --git a/tests/providers/aws/services/rds/rds_instance_critical_event_subscription/rds_instance_critical_event_subscription_test.py b/tests/providers/aws/services/rds/rds_instance_critical_event_subscription/rds_instance_critical_event_subscription_test.py index 290c3b9dfb..d72df2303a 100644 --- a/tests/providers/aws/services/rds/rds_instance_critical_event_subscription/rds_instance_critical_event_subscription_test.py +++ b/tests/providers/aws/services/rds/rds_instance_critical_event_subscription/rds_instance_critical_event_subscription_test.py @@ -46,6 +46,7 @@ class Test_rds_instance_critical_event_subscription: assert result[0].region == AWS_REGION_US_EAST_1 assert result[0].resource_id == AWS_ACCOUNT_NUMBER assert result[0].resource_arn == RDS_ACCOUNT_ARN + assert result[0].resource_tags == [] @mock_aws def test_rds_no_events_ignoring(self): @@ -130,6 +131,7 @@ class Test_rds_instance_critical_event_subscription: result[0].resource_arn == f"arn:aws:rds:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:es:TestSub" ) + assert result[0].resource_tags == [{"Key": "test", "Value": "testing"}] @mock_aws def test_rds_instance_event_failure_only_subscription(self): @@ -190,6 +192,7 @@ class Test_rds_instance_critical_event_subscription: result[0].resource_arn == f"arn:aws:rds:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:es:TestSub" ) + assert result[0].resource_tags == [{"Key": "test", "Value": "testing"}] @mock_aws def test_rds_instance_event_maintenance_only_subscription(self): @@ -247,6 +250,7 @@ class Test_rds_instance_critical_event_subscription: result[0].resource_arn == f"arn:aws:rds:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:es:TestSub" ) + assert result[0].resource_tags == [] @mock_aws def test_rds_instance_event_configuration_change_only_subscription(self): @@ -304,6 +308,7 @@ class Test_rds_instance_critical_event_subscription: result[0].resource_arn == f"arn:aws:rds:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:es:TestSub" ) + assert result[0].resource_tags == [] @mock_aws def test_rds_instance_event_configuration_change_and_maintenance(self): @@ -361,6 +366,7 @@ class Test_rds_instance_critical_event_subscription: result[0].resource_arn == f"arn:aws:rds:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:es:TestSub" ) + assert result[0].resource_tags == [] @mock_aws def test_rds_instance_event_configuration_change_and_failure(self): @@ -418,6 +424,7 @@ class Test_rds_instance_critical_event_subscription: result[0].resource_arn == f"arn:aws:rds:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:es:TestSub" ) + assert result[0].resource_tags == [] @mock_aws def test_rds_instance_event_failure_and_maintenance(self): @@ -475,3 +482,4 @@ class Test_rds_instance_critical_event_subscription: result[0].resource_arn == f"arn:aws:rds:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:es:TestSub" ) + assert result[0].resource_tags == [] diff --git a/tests/providers/aws/services/rds/rds_instance_event_subscription_parameter_groups/rds_instance_event_subscription_parameter_groups_test.py b/tests/providers/aws/services/rds/rds_instance_event_subscription_parameter_groups/rds_instance_event_subscription_parameter_groups_test.py index de5b755b71..81c2ead211 100644 --- a/tests/providers/aws/services/rds/rds_instance_event_subscription_parameter_groups/rds_instance_event_subscription_parameter_groups_test.py +++ b/tests/providers/aws/services/rds/rds_instance_event_subscription_parameter_groups/rds_instance_event_subscription_parameter_groups_test.py @@ -46,6 +46,7 @@ class Test_rds_instance__no_event_subscriptions: assert result[0].region == AWS_REGION_US_EAST_1 assert result[0].resource_id == AWS_ACCOUNT_NUMBER assert result[0].resource_arn == RDS_ACCOUNT_ARN + assert result[0].resource_tags == [] @mock_aws def test_rds_no_events_ignoring(self): @@ -130,6 +131,7 @@ class Test_rds_instance__no_event_subscriptions: result[0].resource_arn == f"arn:aws:rds:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:es:TestSub" ) + assert result[0].resource_tags == [{"Key": "test", "Value": "testing"}] @mock_aws def test_rds_parameter_event_configuration_change_only_subscription(self): @@ -187,3 +189,4 @@ class Test_rds_instance__no_event_subscriptions: result[0].resource_arn == f"arn:aws:rds:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:es:TestSub" ) + assert result[0].resource_tags == [] diff --git a/tests/providers/aws/services/rds/rds_instance_event_subscription_security_groups/rds_instance_event_subscription_security_groups_test.py b/tests/providers/aws/services/rds/rds_instance_event_subscription_security_groups/rds_instance_event_subscription_security_groups_test.py index f907a324bd..fdefbc38d0 100644 --- a/tests/providers/aws/services/rds/rds_instance_event_subscription_security_groups/rds_instance_event_subscription_security_groups_test.py +++ b/tests/providers/aws/services/rds/rds_instance_event_subscription_security_groups/rds_instance_event_subscription_security_groups_test.py @@ -14,7 +14,7 @@ make_api_call = botocore.client.BaseClient._make_api_call RDS_ACCOUNT_ARN = f"arn:aws:rds:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:account" -class Test_rds_instance__no_event_subscriptions: +class Test_rds_instance_no_event_subscriptions: @mock_aws def test_rds_no_events(self): from prowler.providers.aws.services.rds.rds_service import RDS @@ -46,6 +46,7 @@ class Test_rds_instance__no_event_subscriptions: assert result[0].region == AWS_REGION_US_EAST_1 assert result[0].resource_id == AWS_ACCOUNT_NUMBER assert result[0].resource_arn == RDS_ACCOUNT_ARN + assert result[0].resource_tags == [] @mock_aws def test_rds_no_events_ignoring(self): @@ -130,6 +131,7 @@ class Test_rds_instance__no_event_subscriptions: result[0].resource_arn == f"arn:aws:rds:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:es:TestSub" ) + assert result[0].resource_tags == [{"Key": "test", "Value": "testing"}] @mock_aws def test_rds_security_event_failure_only_subscription(self): @@ -190,7 +192,7 @@ class Test_rds_instance__no_event_subscriptions: result[0].resource_arn == f"arn:aws:rds:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:es:TestSub" ) - assert result[0].resource_tags == [] + assert result[0].resource_tags == [{"Key": "test", "Value": "testing"}] @mock_aws def test_rds_security_event_configuration_change_only_subscription(self): @@ -215,6 +217,9 @@ class Test_rds_instance__no_event_subscriptions: SourceType="db-security-group", EventCategories=["configuration change"], Enabled=True, + Tags=[ + {"Key": "test", "Value": "testing"}, + ], ) from prowler.providers.aws.services.rds.rds_service import RDS @@ -248,6 +253,7 @@ class Test_rds_instance__no_event_subscriptions: result[0].resource_arn == f"arn:aws:rds:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:es:TestSub" ) + assert result[0].resource_tags == [{"Key": "test", "Value": "testing"}] @mock_aws def test_rds_no_security_group_event_subscription(self): @@ -272,6 +278,9 @@ class Test_rds_instance__no_event_subscriptions: SourceType="db-instance", EventCategories=["configuration change"], Enabled=True, + Tags=[ + {"Key": "test", "Value": "testing"}, + ], ) from prowler.providers.aws.services.rds.rds_service import RDS @@ -302,6 +311,7 @@ class Test_rds_instance__no_event_subscriptions: assert result[0].region == AWS_REGION_US_EAST_1 assert result[0].resource_id == AWS_ACCOUNT_NUMBER assert result[0].resource_arn == RDS_ACCOUNT_ARN + assert result[0].resource_tags == [] @mock_aws def test_rds_no_event_subscription(self): @@ -349,6 +359,7 @@ class Test_rds_instance__no_event_subscriptions: assert result[0].region == AWS_REGION_US_EAST_1 assert result[0].resource_id == AWS_ACCOUNT_NUMBER assert result[0].resource_arn == RDS_ACCOUNT_ARN + assert result[0].resource_tags == [] @mock_aws def test_rds_security_event_subscription_both_enabled(self): @@ -409,3 +420,4 @@ class Test_rds_instance__no_event_subscriptions: result[0].resource_arn == f"arn:aws:rds:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:es:TestSub" ) + assert result[0].resource_tags == [{"Key": "test", "Value": "testing"}] diff --git a/tests/providers/aws/services/rds/rds_service_test.py b/tests/providers/aws/services/rds/rds_service_test.py index 11043a61e6..4145f5e2c4 100644 --- a/tests/providers/aws/services/rds/rds_service_test.py +++ b/tests/providers/aws/services/rds/rds_service_test.py @@ -33,7 +33,6 @@ def mock_make_api_call(self, operation_name, kwarg): @patch("botocore.client.BaseClient._make_api_call", new=mock_make_api_call) class Test_RDS_Service: - # Test Dynamo Service @mock_aws def test_service(self): @@ -382,3 +381,37 @@ class Test_RDS_Service: rds.db_engines[AWS_REGION_US_EAST_1]["mysql"].engine_description == "description" ) + + @mock_aws + def test_list_tags(self): + # RDS client for this test class + conn = client("rds", region_name=AWS_REGION_US_EAST_1) + conn.create_db_instance( + DBInstanceIdentifier="db-primary-1", + AllocatedStorage=10, + Engine="postgres", + DBName="staging-postgres", + DBInstanceClass="db.m1.small", + ) + event_sub = conn.create_event_subscription( + SubscriptionName="TestSub", + SnsTopicArn=f"arn:aws:sns:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:test", + SourceType="db-security-group", + Enabled=True, + Tags=[ + {"Key": "test", "Value": "testing"}, + ], + ) + # Tag event subscription + conn.add_tags_to_resource( + ResourceName=event_sub["EventSubscription"]["EventSubscriptionArn"], + Tags=[ + {"Key": "test", "Value": "testing"}, + ], + ) + aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1]) + rds = RDS(aws_provider) + assert len(rds.db_event_subscriptions) == 1 + assert rds.db_event_subscriptions[0].tags == [ + {"Key": "test", "Value": "testing"}, + ]