From ca554ad3ffee245c1ab87852c83a9987a8ea2f05 Mon Sep 17 00:00:00 2001 From: madereddy <49539048+madereddy@users.noreply.github.com> Date: Mon, 27 May 2024 09:39:23 -0400 Subject: [PATCH] feat(AWS): New Storage Gateway FileShare KMS CMK Check (#4082) --- .../aws/services/storagegateway/__init__.py | 0 .../storagegateway/storagegateway_client.py | 6 + .../__init__.py | 0 ...fileshare_encryption_enabled.metadata.json | 32 +++ ...agegateway_fileshare_encryption_enabled.py | 28 +++ .../storagegateway/storagegateway_service.py | 96 +++++++++ .../aws/services/storagegateway/__init__.py | 0 ...teway_fileshare_encryption_enabled_test.py | 195 ++++++++++++++++++ .../storagegateway_service_test.py | 127 ++++++++++++ 9 files changed, 484 insertions(+) create mode 100644 prowler/providers/aws/services/storagegateway/__init__.py create mode 100644 prowler/providers/aws/services/storagegateway/storagegateway_client.py create mode 100644 prowler/providers/aws/services/storagegateway/storagegateway_fileshare_encryption_enabled/__init__.py create mode 100644 prowler/providers/aws/services/storagegateway/storagegateway_fileshare_encryption_enabled/storagegateway_fileshare_encryption_enabled.metadata.json create mode 100644 prowler/providers/aws/services/storagegateway/storagegateway_fileshare_encryption_enabled/storagegateway_fileshare_encryption_enabled.py create mode 100644 prowler/providers/aws/services/storagegateway/storagegateway_service.py create mode 100644 tests/providers/aws/services/storagegateway/__init__.py create mode 100644 tests/providers/aws/services/storagegateway/storagegateway_fileshare_encryption_enabled/storagegateway_fileshare_encryption_enabled_test.py create mode 100644 tests/providers/aws/services/storagegateway/storagegateway_service_test.py diff --git a/prowler/providers/aws/services/storagegateway/__init__.py b/prowler/providers/aws/services/storagegateway/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/prowler/providers/aws/services/storagegateway/storagegateway_client.py b/prowler/providers/aws/services/storagegateway/storagegateway_client.py new file mode 100644 index 0000000000..52fd7e6416 --- /dev/null +++ b/prowler/providers/aws/services/storagegateway/storagegateway_client.py @@ -0,0 +1,6 @@ +from prowler.providers.aws.services.storagegateway.storagegateway_service import ( + StorageGateway, +) +from prowler.providers.common.provider import Provider + +storagegateway_client = StorageGateway(Provider.get_global_provider()) diff --git a/prowler/providers/aws/services/storagegateway/storagegateway_fileshare_encryption_enabled/__init__.py b/prowler/providers/aws/services/storagegateway/storagegateway_fileshare_encryption_enabled/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/prowler/providers/aws/services/storagegateway/storagegateway_fileshare_encryption_enabled/storagegateway_fileshare_encryption_enabled.metadata.json b/prowler/providers/aws/services/storagegateway/storagegateway_fileshare_encryption_enabled/storagegateway_fileshare_encryption_enabled.metadata.json new file mode 100644 index 0000000000..3779f480c2 --- /dev/null +++ b/prowler/providers/aws/services/storagegateway/storagegateway_fileshare_encryption_enabled/storagegateway_fileshare_encryption_enabled.metadata.json @@ -0,0 +1,32 @@ +{ + "Provider": "aws", + "CheckID": "storagegateway_fileshare_encryption_enabled", + "CheckTitle": "Check if AWS StorageGateway File Shares are encrypted with KMS CMK.", + "CheckType": [ + "Security" + ], + "ServiceName": "storagegateway", + "SubServiceName": "filegateway", + "ResourceIdTemplate": "arn:aws:storagegateway:region:account-id:share", + "Severity": "low", + "ResourceType": "AwsStorageGatewayFileShares", + "Description": "Ensure that Amazon Storage Gateway service is using AWS KMS Customer Master Keys (CMKs) instead of AWS managed-keys (i.e. default keys) for file share data encryption, in order to have a fine-grained control over data-at-rest encryption/decryption process and meet compliance requirements. An AWS Storage Gateway file share is a file system mount point backed by Amazon S3 cloud storage.", + "Risk": "This could provide an avenue for unauthorized access to your data by not having fine-grained control over data-at-rest encryption/decryption process and meet compliance requirements.", + "RelatedUrl": "https://docs.aws.amazon.com/filegateway/latest/files3/encrypt-objects-stored-by-file-gateway-in-amazon-s3.html", + "Remediation": { + "Code": { + "CLI": "aws storagegateway update-nfs-file-share --region us-east-1 --file-share-arn arn:aws:storagegateway:us-east-1:123456789012:share/share-abcd1234 --kms-encrypted --kms-key arn:aws:kms:us-east-1:123456789012:key/abcdabcd-1234-1234-1234-abcdabcdabcd", + "NativeIaC": "", + "Other": "https://www.trendmicro.com/cloudoneconformity-staging/knowledge-base/aws/StorageGateway/file-shares-encrypted-with-cmk.html#", + "Terraform": "" + }, + "Recommendation": { + "Text": "Ensure that Amazon Storage Gateway service is using AWS KMS Customer Master Keys (CMKs).", + "Url": "https://docs.aws.amazon.com/filegateway/latest/files3/encrypt-objects-stored-by-file-gateway-in-amazon-s3.html" + } + }, + "Categories": [], + "DependsOn": [], + "RelatedTo": [], + "Notes": "" +} diff --git a/prowler/providers/aws/services/storagegateway/storagegateway_fileshare_encryption_enabled/storagegateway_fileshare_encryption_enabled.py b/prowler/providers/aws/services/storagegateway/storagegateway_fileshare_encryption_enabled/storagegateway_fileshare_encryption_enabled.py new file mode 100644 index 0000000000..9018c6ade5 --- /dev/null +++ b/prowler/providers/aws/services/storagegateway/storagegateway_fileshare_encryption_enabled/storagegateway_fileshare_encryption_enabled.py @@ -0,0 +1,28 @@ +from prowler.lib.check.models import Check, Check_Report_AWS +from prowler.providers.aws.services.storagegateway.storagegateway_client import ( + storagegateway_client, +) + + +class storagegateway_fileshare_encryption_enabled(Check): + def execute(self): + findings = [] + for fileshare in storagegateway_client.fileshares: + report = Check_Report_AWS(self.metadata()) + report.region = fileshare.region + report.resource_id = fileshare.id + report.resource_arn = fileshare.arn + report.resource_tags = fileshare.tags + report.status = "FAIL" + report.status_extended = ( + f"StorageGateway File Share {fileshare.id} is not using KMS CMK." + ) + if fileshare.kms: + report.status = "PASS" + report.status_extended = ( + f"StorageGateway File Share {fileshare.id} is using KMS CMK." + ) + + findings.append(report) + + return findings diff --git a/prowler/providers/aws/services/storagegateway/storagegateway_service.py b/prowler/providers/aws/services/storagegateway/storagegateway_service.py new file mode 100644 index 0000000000..135ae58891 --- /dev/null +++ b/prowler/providers/aws/services/storagegateway/storagegateway_service.py @@ -0,0 +1,96 @@ +from typing import Optional + +from pydantic import BaseModel + +from prowler.lib.logger import logger +from prowler.lib.scan_filters.scan_filters import is_resource_filtered +from prowler.providers.aws.lib.service.service import AWSService + + +################################ StorageGateway +class StorageGateway(AWSService): + def __init__(self, provider): + # Call AWSService's __init__ + super().__init__(__class__.__name__, provider) + self.fileshares = [] + self.__threading_call__(self.__list_file_shares__) + self.__threading_call__(self.__describe_nfs_file_shares__) + self.__threading_call__(self.__describe_smb_file_shares__) + + def __list_file_shares__(self, regional_client): + try: + list_file_share_paginator = regional_client.get_paginator( + "list_file_shares" + ) + for page in list_file_share_paginator.paginate(): + for fileshare in page["FileShareInfoList"]: + if not self.audit_resources or ( + is_resource_filtered( + fileshare["FileShareARN"], self.audit_resources + ) + ): + self.fileshares.append( + FileShare( + id=fileshare["FileShareId"], + arn=fileshare["FileShareARN"], + gateway_arn=fileshare["GatewayARN"], + region=regional_client.region, + fs_type=fileshare["FileShareType"], + status=fileshare["FileShareStatus"], + ) + ) + + except Exception as error: + logger.error( + f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + + def __describe_nfs_file_shares__(self, regional_client): + logger.info("StorageGateway - Describe NFS FileShares...") + try: + for fileshare in self.fileshares: + if fileshare.fs_type == "NFS": + response = regional_client.describe_nfs_file_shares( + FileShareARNList=[fileshare.arn] + ) + fileshare.tags = response["NFSFileShareInfoList"][0].get("Tags", []) + fileshare.kms = response["NFSFileShareInfoList"][0]["KMSEncrypted"] + fileshare.kms_key = response["NFSFileShareInfoList"][0].get( + "KMSKey", "" + ) + + except Exception as error: + logger.error( + f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + + def __describe_smb_file_shares__(self, regional_client): + logger.info("StorageGateway - Describe SMB FileShares...") + try: + for fileshare in self.fileshares: + if fileshare.fs_type == "SMB": + response = regional_client.describe_smb_file_shares( + FileShareARNList=[fileshare.arn] + ) + fileshare.tags = response["SMBFileShareInfoList"][0].get("Tags", []) + fileshare.kms = response["SMBFileShareInfoList"][0]["KMSEncrypted"] + fileshare.kms_key = response["SMBFileShareInfoList"][0].get( + "KMSKey", "" + ) + + except Exception as error: + logger.error( + f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + + +class FileShare(BaseModel): + id: str + arn: str + gateway_arn: str + region: str + fs_type: str + status: str + kms: Optional[bool] + kms_key: Optional[str] + tags: Optional[list] = [] diff --git a/tests/providers/aws/services/storagegateway/__init__.py b/tests/providers/aws/services/storagegateway/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/providers/aws/services/storagegateway/storagegateway_fileshare_encryption_enabled/storagegateway_fileshare_encryption_enabled_test.py b/tests/providers/aws/services/storagegateway/storagegateway_fileshare_encryption_enabled/storagegateway_fileshare_encryption_enabled_test.py new file mode 100644 index 0000000000..3e90c85386 --- /dev/null +++ b/tests/providers/aws/services/storagegateway/storagegateway_fileshare_encryption_enabled/storagegateway_fileshare_encryption_enabled_test.py @@ -0,0 +1,195 @@ +from unittest import mock + +from prowler.providers.aws.services.storagegateway.storagegateway_service import ( + FileShare, +) +from tests.providers.aws.utils import AWS_ACCOUNT_NUMBER, AWS_REGION_US_EAST_1 + +test_gateway = "sgw-12A3456B" +test_gateway_arn = f"arn:aws:storagegateway:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:gateway/{test_gateway}" +test_kms_key = f"arn:aws:kms:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:key/b72aaa2a-2222-99tt-12345690qwe" +test_share_nfs = "share-nfs2wwe" +test_share_arn_nfs = f"arn:aws:storagegateway:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:share/{test_share_nfs}" +test_share_smb = "share-smb2wwe" +test_share_arn_smb = f"arn:aws:storagegateway:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:share/{test_share_smb}" + + +class Test_storagegateway_fileshare_encryption_enabled: + def test_no_storagegateway_fileshare(self): + storagegateway_client = mock.MagicMock + storagegateway_client.fileshares = [] + with mock.patch( + "prowler.providers.aws.services.storagegateway.storagegateway_service.StorageGateway", + storagegateway_client, + ): + from prowler.providers.aws.services.storagegateway.storagegateway_fileshare_encryption_enabled.storagegateway_fileshare_encryption_enabled import ( + storagegateway_fileshare_encryption_enabled, + ) + + check = storagegateway_fileshare_encryption_enabled() + result = check.execute() + assert len(result) == 0 + + def test_nfs_fileshare_kms_encryption(self): + storagegateway_client = mock.MagicMock + storagegateway_client.fileshares = [] + storagegateway_client.fileshares.append( + FileShare( + id=test_share_nfs, + arn=test_share_arn_nfs, + gateway_arn=test_gateway_arn, + region=AWS_REGION_US_EAST_1, + fs_type="NFS", + status="AVAILABLE", + kms=True, + kms_key=test_kms_key, + tags=[ + {"Key": "test", "Value": "test"}, + ], + ) + ) + with mock.patch( + "prowler.providers.aws.services.storagegateway.storagegateway_service.StorageGateway", + storagegateway_client, + ): + from prowler.providers.aws.services.storagegateway.storagegateway_fileshare_encryption_enabled.storagegateway_fileshare_encryption_enabled import ( + storagegateway_fileshare_encryption_enabled, + ) + + check = storagegateway_fileshare_encryption_enabled() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == f"StorageGateway File Share {test_share_nfs} is using KMS CMK." + ) + assert result[0].resource_id == f"{test_share_nfs}" + assert result[0].region == AWS_REGION_US_EAST_1 + assert ( + result[0].resource_arn + == f"arn:aws:storagegateway:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:share/{test_share_nfs}" + ) + assert result[0].resource_tags == [ + {"Key": "test", "Value": "test"}, + ] + + def test_nfs_fileshare_no_kms_encryption(self): + storagegateway_client = mock.MagicMock + storagegateway_client.fileshares = [] + storagegateway_client.fileshares.append( + FileShare( + id=test_share_nfs, + arn=test_share_arn_nfs, + gateway_arn=test_gateway_arn, + region=AWS_REGION_US_EAST_1, + fs_type="NFS", + status="AVAILABLE", + kms=False, + ) + ) + with mock.patch( + "prowler.providers.aws.services.storagegateway.storagegateway_service.StorageGateway", + storagegateway_client, + ): + from prowler.providers.aws.services.storagegateway.storagegateway_fileshare_encryption_enabled.storagegateway_fileshare_encryption_enabled import ( + storagegateway_fileshare_encryption_enabled, + ) + + check = storagegateway_fileshare_encryption_enabled() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"StorageGateway File Share {test_share_nfs} is not using KMS CMK." + ) + assert result[0].resource_id == f"{test_share_nfs}" + assert result[0].region == AWS_REGION_US_EAST_1 + assert ( + result[0].resource_arn + == f"arn:aws:storagegateway:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:share/{test_share_nfs}" + ) + assert result[0].resource_tags == [] + + def test_smb_fileshare_kms_encryption(self): + storagegateway_client = mock.MagicMock + storagegateway_client.fileshares = [] + storagegateway_client.fileshares.append( + FileShare( + id=test_share_smb, + arn=test_share_arn_smb, + gateway_arn=test_gateway_arn, + region=AWS_REGION_US_EAST_1, + fs_type="SMB", + status="AVAILABLE", + kms=True, + kms_key=test_kms_key, + tags=[ + {"Key": "test", "Value": "test"}, + ], + ) + ) + with mock.patch( + "prowler.providers.aws.services.storagegateway.storagegateway_service.StorageGateway", + storagegateway_client, + ): + from prowler.providers.aws.services.storagegateway.storagegateway_fileshare_encryption_enabled.storagegateway_fileshare_encryption_enabled import ( + storagegateway_fileshare_encryption_enabled, + ) + + check = storagegateway_fileshare_encryption_enabled() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == f"StorageGateway File Share {test_share_smb} is using KMS CMK." + ) + assert result[0].resource_id == f"{test_share_smb}" + assert result[0].region == AWS_REGION_US_EAST_1 + assert ( + result[0].resource_arn + == f"arn:aws:storagegateway:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:share/{test_share_smb}" + ) + assert result[0].resource_tags == [ + {"Key": "test", "Value": "test"}, + ] + + def test_smb_fileshare_no_kms_encryption(self): + storagegateway_client = mock.MagicMock + storagegateway_client.fileshares = [] + storagegateway_client.fileshares.append( + FileShare( + id=test_share_smb, + arn=test_share_arn_smb, + gateway_arn=test_gateway_arn, + region=AWS_REGION_US_EAST_1, + fs_type="SMB", + status="AVAILABLE", + kms=False, + ) + ) + with mock.patch( + "prowler.providers.aws.services.storagegateway.storagegateway_service.StorageGateway", + storagegateway_client, + ): + from prowler.providers.aws.services.storagegateway.storagegateway_fileshare_encryption_enabled.storagegateway_fileshare_encryption_enabled import ( + storagegateway_fileshare_encryption_enabled, + ) + + check = storagegateway_fileshare_encryption_enabled() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"StorageGateway File Share {test_share_smb} is not using KMS CMK." + ) + assert result[0].resource_id == f"{test_share_smb}" + assert result[0].region == AWS_REGION_US_EAST_1 + assert ( + result[0].resource_arn + == f"arn:aws:storagegateway:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:share/{test_share_smb}" + ) + assert result[0].resource_tags == [] diff --git a/tests/providers/aws/services/storagegateway/storagegateway_service_test.py b/tests/providers/aws/services/storagegateway/storagegateway_service_test.py new file mode 100644 index 0000000000..093382a9cf --- /dev/null +++ b/tests/providers/aws/services/storagegateway/storagegateway_service_test.py @@ -0,0 +1,127 @@ +from unittest.mock import patch + +import botocore +from moto import mock_aws + +from prowler.providers.aws.services.storagegateway.storagegateway_service import ( + StorageGateway, +) +from tests.providers.aws.utils import ( + AWS_ACCOUNT_NUMBER, + AWS_REGION_US_EAST_1, + set_mocked_aws_provider, +) + +test_gateway = "sgw-12A3456B" +test_gateway_arn = f"arn:aws:storagegateway:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:gateway/{test_gateway}" +test_iam_role = f"arn:aws:iam::{AWS_ACCOUNT_NUMBER}:role/my-role" +test_kms_key = f"arn:aws:kms:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:key/b72aaa2a-2222-99tt-12345690qwe" +test_share_nfs = "share-nfs2wwe" +test_share_arn_nfs = f"arn:aws:storagegateway:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:share/{test_share_nfs}" +test_share_smb = "share-smb2wwe" +test_share_arn_smb = f"arn:aws:storagegateway:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:share/{test_share_smb}" + +make_api_call = botocore.client.BaseClient._make_api_call + + +def mock_make_api_call(self, operation_name, kwarg): + if operation_name == "CreateNFSFileShare": + return {"FileShareARN": f"{test_share_arn_nfs}"} + if operation_name == "CreateSMBFileShare": + return {"FileShareARN": f"{test_share_arn_smb}"} + if operation_name == "ListFileShares": + return { + "FileShareInfoList": [ + { + "FileShareType": "NFS", + "FileShareARN": f"{test_share_arn_nfs}", + "FileShareId": f"{test_share_nfs}", + "FileShareStatus": "AVAILABLE", + "GatewayARN": f"{test_gateway_arn}", + }, + { + "FileShareType": "SMB", + "FileShareARN": f"{test_share_arn_smb}", + "FileShareId": f"{test_share_smb}", + "FileShareStatus": "AVAILABLE", + "GatewayARN": f"{test_gateway_arn}", + }, + ] + } + if operation_name == "DescribeNFSFileShares": + return { + "NFSFileShareInfoList": [ + { + "FileShareType": "NFS", + "FileShareARN": f"{test_share_arn_nfs}", + "FileShareId": f"{test_share_nfs}", + "FileShareStatus": "AVAILABLE", + "GatewayARN": f"{test_gateway_arn}", + "Tags": [ + {"Key": "test", "Value": "test"}, + ], + "KMSEncrypted": True, + "KMSKey": f"{test_kms_key}", + }, + ] + } + if operation_name == "DescribeSMBFileShares": + return { + "SMBFileShareInfoList": [ + { + "FileShareType": "SMB", + "FileShareARN": f"{test_share_arn_smb}", + "FileShareId": f"{test_share_smb}", + "FileShareStatus": "AVAILABLE", + "GatewayARN": f"{test_gateway_arn}", + "KMSEncrypted": False, + "KMSKey": "", + }, + ] + } + return make_api_call(self, operation_name, kwarg) + + +@patch("botocore.client.BaseClient._make_api_call", new=mock_make_api_call) +class Test_StorageGateway_Service: + + # Test SGW Service + @mock_aws + def test_service(self): + # SGW client for this test class + aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1]) + storagegateway = StorageGateway(aws_provider) + assert storagegateway.service == "storagegateway" + + # Test SGW Describe FileShares + @mock_aws + def test__describe_file_shares__(self): + # StorageGateway client for this test class + aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1]) + sgw = StorageGateway(aws_provider) + assert len(sgw.fileshares) == 2 + assert sgw.fileshares[0].id == "share-nfs2wwe" + assert sgw.fileshares[0].fs_type == "NFS" + assert sgw.fileshares[0].status == "AVAILABLE" + assert ( + sgw.fileshares[0].gateway_arn + == "arn:aws:storagegateway:us-east-1:123456789012:gateway/sgw-12A3456B" + ) + assert sgw.fileshares[0].kms + assert ( + sgw.fileshares[0].kms_key + == "arn:aws:kms:us-east-1:123456789012:key/b72aaa2a-2222-99tt-12345690qwe" + ) + assert sgw.fileshares[0].tags == [ + {"Key": "test", "Value": "test"}, + ] + assert sgw.fileshares[1].id == "share-smb2wwe" + assert sgw.fileshares[1].fs_type == "SMB" + assert sgw.fileshares[1].status == "AVAILABLE" + assert ( + sgw.fileshares[1].gateway_arn + == "arn:aws:storagegateway:us-east-1:123456789012:gateway/sgw-12A3456B" + ) + assert not sgw.fileshares[1].kms + assert sgw.fileshares[1].kms_key == "" + assert sgw.fileshares[1].tags == []