diff --git a/prowler/CHANGELOG.md b/prowler/CHANGELOG.md index 5cf7034acb..ee851f0b91 100644 --- a/prowler/CHANGELOG.md +++ b/prowler/CHANGELOG.md @@ -36,6 +36,7 @@ All notable changes to the **Prowler SDK** are documented in this file. - IaC provider [(#7852)](https://github.com/prowler-cloud/prowler/pull/7852) - Azure Databricks service integration for Azure provider, including the `databricks_workspace_vnet_injection_enabled` check [(#8008)](https://github.com/prowler-cloud/prowler/pull/8008) - Azure Databricks check `databricks_workspace_cmk_encryption_enabled` to ensure workspaces use customer-managed keys (CMK) for encryption at rest [(#8017)](https://github.com/prowler-cloud/prowler/pull/8017) +- Add `storage_account_default_to_entra_authorization_enabled` check for Azure provider. [(#7981)](https://github.com/prowler-cloud/prowler/pull/7981) --- diff --git a/prowler/providers/azure/services/storage/storage_default_to_entra_authorization_enabled/__init__.py b/prowler/providers/azure/services/storage/storage_default_to_entra_authorization_enabled/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/prowler/providers/azure/services/storage/storage_default_to_entra_authorization_enabled/storage_default_to_entra_authorization_enabled.metadata.json b/prowler/providers/azure/services/storage/storage_default_to_entra_authorization_enabled/storage_default_to_entra_authorization_enabled.metadata.json new file mode 100644 index 0000000000..00c1f2623b --- /dev/null +++ b/prowler/providers/azure/services/storage/storage_default_to_entra_authorization_enabled/storage_default_to_entra_authorization_enabled.metadata.json @@ -0,0 +1,30 @@ +{ + "Provider": "azure", + "CheckID": "storage_default_to_entra_authorization_enabled", + "CheckTitle": "Ensure Microsoft Entra authorization is enabled by default for Azure Storage Accounts", + "CheckType": [], + "ServiceName": "storage", + "SubServiceName": "", + "ResourceIdTemplate": "", + "Severity": "high", + "ResourceType": "AzureStorageAccount", + "Description": "Ensure that the Azure Storage Account setting 'Default to Microsoft Entra authorization in the Azure portal' is enabled to enforce the use of Microsoft Entra ID for accessing blobs, files, queues, and tables.", + "Risk": "If this setting is not enabled, the Azure portal may authorize access using less secure methods such as Shared Key, increasing the risk of unauthorized data access.", + "RelatedUrl": "https://learn.microsoft.com/en-us/azure/storage/blobs/authorize-access-azure-active-directory", + "Remediation": { + "Code": { + "CLI": "az storage account update --name --resource-group --default-to-AzAd-auth true", + "NativeIaC": "", + "Other": "https://www.trendmicro.com/cloudoneconformity-staging/knowledge-base/azure/StorageAccounts/enable-microsoft-entra-authorization-by-default.html", + "Terraform": "" + }, + "Recommendation": { + "Text": "Enable Microsoft Entra authorization by default in the Azure portal to enhance security and avoid reliance on Shared Key authentication.", + "Url": "https://learn.microsoft.com/en-us/azure/storage/blobs/authorize-access-azure-active-directory" + } + }, + "Categories": [], + "DependsOn": [], + "RelatedTo": [], + "Notes": "" +} diff --git a/prowler/providers/azure/services/storage/storage_default_to_entra_authorization_enabled/storage_default_to_entra_authorization_enabled.py b/prowler/providers/azure/services/storage/storage_default_to_entra_authorization_enabled/storage_default_to_entra_authorization_enabled.py new file mode 100644 index 0000000000..4b82c363e5 --- /dev/null +++ b/prowler/providers/azure/services/storage/storage_default_to_entra_authorization_enabled/storage_default_to_entra_authorization_enabled.py @@ -0,0 +1,38 @@ +from prowler.lib.check.models import Check, Check_Report_Azure +from prowler.providers.azure.services.storage.storage_client import storage_client + + +class storage_default_to_entra_authorization_enabled(Check): + """Check if the default to Microsoft Entra authorization is enabled for the storage account. + + Attributes: + metadata: Metadata associated with the check (inherited from Check). + + """ + + def execute(self) -> Check_Report_Azure: + """Execute the check for the default to Microsoft Entra authorization. + + This method checks if the default to Microsoft Entra authorization is enabled for the storage account. + + Returns: + Check_Report_Azure: A report containing the result of the check. + """ + findings = [] + for subscription, storage_accounts in storage_client.storage_accounts.items(): + for storage_account in storage_accounts: + report = Check_Report_Azure( + metadata=self.metadata(), resource=storage_account + ) + report.subscription = subscription + report.resource_name = storage_account.name + report.resource_id = storage_account.id + report.status = "FAIL" + report.status_extended = f"Default to Microsoft Entra authorization is not enabled for storage account {storage_account.name}." + + if storage_account.default_to_entra_authorization: + report.status = "PASS" + report.status_extended = f"Default to Microsoft Entra authorization is enabled for storage account {storage_account.name}." + + 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 026742631b..374409b470 100644 --- a/prowler/providers/azure/services/storage/storage_service.py +++ b/prowler/providers/azure/services/storage/storage_service.py @@ -70,6 +70,11 @@ class Storage(AzureService): ], key_expiration_period_in_days=key_expiration_period_in_days, location=storage_account.location, + default_to_entra_authorization=getattr( + storage_account, + "default_to_o_auth_authentication", + False, + ), replication_settings=replication_settings, allow_cross_tenant_replication=getattr( storage_account, "allow_cross_tenant_replication", True @@ -243,6 +248,7 @@ class Account: allow_cross_tenant_replication: bool = True allow_shared_key_access: bool = True blob_properties: Optional[BlobProperties] = None + default_to_entra_authorization: bool = False file_shares: list = None diff --git a/tests/providers/azure/services/storage/storage_default_to_entra_authorization_enabled/storage_default_to_entra_authorization_enabled_test.py b/tests/providers/azure/services/storage/storage_default_to_entra_authorization_enabled/storage_default_to_entra_authorization_enabled_test.py new file mode 100644 index 0000000000..33de6038d8 --- /dev/null +++ b/tests/providers/azure/services/storage/storage_default_to_entra_authorization_enabled/storage_default_to_entra_authorization_enabled_test.py @@ -0,0 +1,134 @@ +from unittest import mock +from uuid import uuid4 + +from prowler.providers.azure.services.storage.storage_service import Account +from tests.providers.azure.azure_fixtures import ( + AZURE_SUBSCRIPTION_ID, + set_mocked_azure_provider, +) + + +class Test_storage_default_to_entra_authorization_enabled: + def test_no_storage_accounts(self): + storage_client = mock.MagicMock() + storage_client.storage_accounts = {} + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=set_mocked_azure_provider(), + ), + mock.patch( + "prowler.providers.azure.services.storage.storage_default_to_entra_authorization_enabled.storage_default_to_entra_authorization_enabled.storage_client", + new=storage_client, + ), + ): + from prowler.providers.azure.services.storage.storage_default_to_entra_authorization_enabled.storage_default_to_entra_authorization_enabled import ( + storage_default_to_entra_authorization_enabled, + ) + + check = storage_default_to_entra_authorization_enabled() + result = check.execute() + assert len(result) == 0 + + def test_storage_default_to_entra_authorization_enabled(self): + storage_account_id = str(uuid4()) + storage_account_name = "Test Storage Account Entra Auth Enabled" + storage_client = mock.MagicMock() + storage_client.storage_accounts = { + AZURE_SUBSCRIPTION_ID: [ + Account( + id=storage_account_id, + name=storage_account_name, + resouce_group_name=None, + enable_https_traffic_only=False, + infrastructure_encryption=False, + allow_blob_public_access=False, + network_rule_set=None, + encryption_type=None, + minimum_tls_version=None, + key_expiration_period_in_days=None, + location="westeurope", + private_endpoint_connections=None, + default_to_entra_authorization=True, + ) + ] + } + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=set_mocked_azure_provider(), + ), + mock.patch( + "prowler.providers.azure.services.storage.storage_default_to_entra_authorization_enabled.storage_default_to_entra_authorization_enabled.storage_client", + new=storage_client, + ), + ): + from prowler.providers.azure.services.storage.storage_default_to_entra_authorization_enabled.storage_default_to_entra_authorization_enabled import ( + storage_default_to_entra_authorization_enabled, + ) + + check = storage_default_to_entra_authorization_enabled() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == f"Default to Microsoft Entra authorization is enabled for storage account {storage_account_name}." + ) + assert result[0].subscription == AZURE_SUBSCRIPTION_ID + assert result[0].resource_name == storage_account_name + assert result[0].resource_id == storage_account_id + assert result[0].location == "westeurope" + + def test_storage_account_default_to_entra_authorization_disabled(self): + storage_account_id = str(uuid4()) + storage_account_name = "Test Storage Account Entra Auth Disabled" + storage_client = mock.MagicMock() + storage_client.storage_accounts = { + AZURE_SUBSCRIPTION_ID: [ + Account( + id=storage_account_id, + name=storage_account_name, + resouce_group_name=None, + enable_https_traffic_only=False, + infrastructure_encryption=False, + allow_blob_public_access=False, + network_rule_set=None, + encryption_type=None, + minimum_tls_version=None, + key_expiration_period_in_days=None, + location="westeurope", + private_endpoint_connections=None, + default_to_entra_authorization=False, + ) + ] + } + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=set_mocked_azure_provider(), + ), + mock.patch( + "prowler.providers.azure.services.storage.storage_default_to_entra_authorization_enabled.storage_default_to_entra_authorization_enabled.storage_client", + new=storage_client, + ), + ): + from prowler.providers.azure.services.storage.storage_default_to_entra_authorization_enabled.storage_default_to_entra_authorization_enabled import ( + storage_default_to_entra_authorization_enabled, + ) + + check = storage_default_to_entra_authorization_enabled() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"Default to Microsoft Entra authorization is not enabled for storage account {storage_account_name}." + ) + assert result[0].subscription == AZURE_SUBSCRIPTION_ID + assert result[0].resource_name == storage_account_name + assert result[0].resource_id == storage_account_id + assert result[0].location == "westeurope" diff --git a/tests/providers/azure/services/storage/storage_service_test.py b/tests/providers/azure/services/storage/storage_service_test.py index 9175ca9876..2ac6031322 100644 --- a/tests/providers/azure/services/storage/storage_service_test.py +++ b/tests/providers/azure/services/storage/storage_service_test.py @@ -41,6 +41,7 @@ def mock_storage_get_storage_accounts(_): private_endpoint_connections=None, location="westeurope", blob_properties=blob_properties, + default_to_entra_authorization=True, replication_settings=ReplicationSettings.STANDARD_LRS, allow_cross_tenant_replication=True, allow_shared_key_access=True, @@ -120,6 +121,9 @@ class Test_Storage_Service: default_service_version=None, container_delete_retention_policy=None, ) + assert storage.storage_accounts[AZURE_SUBSCRIPTION_ID][ + 0 + ].default_to_entra_authorization assert ( storage.storage_accounts[AZURE_SUBSCRIPTION_ID][0].replication_settings == ReplicationSettings.STANDARD_LRS