diff --git a/prowler/providers/azure/services/storage/storage_key_rotation_90_days/__init__.py b/prowler/providers/azure/services/storage/storage_key_rotation_90_days/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/prowler/providers/azure/services/storage/storage_key_rotation_90_days/storage_key_rotation_90_days.metadata.json b/prowler/providers/azure/services/storage/storage_key_rotation_90_days/storage_key_rotation_90_days.metadata.json new file mode 100644 index 00000000..548782f2 --- /dev/null +++ b/prowler/providers/azure/services/storage/storage_key_rotation_90_days/storage_key_rotation_90_days.metadata.json @@ -0,0 +1,32 @@ +{ + "Provider": "azure", + "CheckID": "storage_key_rotation_90_days", + "CheckTitle": "Ensure that Storage Account Access Keys are Periodically Regenerated", + "CheckType": [], + "ServiceName": "storage", + "SubServiceName": "", + "ResourceIdTemplate": "", + "Severity": "medium", + "ResourceType": "AzureStorageAccount", + "Description": "Ensure that Storage Account Access Keys are Periodically Regenerated", + "Risk": "If the access keys are not regenerated periodically, the likelihood of accidental exposures increases, which can lead to unauthorized access to your storage account resources.", + "RelatedUrl": "https://learn.microsoft.com/en-us/azure/storage/common/storage-account-keys-manage?tabs=azure-portal", + "Remediation": { + "Code": { + "CLI": "", + "NativeIaC": "", + "Other": "https://www.trendmicro.com/cloudoneconformity-staging/knowledge-base/azure/StorageAccounts/regenerate-storage-account-access-keys-periodically.html#", + "Terraform": "" + }, + "Recommendation": { + "Text": "Ensure that Azure Storage account access keys are regenerated every 90 days in order to decrease the likelihood of accidental exposures and protect your storage account resources against unauthorized access.", + "Url": "https://learn.microsoft.com/en-us/azure/storage/common/storage-account-create?tabs=azure-portal#regenerate-storage-access-keys" + } + }, + "Categories": [ + "encryption" + ], + "DependsOn": [], + "RelatedTo": [], + "Notes": "" +} diff --git a/prowler/providers/azure/services/storage/storage_key_rotation_90_days/storage_key_rotation_90_days.py b/prowler/providers/azure/services/storage/storage_key_rotation_90_days/storage_key_rotation_90_days.py new file mode 100644 index 00000000..c6e7553c --- /dev/null +++ b/prowler/providers/azure/services/storage/storage_key_rotation_90_days/storage_key_rotation_90_days.py @@ -0,0 +1,26 @@ +from prowler.lib.check.models import Check, Check_Report_Azure +from prowler.providers.azure.services.storage.storage_client import storage_client + + +class storage_key_rotation_90_days(Check): + def execute(self) -> Check_Report_Azure: + findings = [] + for subscription, storage_accounts in storage_client.storage_accounts.items(): + for storage_account in storage_accounts: + report = Check_Report_Azure(self.metadata()) + report.subscription = subscription + report.resource_name = storage_account.name + report.resource_id = storage_account.id + if not storage_account.key_expiration_period_in_days: + report.status = "FAIL" + report.status_extended = f"Storage account {storage_account.name} from subscription {subscription} has no key expiration period set." + else: + if storage_account.key_expiration_period_in_days > 90: + report.status = "FAIL" + report.status_extended = f"Storage account {storage_account.name} from subscription {subscription} has an invalid key expiration period of {storage_account.key_expiration_period_in_days} days." + else: + report.status = "PASS" + report.status_extended = f"Storage account {storage_account.name} from subscription {subscription} has a key expiration period of {storage_account.key_expiration_period_in_days} days." + findings.append(report) + + return findings diff --git a/prowler/providers/azure/services/storage/storage_service.py b/prowler/providers/azure/services/storage/storage_service.py index fcc7168b..2a57187f 100644 --- a/prowler/providers/azure/services/storage/storage_service.py +++ b/prowler/providers/azure/services/storage/storage_service.py @@ -31,12 +31,12 @@ class Storage(AzureService): network_rule_set=storage_account.network_rule_set, encryption_type=storage_account.encryption.key_source, minimum_tls_version=storage_account.minimum_tls_version, + key_expiration_period_in_days=storage_account.key_policy.key_expiration_period_in_days, ) ) except Exception as error: - logger.error(f"Subscription name: {subscription}") logger.error( - f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + f"Subscription name: {subscription} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" ) return storage_accounts @@ -51,3 +51,4 @@ class Storage_Account: network_rule_set: NetworkRuleSet encryption_type: str minimum_tls_version: str + key_expiration_period_in_days: str diff --git a/tests/lib/check/check_test.py b/tests/lib/check/check_test.py index e3b3e41b..9b749ded 100644 --- a/tests/lib/check/check_test.py +++ b/tests/lib/check/check_test.py @@ -40,6 +40,13 @@ expected_packages = [ name="prowler.providers.azure.services.storage.storage_ensure_minimum_tls_version_12.storage_ensure_minimum_tls_version_12", ispkg=False, ), + ModuleInfo( + module_finder=FileFinder( + "/root_dir/prowler/providers/azure/services/storage/storage_key_rotation_90_days" + ), + name="prowler.providers.azure.services.storage.storage_key_rotation_90_days.storage_key_rotation_90_days", + ispkg=False, + ), ModuleInfo( module_finder=FileFinder("/root_dir/prowler/providers/azure/services/storage"), name="prowler.providers.azure.services.storage.storage_ensure_encryption_with_customer_managed_keys", @@ -68,6 +75,13 @@ def mock_list_modules(*_): name="prowler.providers.azure.services.storage.storage_ensure_minimum_tls_version_12.storage_ensure_minimum_tls_version_12", ispkg=False, ), + ModuleInfo( + module_finder=FileFinder( + "/root_dir/prowler/providers/azure/services/storage/storage_key_rotation_90_days" + ), + name="prowler.providers.azure.services.storage.storage_key_rotation_90_days.storage_key_rotation_90_days", + ispkg=False, + ), ModuleInfo( module_finder=FileFinder( "/root_dir/prowler/providers/azure/services/storage" @@ -447,6 +461,10 @@ class Test_Check: "storage_ensure_minimum_tls_version_12", "/root_dir/prowler/providers/azure/services/storage/storage_ensure_minimum_tls_version_12", ), + ( + "storage_key_rotation_90_days", + "/root_dir/prowler/providers/azure/services/storage/storage_key_rotation_90_days", + ), ( "storage_ensure_encryption_with_customer_managed_keys", "/root_dir/prowler/providers/azure/services/storage/storage_ensure_encryption_with_customer_managed_keys", diff --git a/tests/providers/azure/services/storage/storage_blob_public_access_level_is_disabled/storage_blob_public_access_level_is_disabled_test.py b/tests/providers/azure/services/storage/storage_blob_public_access_level_is_disabled/storage_blob_public_access_level_is_disabled_test.py index 94f31b3c..c0cedf97 100644 --- a/tests/providers/azure/services/storage/storage_blob_public_access_level_is_disabled/storage_blob_public_access_level_is_disabled_test.py +++ b/tests/providers/azure/services/storage/storage_blob_public_access_level_is_disabled/storage_blob_public_access_level_is_disabled_test.py @@ -38,6 +38,7 @@ class Test_storage_blob_public_access_level_is_disabled: network_rule_set=None, encryption_type=None, minimum_tls_version=None, + key_expiration_period_in_days=None, ) ] } @@ -77,6 +78,7 @@ class Test_storage_blob_public_access_level_is_disabled: network_rule_set=None, encryption_type=None, minimum_tls_version=None, + key_expiration_period_in_days=None, ) ] } diff --git a/tests/providers/azure/services/storage/storage_default_network_access_rule_is_denied/storage_default_network_access_rule_is_denied_test.py b/tests/providers/azure/services/storage/storage_default_network_access_rule_is_denied/storage_default_network_access_rule_is_denied_test.py index 18fa9f2e..a37f7594 100644 --- a/tests/providers/azure/services/storage/storage_default_network_access_rule_is_denied/storage_default_network_access_rule_is_denied_test.py +++ b/tests/providers/azure/services/storage/storage_default_network_access_rule_is_denied/storage_default_network_access_rule_is_denied_test.py @@ -40,6 +40,7 @@ class Test_storage_default_network_access_rule_is_denied: network_rule_set=NetworkRuleSet(default_action="Allow"), encryption_type=None, minimum_tls_version=None, + key_expiration_period_in_days=None, ) ] } @@ -79,6 +80,7 @@ class Test_storage_default_network_access_rule_is_denied: network_rule_set=NetworkRuleSet(default_action="Deny"), encryption_type=None, minimum_tls_version=None, + key_expiration_period_in_days=None, ) ] } diff --git a/tests/providers/azure/services/storage/storage_ensure_azure_services_are_trusted_to_access_is_enabled/storage_ensure_azure_services_are_trusted_to_access_is_enabled_test.py b/tests/providers/azure/services/storage/storage_ensure_azure_services_are_trusted_to_access_is_enabled/storage_ensure_azure_services_are_trusted_to_access_is_enabled_test.py index 983c5e9c..ee80ef95 100644 --- a/tests/providers/azure/services/storage/storage_ensure_azure_services_are_trusted_to_access_is_enabled/storage_ensure_azure_services_are_trusted_to_access_is_enabled_test.py +++ b/tests/providers/azure/services/storage/storage_ensure_azure_services_are_trusted_to_access_is_enabled/storage_ensure_azure_services_are_trusted_to_access_is_enabled_test.py @@ -40,6 +40,7 @@ class Test_storage_ensure_azure_services_are_trusted_to_access_is_enabled: network_rule_set=NetworkRuleSet(bypass=[None]), encryption_type=None, minimum_tls_version=None, + key_expiration_period_in_days=None, ) ] } @@ -79,6 +80,7 @@ class Test_storage_ensure_azure_services_are_trusted_to_access_is_enabled: network_rule_set=NetworkRuleSet(bypass=["AzureServices"]), encryption_type=None, minimum_tls_version=None, + key_expiration_period_in_days=None, ) ] } diff --git a/tests/providers/azure/services/storage/storage_ensure_encryption_with_customer_managed_keys/storage_ensure_encryption_with_customer_managed_keys_test.py b/tests/providers/azure/services/storage/storage_ensure_encryption_with_customer_managed_keys/storage_ensure_encryption_with_customer_managed_keys_test.py index 3f0deede..5a4bfe7b 100644 --- a/tests/providers/azure/services/storage/storage_ensure_encryption_with_customer_managed_keys/storage_ensure_encryption_with_customer_managed_keys_test.py +++ b/tests/providers/azure/services/storage/storage_ensure_encryption_with_customer_managed_keys/storage_ensure_encryption_with_customer_managed_keys_test.py @@ -38,6 +38,7 @@ class Test_storage_ensure_encryption_with_customer_managed_keys: network_rule_set=None, encryption_type="None", minimum_tls_version=None, + key_expiration_period_in_days=None, ) ] } @@ -77,6 +78,7 @@ class Test_storage_ensure_encryption_with_customer_managed_keys: network_rule_set=None, encryption_type="Microsoft.Keyvault", minimum_tls_version=None, + key_expiration_period_in_days=None, ) ] } diff --git a/tests/providers/azure/services/storage/storage_ensure_minimum_tls_version_12/storage_ensure_minimum_tls_version_12_test.py b/tests/providers/azure/services/storage/storage_ensure_minimum_tls_version_12/storage_ensure_minimum_tls_version_12_test.py index ac35f941..c0f87594 100644 --- a/tests/providers/azure/services/storage/storage_ensure_minimum_tls_version_12/storage_ensure_minimum_tls_version_12_test.py +++ b/tests/providers/azure/services/storage/storage_ensure_minimum_tls_version_12/storage_ensure_minimum_tls_version_12_test.py @@ -38,6 +38,7 @@ class Test_storage_ensure_minimum_tls_version_12: network_rule_set=None, encryption_type="None", minimum_tls_version="TLS1_1", + key_expiration_period_in_days=None, ) ] } @@ -77,6 +78,7 @@ class Test_storage_ensure_minimum_tls_version_12: network_rule_set=None, encryption_type="None", minimum_tls_version="TLS1_2", + key_expiration_period_in_days=None, ) ] } diff --git a/tests/providers/azure/services/storage/storage_infrastructure_encryption_is_enabled/storage_infrastructure_encryption_is_enabled_test.py b/tests/providers/azure/services/storage/storage_infrastructure_encryption_is_enabled/storage_infrastructure_encryption_is_enabled_test.py index d12aabb1..98fc28b8 100644 --- a/tests/providers/azure/services/storage/storage_infrastructure_encryption_is_enabled/storage_infrastructure_encryption_is_enabled_test.py +++ b/tests/providers/azure/services/storage/storage_infrastructure_encryption_is_enabled/storage_infrastructure_encryption_is_enabled_test.py @@ -38,6 +38,7 @@ class Test_storage_infrastructure_encryption_is_enabled: network_rule_set=None, encryption_type="None", minimum_tls_version="TLS1_1", + key_expiration_period_in_days=None, ) ] } @@ -77,6 +78,7 @@ class Test_storage_infrastructure_encryption_is_enabled: network_rule_set=None, encryption_type="None", minimum_tls_version="TLS1_1", + key_expiration_period_in_days=None, ) ] } diff --git a/tests/providers/azure/services/storage/storage_key_rotation_90_days/storage_key_rotation_90_days_test.py b/tests/providers/azure/services/storage/storage_key_rotation_90_days/storage_key_rotation_90_days_test.py new file mode 100644 index 00000000..6eb767cf --- /dev/null +++ b/tests/providers/azure/services/storage/storage_key_rotation_90_days/storage_key_rotation_90_days_test.py @@ -0,0 +1,145 @@ +from unittest import mock +from uuid import uuid4 + +from prowler.providers.azure.services.storage.storage_service import Storage_Account +from tests.providers.azure.azure_fixtures import AZURE_SUSCRIPTION + + +class Test_storage_key_rotation_90_dayss: + def test_storage_no_storage_accounts(self): + storage_client = mock.MagicMock + storage_client.storage_accounts = {} + + with mock.patch( + "prowler.providers.azure.services.storage.storage_key_rotation_90_days.storage_key_rotation_90_days.storage_client", + new=storage_client, + ): + from prowler.providers.azure.services.storage.storage_key_rotation_90_days.storage_key_rotation_90_days import ( + storage_key_rotation_90_days, + ) + + check = storage_key_rotation_90_days() + result = check.execute() + assert len(result) == 0 + + def test_storage_storage_key_rotation_91_days(self): + storage_account_id = str(uuid4()) + storage_account_name = "Test Storage Account" + expiration_days = 91 + storage_client = mock.MagicMock + storage_client.storage_accounts = { + AZURE_SUSCRIPTION: [ + Storage_Account( + id=storage_account_id, + name=storage_account_name, + enable_https_traffic_only=False, + infrastructure_encryption=False, + allow_blob_public_access=None, + network_rule_set=None, + encryption_type="None", + minimum_tls_version="TLS1_1", + key_expiration_period_in_days=expiration_days, + ) + ] + } + + with mock.patch( + "prowler.providers.azure.services.storage.storage_key_rotation_90_days.storage_key_rotation_90_days.storage_client", + new=storage_client, + ): + from prowler.providers.azure.services.storage.storage_key_rotation_90_days.storage_key_rotation_90_days import ( + storage_key_rotation_90_days, + ) + + check = storage_key_rotation_90_days() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"Storage account {storage_account_name} from subscription {AZURE_SUSCRIPTION} has an invalid key expiration period of {expiration_days} days." + ) + assert result[0].subscription == AZURE_SUSCRIPTION + assert result[0].resource_name == storage_account_name + assert result[0].resource_id == storage_account_id + + def test_storage_storage_key_rotation_90_days(self): + storage_account_id = str(uuid4()) + storage_account_name = "Test Storage Account" + expiration_days = 90 + storage_client = mock.MagicMock + storage_client.storage_accounts = { + AZURE_SUSCRIPTION: [ + Storage_Account( + id=storage_account_id, + name=storage_account_name, + enable_https_traffic_only=False, + infrastructure_encryption=False, + allow_blob_public_access=None, + network_rule_set=None, + encryption_type="None", + minimum_tls_version="TLS1_2", + key_expiration_period_in_days=expiration_days, + ) + ] + } + + with mock.patch( + "prowler.providers.azure.services.storage.storage_key_rotation_90_days.storage_key_rotation_90_days.storage_client", + new=storage_client, + ): + from prowler.providers.azure.services.storage.storage_key_rotation_90_days.storage_key_rotation_90_days import ( + storage_key_rotation_90_days, + ) + + check = storage_key_rotation_90_days() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == f"Storage account {storage_account_name} from subscription {AZURE_SUSCRIPTION} has a key expiration period of {expiration_days} days." + ) + assert result[0].subscription == AZURE_SUSCRIPTION + assert result[0].resource_name == storage_account_name + assert result[0].resource_id == storage_account_id + + def test_storage_storage_no_key_rotation(self): + storage_account_id = str(uuid4()) + storage_account_name = "Test Storage Account" + storage_client = mock.MagicMock + storage_client.storage_accounts = { + AZURE_SUSCRIPTION: [ + Storage_Account( + id=storage_account_id, + name=storage_account_name, + enable_https_traffic_only=False, + infrastructure_encryption=False, + allow_blob_public_access=None, + network_rule_set=None, + encryption_type="None", + minimum_tls_version="TLS1_2", + key_expiration_period_in_days=None, + ) + ] + } + + with mock.patch( + "prowler.providers.azure.services.storage.storage_key_rotation_90_days.storage_key_rotation_90_days.storage_client", + new=storage_client, + ): + from prowler.providers.azure.services.storage.storage_key_rotation_90_days.storage_key_rotation_90_days import ( + storage_key_rotation_90_days, + ) + + check = storage_key_rotation_90_days() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"Storage account {storage_account_name} from subscription {AZURE_SUSCRIPTION} has no key expiration period set." + ) + assert result[0].subscription == AZURE_SUSCRIPTION + assert result[0].resource_name == storage_account_name + assert result[0].resource_id == storage_account_id diff --git a/tests/providers/azure/services/storage/storage_secure_transfer_required_is_enabled/storage_secure_transfer_required_is_enabled_test.py b/tests/providers/azure/services/storage/storage_secure_transfer_required_is_enabled/storage_secure_transfer_required_is_enabled_test.py index 663ee684..4c3bb232 100644 --- a/tests/providers/azure/services/storage/storage_secure_transfer_required_is_enabled/storage_secure_transfer_required_is_enabled_test.py +++ b/tests/providers/azure/services/storage/storage_secure_transfer_required_is_enabled/storage_secure_transfer_required_is_enabled_test.py @@ -38,6 +38,7 @@ class Test_storage_secure_transfer_required_is_enabled: network_rule_set=None, encryption_type="None", minimum_tls_version="TLS1_1", + key_expiration_period_in_days=None, ) ] } @@ -77,6 +78,7 @@ class Test_storage_secure_transfer_required_is_enabled: network_rule_set=None, encryption_type="None", minimum_tls_version="TLS1_1", + key_expiration_period_in_days=None, ) ] }