feat(AWS): New Storage Gateway FileShare KMS CMK Check (#4082)

This commit is contained in:
madereddy
2024-05-27 09:39:23 -04:00
committed by GitHub
parent b72e4a657c
commit ca554ad3ff
9 changed files with 484 additions and 0 deletions
@@ -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())
@@ -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": ""
}
@@ -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
@@ -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] = []
@@ -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 == []
@@ -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 == []