From c5a460b65e7c7065fbca6fe22fab8abaec7d7d01 Mon Sep 17 00:00:00 2001 From: Daniel Barranquero Date: Wed, 13 Nov 2024 13:28:05 +0100 Subject: [PATCH 1/3] feat(neptune): add fixer code for public snapshot check --- .../neptune_cluster_public_snapshot_fixer.py | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 prowler/providers/aws/services/neptune/neptune_cluster_public_snapshot/neptune_cluster_public_snapshot_fixer.py diff --git a/prowler/providers/aws/services/neptune/neptune_cluster_public_snapshot/neptune_cluster_public_snapshot_fixer.py b/prowler/providers/aws/services/neptune/neptune_cluster_public_snapshot/neptune_cluster_public_snapshot_fixer.py new file mode 100644 index 00000000000..d7958512fed --- /dev/null +++ b/prowler/providers/aws/services/neptune/neptune_cluster_public_snapshot/neptune_cluster_public_snapshot_fixer.py @@ -0,0 +1,31 @@ +from prowler.lib.logger import logger +from prowler.providers.aws.services.neptune.neptune_client import neptune_client + + +def fixer(resource_id: str, region: str) -> bool: + """ + Modify the attributes of a Neptune DB cluster snapshot to remove public access. + Specifically, this fixer removes the 'all' value from the 'restore' attribute to + prevent the snapshot from being publicly accessible. + + Args: + resource_id (str): The DB cluster snapshot identifier. + region (str): AWS region where the snapshot exists. + + Returns: + bool: True if the operation is successful (public access is removed), False otherwise. + """ + try: + regional_client = neptune_client.regional_clients[region] + regional_client.modify_db_cluster_snapshot_attribute( + DBClusterSnapshotIdentifier=resource_id, + AttributeName="restore", + ValuesToRemove=["all"], + ) + except Exception as error: + logger.error( + f"{region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + return False + else: + return True From 5600ba3d7f048c8d81152e7b66f31737fecedf85 Mon Sep 17 00:00:00 2001 From: Daniel Barranquero Date: Wed, 13 Nov 2024 13:29:03 +0100 Subject: [PATCH 2/3] feat(neptune): add unit tests --- ...tune_cluster_public_snapshot_fixer_test.py | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 tests/providers/aws/services/neptune/neptune_cluster_public_snapshot/neptune_cluster_public_snapshot_fixer_test.py diff --git a/tests/providers/aws/services/neptune/neptune_cluster_public_snapshot/neptune_cluster_public_snapshot_fixer_test.py b/tests/providers/aws/services/neptune/neptune_cluster_public_snapshot/neptune_cluster_public_snapshot_fixer_test.py new file mode 100644 index 00000000000..137b55c7912 --- /dev/null +++ b/tests/providers/aws/services/neptune/neptune_cluster_public_snapshot/neptune_cluster_public_snapshot_fixer_test.py @@ -0,0 +1,89 @@ +from unittest import mock + +import botocore +import botocore.client +from moto import mock_aws + +from tests.providers.aws.utils import AWS_REGION_EU_WEST_1, set_mocked_aws_provider + +mock_make_api_call = botocore.client.BaseClient._make_api_call + + +def mock_make_api_call_public_snapshot(self, operation_name, kwarg): + if operation_name == "ModifyDBClusterSnapshotAttribute": + return { + "DBClusterSnapshotAttributesResult": { + "DBClusterSnapshotAttributes": [ + { + "AttributeName": "restore", + "DBClusterSnapshotIdentifier": "test-snapshot", + "AttributeValues": [], + } + ] + } + } + return mock_make_api_call(self, operation_name, kwarg) + + +def mock_make_api_call_public_snapshot_error(self, operation_name, kwarg): + if operation_name == "ModifyDBClusterSnapshotAttribute": + raise botocore.exceptions.ClientError( + { + "Error": { + "Code": "DBClusterSnapshotNotFoundFault", + "Message": "DBClusterSnapshotNotFoundFault", + } + }, + operation_name, + ) + return mock_make_api_call(self, operation_name, kwarg) + + +class Test_neptune_cluster_public_snapshot_fixer: + @mock_aws + def test_neptune_cluster_public_snapshot_fixer(self): + with mock.patch( + "botocore.client.BaseClient._make_api_call", + new=mock_make_api_call_public_snapshot, + ): + from prowler.providers.aws.services.neptune.neptune_service import Neptune + + 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, + ), mock.patch( + "prowler.providers.aws.services.neptune.neptune_cluster_public_snapshot.neptune_cluster_public_snapshot_fixer.neptune_client", + new=Neptune(aws_provider), + ): + from prowler.providers.aws.services.neptune.neptune_cluster_public_snapshot.neptune_cluster_public_snapshot_fixer import ( + fixer, + ) + + assert fixer(resource_id="test-snapshot", region=AWS_REGION_EU_WEST_1) + + @mock_aws + def test_neptune_cluster_public_snapshot_fixer_error(self): + with mock.patch( + "botocore.client.BaseClient._make_api_call", + new=mock_make_api_call_public_snapshot_error, + ): + from prowler.providers.aws.services.neptune.neptune_service import Neptune + + 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, + ), mock.patch( + "prowler.providers.aws.services.neptune.neptune_cluster_public_snapshot.neptune_cluster_public_snapshot_fixer.neptune_client", + new=Neptune(aws_provider), + ): + from prowler.providers.aws.services.neptune.neptune_cluster_public_snapshot.neptune_cluster_public_snapshot_fixer import ( + fixer, + ) + + assert not fixer( + resource_id="test-snapshot", region=AWS_REGION_EU_WEST_1 + ) From befe49d83161ecb39386fade5cbe27fbea89a599 Mon Sep 17 00:00:00 2001 From: Daniel Barranquero Date: Fri, 15 Nov 2024 09:06:27 +0100 Subject: [PATCH 3/3] chore(neptune): add permissions to the docstring --- .../neptune_cluster_public_snapshot_fixer.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/prowler/providers/aws/services/neptune/neptune_cluster_public_snapshot/neptune_cluster_public_snapshot_fixer.py b/prowler/providers/aws/services/neptune/neptune_cluster_public_snapshot/neptune_cluster_public_snapshot_fixer.py index d7958512fed..ad2a081cba6 100644 --- a/prowler/providers/aws/services/neptune/neptune_cluster_public_snapshot/neptune_cluster_public_snapshot_fixer.py +++ b/prowler/providers/aws/services/neptune/neptune_cluster_public_snapshot/neptune_cluster_public_snapshot_fixer.py @@ -8,6 +8,18 @@ def fixer(resource_id: str, region: str) -> bool: Specifically, this fixer removes the 'all' value from the 'restore' attribute to prevent the snapshot from being publicly accessible. + Requires the rds:ModifyDBClusterSnapshotAttribute permissions. + { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": "rds:ModifyDBClusterSnapshotAttribute", + "Resource": "*" + } + ] + } + Args: resource_id (str): The DB cluster snapshot identifier. region (str): AWS region where the snapshot exists.