diff --git a/docs/user-guide/cli/tutorials/configuration_file.mdx b/docs/user-guide/cli/tutorials/configuration_file.mdx index 38dad9ac3e..8d24eb90f8 100644 --- a/docs/user-guide/cli/tutorials/configuration_file.mdx +++ b/docs/user-guide/cli/tutorials/configuration_file.mdx @@ -91,6 +91,7 @@ The following list includes all the Azure checks with configurable variables tha | `sqlserver_recommended_minimal_tls_version` | `recommended_minimal_tls_versions` | List of Strings | | `vm_sufficient_daily_backup_retention_period` | `vm_backup_min_daily_retention_days` | Integer | | `vm_desired_sku_size` | `desired_vm_sku_sizes` | List of Strings | +| `storage_smb_channel_encryption_with_secure_algorithm` | `recommended_smb_channel_encryption_algorithms` | List of Strings | | `defender_attack_path_notifications_properly_configured` | `defender_attack_path_minimal_risk_level` | String | | `apim_threat_detection_llm_jacking` | `apim_threat_detection_llm_jacking_threshold` | Float | | `apim_threat_detection_llm_jacking` | `apim_threat_detection_llm_jacking_minutes` | Integer | @@ -534,6 +535,18 @@ azure: "1.3" ] + # Azure Storage + # azure.storage_smb_channel_encryption_with_secure_algorithm + # List of SMB channel encryption algorithms allowed on file shares. A storage + # account passes only if every enabled algorithm is in this list. Defaults to + # the value required by CIS (AES-256-GCM only, excluding weaker AES-128 ciphers). + recommended_smb_channel_encryption_algorithms: + [ + "AES-256-GCM", + # "AES-128-CCM", + # "AES-128-GCM", + ] + # Azure Virtual Machines # azure.vm_desired_sku_size # List of desired VM SKU sizes that are allowed in the organization diff --git a/prowler/CHANGELOG.md b/prowler/CHANGELOG.md index 6cea3faf30..d786e23ee6 100644 --- a/prowler/CHANGELOG.md +++ b/prowler/CHANGELOG.md @@ -10,6 +10,14 @@ All notable changes to the **Prowler SDK** are documented in this file. --- +## [5.28.1] (Prowler v5.28.1) + +### 🐞 Fixed + +- `storage_smb_channel_encryption_with_secure_algorithm` check for Azure provider no longer passes when a storage account allows a weak SMB channel encryption algorithm (e.g. `AES-128-CCM`/`AES-128-GCM`) alongside `AES-256-GCM`; it now requires every enabled algorithm to be in the recommended list, configurable via `azure.recommended_smb_channel_encryption_algorithms` (defaults to `AES-256-GCM` only, as required by CIS) [(#11327)](https://github.com/prowler-cloud/prowler/pull/11327) + +--- + ## [5.28.0] (Prowler v5.28.0) ### 🚀 Added diff --git a/prowler/config/config.yaml b/prowler/config/config.yaml index a98512d720..ae263789e7 100644 --- a/prowler/config/config.yaml +++ b/prowler/config/config.yaml @@ -467,6 +467,18 @@ azure: "1.3", ] + # Azure Storage + # azure.storage_smb_channel_encryption_with_secure_algorithm + # List of SMB channel encryption algorithms allowed on file shares. A storage + # account passes only if every enabled algorithm is in this list. Defaults to + # the value required by CIS (AES-256-GCM only, excluding weaker AES-128 ciphers). + recommended_smb_channel_encryption_algorithms: + [ + "AES-256-GCM", + # "AES-128-CCM", + # "AES-128-GCM", + ] + # Azure Virtual Machines # azure.vm_desired_sku_size # List of desired VM SKU sizes that are allowed in the organization diff --git a/prowler/providers/azure/services/storage/storage_smb_channel_encryption_with_secure_algorithm/storage_smb_channel_encryption_with_secure_algorithm.metadata.json b/prowler/providers/azure/services/storage/storage_smb_channel_encryption_with_secure_algorithm/storage_smb_channel_encryption_with_secure_algorithm.metadata.json index 0649b30110..3bffd83e67 100644 --- a/prowler/providers/azure/services/storage/storage_smb_channel_encryption_with_secure_algorithm/storage_smb_channel_encryption_with_secure_algorithm.metadata.json +++ b/prowler/providers/azure/services/storage/storage_smb_channel_encryption_with_secure_algorithm/storage_smb_channel_encryption_with_secure_algorithm.metadata.json @@ -34,5 +34,5 @@ ], "DependsOn": [], "RelatedTo": [], - "Notes": "This check passes if SMB channel encryption is set to a secure algorithm." + "Notes": "This check passes only if every SMB channel encryption algorithm allowed on the file shares is in the recommended list, which is configurable via azure.recommended_smb_channel_encryption_algorithms and defaults to AES-256-GCM only, as required by CIS." } diff --git a/prowler/providers/azure/services/storage/storage_smb_channel_encryption_with_secure_algorithm/storage_smb_channel_encryption_with_secure_algorithm.py b/prowler/providers/azure/services/storage/storage_smb_channel_encryption_with_secure_algorithm/storage_smb_channel_encryption_with_secure_algorithm.py index e46b951583..61d4abc185 100644 --- a/prowler/providers/azure/services/storage/storage_smb_channel_encryption_with_secure_algorithm/storage_smb_channel_encryption_with_secure_algorithm.py +++ b/prowler/providers/azure/services/storage/storage_smb_channel_encryption_with_secure_algorithm/storage_smb_channel_encryption_with_secure_algorithm.py @@ -1,32 +1,38 @@ from prowler.lib.check.models import Check, Check_Report_Azure from prowler.providers.azure.services.storage.storage_client import storage_client -SECURE_ENCRYPTION_ALGORITHMS = ["AES-256-GCM"] +DEFAULT_SECURE_ENCRYPTION_ALGORITHMS = ["AES-256-GCM"] class storage_smb_channel_encryption_with_secure_algorithm(Check): """ - Ensure SMB channel encryption for file shares is set to the recommended algorithm (AES-256-GCM or higher). + Ensure SMB channel encryption for file shares only allows secure algorithms (AES-256-GCM or higher by default). + + The list of allowed algorithms is configurable via + azure.recommended_smb_channel_encryption_algorithms in the Prowler configuration file. This check evaluates whether SMB file shares are configured to use only the recommended SMB channel encryption algorithms. - - PASS: Storage account has the recommended SMB channel encryption (AES-256-GCM or higher) enabled for file shares. - - FAIL: Storage account does not have the recommended SMB channel encryption enabled for file shares or uses an unsupported algorithm. + - PASS: Storage account only allows secure SMB channel encryption algorithms for file shares. + - FAIL: Storage account does not have SMB channel encryption enabled, or it allows at least one algorithm that is not in the recommended list. """ def execute(self) -> list[Check_Report_Azure]: findings = [] + secure_encryption_algorithms = storage_client.audit_config.get( + "recommended_smb_channel_encryption_algorithms", + DEFAULT_SECURE_ENCRYPTION_ALGORITHMS, + ) for subscription, storage_accounts in storage_client.storage_accounts.items(): subscription_name = storage_client.subscriptions.get( subscription, subscription ) for account in storage_accounts: if account.file_service_properties: + channel_encryption = ( + account.file_service_properties.smb_protocol_settings.channel_encryption + ) pretty_current_algorithms = ( - ", ".join( - account.file_service_properties.smb_protocol_settings.channel_encryption - ) - if account.file_service_properties.smb_protocol_settings.channel_encryption - else "none" + ", ".join(channel_encryption) if channel_encryption else "none" ) report = Check_Report_Azure( metadata=self.metadata(), @@ -35,20 +41,18 @@ class storage_smb_channel_encryption_with_secure_algorithm(Check): report.subscription = subscription report.resource_name = account.name - if ( - not account.file_service_properties.smb_protocol_settings.channel_encryption - ): + if not channel_encryption: report.status = "FAIL" report.status_extended = f"Storage account {account.name} from subscription {subscription_name} ({subscription}) does not have SMB channel encryption enabled for file shares." - elif any( - algorithm in SECURE_ENCRYPTION_ALGORITHMS - for algorithm in account.file_service_properties.smb_protocol_settings.channel_encryption + elif all( + algorithm in secure_encryption_algorithms + for algorithm in channel_encryption ): report.status = "PASS" - report.status_extended = f"Storage account {account.name} from subscription {subscription_name} ({subscription}) has a secure algorithm for SMB channel encryption ({', '.join(SECURE_ENCRYPTION_ALGORITHMS)}) enabled for file shares since it supports {pretty_current_algorithms}." + report.status_extended = f"Storage account {account.name} from subscription {subscription_name} ({subscription}) only allows secure algorithms for SMB channel encryption on file shares since it supports {pretty_current_algorithms}." else: report.status = "FAIL" - report.status_extended = f"Storage account {account.name} from subscription {subscription_name} ({subscription}) does not have SMB channel encryption with a secure algorithm for file shares since it supports {pretty_current_algorithms}." + report.status_extended = f"Storage account {account.name} from subscription {subscription_name} ({subscription}) allows insecure algorithms for SMB channel encryption on file shares since it supports {pretty_current_algorithms} and only {', '.join(secure_encryption_algorithms)} is recommended." findings.append(report) return findings diff --git a/tests/providers/azure/azure_provider_test.py b/tests/providers/azure/azure_provider_test.py index 8f99df8637..4aa09644e4 100644 --- a/tests/providers/azure/azure_provider_test.py +++ b/tests/providers/azure/azure_provider_test.py @@ -86,6 +86,7 @@ class TestAzureProvider: "python_latest_version": "3.12", "java_latest_version": "17", "recommended_minimal_tls_versions": ["1.2", "1.3"], + "recommended_smb_channel_encryption_algorithms": ["AES-256-GCM"], "vm_backup_min_daily_retention_days": 7, "desired_vm_sku_sizes": [ "Standard_A8_v2", diff --git a/tests/providers/azure/services/storage/storage_smb_channel_encryption_with_secure_algorithm/storage_smb_channel_encryption_with_secure_algorithm_test.py b/tests/providers/azure/services/storage/storage_smb_channel_encryption_with_secure_algorithm/storage_smb_channel_encryption_with_secure_algorithm_test.py index 49bc32ba26..e213080d05 100644 --- a/tests/providers/azure/services/storage/storage_smb_channel_encryption_with_secure_algorithm/storage_smb_channel_encryption_with_secure_algorithm_test.py +++ b/tests/providers/azure/services/storage/storage_smb_channel_encryption_with_secure_algorithm/storage_smb_channel_encryption_with_secure_algorithm_test.py @@ -20,6 +20,7 @@ class Test_storage_smb_channel_encryption_with_secure_algorithm: def test_no_storage_accounts(self): storage_client = mock.MagicMock() storage_client.subscriptions = {AZURE_SUBSCRIPTION_ID: AZURE_SUBSCRIPTION_NAME} + storage_client.audit_config = {} storage_client.storage_accounts = {} with ( mock.patch( @@ -44,6 +45,7 @@ class Test_storage_smb_channel_encryption_with_secure_algorithm: storage_account_name = "Test Storage Account" storage_client = mock.MagicMock() storage_client.subscriptions = {AZURE_SUBSCRIPTION_ID: AZURE_SUBSCRIPTION_NAME} + storage_client.audit_config = {} storage_client.storage_accounts = { AZURE_SUBSCRIPTION_ID: [ Account( @@ -97,6 +99,7 @@ class Test_storage_smb_channel_encryption_with_secure_algorithm: ) storage_client = mock.MagicMock() storage_client.subscriptions = {AZURE_SUBSCRIPTION_ID: AZURE_SUBSCRIPTION_NAME} + storage_client.audit_config = {} storage_client.storage_accounts = { AZURE_SUBSCRIPTION_ID: [ Account( @@ -154,6 +157,7 @@ class Test_storage_smb_channel_encryption_with_secure_algorithm: ) storage_client = mock.MagicMock() storage_client.subscriptions = {AZURE_SUBSCRIPTION_ID: AZURE_SUBSCRIPTION_NAME} + storage_client.audit_config = {} storage_client.storage_accounts = { AZURE_SUBSCRIPTION_ID: [ Account( @@ -194,7 +198,7 @@ class Test_storage_smb_channel_encryption_with_secure_algorithm: assert len(result) == 1 assert result[0].status == "FAIL" assert result[0].status_extended == ( - f"Storage account {storage_account_name} from subscription {AZURE_SUBSCRIPTION_DISPLAY} does not have SMB channel encryption with a secure algorithm for file shares since it supports AES-128-GCM." + f"Storage account {storage_account_name} from subscription {AZURE_SUBSCRIPTION_DISPLAY} allows insecure algorithms for SMB channel encryption on file shares since it supports AES-128-GCM and only AES-256-GCM is recommended." ) def test_recommended_encryption(self): @@ -211,6 +215,7 @@ class Test_storage_smb_channel_encryption_with_secure_algorithm: ) storage_client = mock.MagicMock() storage_client.subscriptions = {AZURE_SUBSCRIPTION_ID: AZURE_SUBSCRIPTION_NAME} + storage_client.audit_config = {} storage_client.storage_accounts = { AZURE_SUBSCRIPTION_ID: [ Account( @@ -251,5 +256,126 @@ class Test_storage_smb_channel_encryption_with_secure_algorithm: assert len(result) == 1 assert result[0].status == "PASS" assert result[0].status_extended == ( - f"Storage account {storage_account_name} from subscription {AZURE_SUBSCRIPTION_DISPLAY} has a secure algorithm for SMB channel encryption (AES-256-GCM) enabled for file shares since it supports AES-256-GCM." + f"Storage account {storage_account_name} from subscription {AZURE_SUBSCRIPTION_DISPLAY} only allows secure algorithms for SMB channel encryption on file shares since it supports AES-256-GCM." + ) + + def test_recommended_algorithm_mixed_with_weak_algorithm(self): + storage_account_id = str(uuid4()) + storage_account_name = "Test Storage Account" + file_service_properties = FileServiceProperties( + id="id1", + name="fs1", + type="type1", + share_delete_retention_policy=DeleteRetentionPolicy(enabled=True, days=7), + smb_protocol_settings=SMBProtocolSettings( + channel_encryption=["AES-128-CCM", "AES-256-GCM"], supported_versions=[] + ), + ) + storage_client = mock.MagicMock() + storage_client.subscriptions = {AZURE_SUBSCRIPTION_ID: AZURE_SUBSCRIPTION_NAME} + storage_client.audit_config = {} + storage_client.storage_accounts = { + AZURE_SUBSCRIPTION_ID: [ + Account( + id=storage_account_id, + name=storage_account_name, + resouce_group_name="rg", + enable_https_traffic_only=False, + infrastructure_encryption=False, + allow_blob_public_access=False, + network_rule_set=NetworkRuleSet( + bypass="AzureServices", default_action="Allow" + ), + encryption_type="None", + minimum_tls_version="TLS1_2", + key_expiration_period_in_days=None, + location="westeurope", + private_endpoint_connections=[], + file_service_properties=file_service_properties, + ) + ] + } + 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_smb_channel_encryption_with_secure_algorithm.storage_smb_channel_encryption_with_secure_algorithm.storage_client", + new=storage_client, + ), + ): + from prowler.providers.azure.services.storage.storage_smb_channel_encryption_with_secure_algorithm.storage_smb_channel_encryption_with_secure_algorithm import ( + storage_smb_channel_encryption_with_secure_algorithm, + ) + + check = storage_smb_channel_encryption_with_secure_algorithm() + 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_SUBSCRIPTION_DISPLAY} allows insecure algorithms for SMB channel encryption on file shares since it supports AES-128-CCM, AES-256-GCM and only AES-256-GCM is recommended." + ) + + def test_custom_recommended_algorithms_from_config(self): + storage_account_id = str(uuid4()) + storage_account_name = "Test Storage Account" + file_service_properties = FileServiceProperties( + id="id1", + name="fs1", + type="type1", + share_delete_retention_policy=DeleteRetentionPolicy(enabled=True, days=7), + smb_protocol_settings=SMBProtocolSettings( + channel_encryption=["AES-128-GCM", "AES-256-GCM"], supported_versions=[] + ), + ) + storage_client = mock.MagicMock() + storage_client.subscriptions = {AZURE_SUBSCRIPTION_ID: AZURE_SUBSCRIPTION_NAME} + storage_client.audit_config = { + "recommended_smb_channel_encryption_algorithms": [ + "AES-128-GCM", + "AES-256-GCM", + ] + } + storage_client.storage_accounts = { + AZURE_SUBSCRIPTION_ID: [ + Account( + id=storage_account_id, + name=storage_account_name, + resouce_group_name="rg", + enable_https_traffic_only=False, + infrastructure_encryption=False, + allow_blob_public_access=False, + network_rule_set=NetworkRuleSet( + bypass="AzureServices", default_action="Allow" + ), + encryption_type="None", + minimum_tls_version="TLS1_2", + key_expiration_period_in_days=None, + location="westeurope", + private_endpoint_connections=[], + file_service_properties=file_service_properties, + ) + ] + } + 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_smb_channel_encryption_with_secure_algorithm.storage_smb_channel_encryption_with_secure_algorithm.storage_client", + new=storage_client, + ), + ): + from prowler.providers.azure.services.storage.storage_smb_channel_encryption_with_secure_algorithm.storage_smb_channel_encryption_with_secure_algorithm import ( + storage_smb_channel_encryption_with_secure_algorithm, + ) + + check = storage_smb_channel_encryption_with_secure_algorithm() + 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_SUBSCRIPTION_DISPLAY} only allows secure algorithms for SMB channel encryption on file shares since it supports AES-128-GCM, AES-256-GCM." )