From b4a05f4be0532c2cd3271322d67a777206dd77be Mon Sep 17 00:00:00 2001 From: Hugo966 <148140670+Hugo966@users.noreply.github.com> Date: Fri, 1 Mar 2024 13:38:33 +0100 Subject: [PATCH] feat(azure): new monitoring check ensuring storage account with logs private (#3453) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Hugo Gálvez Ureña --- .../azure/services/monitor/monitor_service.py | 4 + .../__init__.py | 0 ...ith_activity_logs_is_private.metadata.json | 30 ++++ ...e_account_with_activity_logs_is_private.py | 32 ++++ ...etting_with_appropriate_categories_test.py | 4 +- .../services/monitor/monitor_service_test.py | 1 + ...ount_with_activity_logs_is_private_test.py | 158 ++++++++++++++++++ 7 files changed, 228 insertions(+), 1 deletion(-) create mode 100644 prowler/providers/azure/services/monitor/monitor_storage_account_with_activity_logs_is_private/__init__.py create mode 100644 prowler/providers/azure/services/monitor/monitor_storage_account_with_activity_logs_is_private/monitor_storage_account_with_activity_logs_is_private.metadata.json create mode 100644 prowler/providers/azure/services/monitor/monitor_storage_account_with_activity_logs_is_private/monitor_storage_account_with_activity_logs_is_private.py create mode 100644 tests/providers/azure/services/monitor/monitor_storage_account_with_activity_logs_is_private/monitor_storage_account_with_activity_logs_is_private_test.py diff --git a/prowler/providers/azure/services/monitor/monitor_service.py b/prowler/providers/azure/services/monitor/monitor_service.py index 4359ed53..7570cd5a 100644 --- a/prowler/providers/azure/services/monitor/monitor_service.py +++ b/prowler/providers/azure/services/monitor/monitor_service.py @@ -27,6 +27,9 @@ class Monitor(AzureService): diagnostics_settings[subscription].append( DiagnosticSetting( id=setting.id, + storage_account_name=setting.storage_account_id.split("/")[ + -1 + ], logs=setting.logs, storage_account_id=setting.storage_account_id, ) @@ -43,4 +46,5 @@ class Monitor(AzureService): class DiagnosticSetting: id: str storage_account_id: str + storage_account_name: str logs: LogSettings diff --git a/prowler/providers/azure/services/monitor/monitor_storage_account_with_activity_logs_is_private/__init__.py b/prowler/providers/azure/services/monitor/monitor_storage_account_with_activity_logs_is_private/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/prowler/providers/azure/services/monitor/monitor_storage_account_with_activity_logs_is_private/monitor_storage_account_with_activity_logs_is_private.metadata.json b/prowler/providers/azure/services/monitor/monitor_storage_account_with_activity_logs_is_private/monitor_storage_account_with_activity_logs_is_private.metadata.json new file mode 100644 index 00000000..7f80273b --- /dev/null +++ b/prowler/providers/azure/services/monitor/monitor_storage_account_with_activity_logs_is_private/monitor_storage_account_with_activity_logs_is_private.metadata.json @@ -0,0 +1,30 @@ +{ + "Provider": "azure", + "CheckID": "monitor_storage_container_with_activity_logs_is_private", + "CheckTitle": "Ensure the Storage Container Storing the Activity Logs is not Publicly Accessible", + "CheckType": [], + "ServiceName": "monitor", + "SubServiceName": "", + "ResourceIdTemplate": "", + "Severity": "high", + "ResourceType": "Monitor", + "Description": "The storage account container containing the activity log export should not be publicly accessible.", + "Risk": "Allowing public access to activity log content may aid an adversary in identifying weaknesses in the affected account's use or configuration.", + "RelatedUrl": "https://learn.microsoft.com/en-us/azure/azure-monitor/essentials/diagnostic-settings", + "Remediation": { + "Code": { + "CLI": "az storage container set-permission --name insights-activity-logs --account-name --public-access off", + "NativeIaC": "", + "Other": "https://www.trendmicro.com/cloudoneconformity-staging/knowledge-base/azure/Monitor/check-for-publicly-accessible-activity-log-storage-container.html", + "Terraform": "https://docs.bridgecrew.io/docs/ensure-the-storage-container-storing-the-activity-logs-is-not-publicly-accessible#terraform" + }, + "Recommendation": { + "Text": "1. From Azure Home select the Portal Menu 2. Search for Storage Accounts to access Storage account blade 3. Click on the storage account name 4. Click on Configuration under settings 5. Select Enabled under 'Allow Blob public access'", + "Url": "https://docs.microsoft.com/en-us/security/benchmark/azure/security-controls-v3-network-security#ns-2-secure-cloud-services-with-network-controls" + } + }, + "Categories": [], + "DependsOn": [], + "RelatedTo": [], + "Notes": "Configuring container Access policy to private will remove access from the container for everyone except owners of the storage account. Access policy needs to be set explicitly in order to allow access to other desired users." +} diff --git a/prowler/providers/azure/services/monitor/monitor_storage_account_with_activity_logs_is_private/monitor_storage_account_with_activity_logs_is_private.py b/prowler/providers/azure/services/monitor/monitor_storage_account_with_activity_logs_is_private/monitor_storage_account_with_activity_logs_is_private.py new file mode 100644 index 00000000..8e57808a --- /dev/null +++ b/prowler/providers/azure/services/monitor/monitor_storage_account_with_activity_logs_is_private/monitor_storage_account_with_activity_logs_is_private.py @@ -0,0 +1,32 @@ +from prowler.lib.check.models import Check, Check_Report_Azure +from prowler.providers.azure.services.monitor.monitor_client import monitor_client +from prowler.providers.azure.services.storage.storage_client import storage_client + + +class monitor_storage_account_with_activity_logs_is_private(Check): + def execute(self) -> Check_Report_Azure: + findings = [] + + for ( + subscription_name, + diagnostic_settings, + ) in monitor_client.diagnostics_settings.items(): + for diagnostic_setting in diagnostic_settings: + for storage_account in storage_client.storage_accounts[ + subscription_name + ]: + if storage_account.name == diagnostic_setting.storage_account_name: + report = Check_Report_Azure(self.metadata()) + report.subscription = subscription_name + report.resource_name = storage_account.name + report.resource_id = storage_account.id + if storage_account.allow_blob_public_access: + report.status = "FAIL" + report.status_extended = f"Blob public access enabled in storage account {storage_account.name} storing activity logs in subscription {subscription_name}." + else: + report.status = "PASS" + report.status_extended = f"Blob public access disabled in storage account {storage_account.name} storing activity logs in subscription {subscription_name}." + + findings.append(report) + + return findings diff --git a/tests/providers/azure/services/monitor/monitor_diagnostic_setting_with_appropriate_categories/monitor_diagnostic_setting_with_appropriate_categories_test.py b/tests/providers/azure/services/monitor/monitor_diagnostic_setting_with_appropriate_categories/monitor_diagnostic_setting_with_appropriate_categories_test.py index 047e8cd2..1458d06d 100644 --- a/tests/providers/azure/services/monitor/monitor_diagnostic_setting_with_appropriate_categories/monitor_diagnostic_setting_with_appropriate_categories_test.py +++ b/tests/providers/azure/services/monitor/monitor_diagnostic_setting_with_appropriate_categories/monitor_diagnostic_setting_with_appropriate_categories_test.py @@ -64,6 +64,7 @@ class Test_monitor_diagnostic_setting_with_appropriate_categories: mock.MagicMock(category="ResourceHealth", enabled=False), ], storage_account_id="/subscriptions/1234a5-123a-123a-123a-1234567890ab/resourceGroups/rg/providers/Microsoft.Storage/storageAccounts/storageaccountname", + storage_account_name="storageaccountname", ), DiagnosticSetting( id="id2", @@ -77,7 +78,8 @@ class Test_monitor_diagnostic_setting_with_appropriate_categories: mock.MagicMock(category="Autoscale", enabled=False), mock.MagicMock(category="ResourceHealth", enabled=False), ], - storage_account_id="/subscriptions/1224a5-123a-123a-123a-1234567890ab/resourceGroups/rg/providers/Microsoft.Storage/storageAccounts/storageaccountname", + storage_account_id="/subscriptions/1224a5-123a-123a-123a-1234567890ab/resourceGroups/rg/providers/Microsoft.Storage/storageAccounts/storageaccountname2", + storage_account_name="storageaccountname2", ), ] } diff --git a/tests/providers/azure/services/monitor/monitor_service_test.py b/tests/providers/azure/services/monitor/monitor_service_test.py index ee935ca1..6339a8a5 100644 --- a/tests/providers/azure/services/monitor/monitor_service_test.py +++ b/tests/providers/azure/services/monitor/monitor_service_test.py @@ -27,6 +27,7 @@ def mock_monitor_get_diagnostics_settings(_): mock.MagicMock(category="ResourceHealth", enabled=False), ], storage_account_id="/subscriptions/1234a5-123a-123a-123a-1234567890ab/resourceGroups/rg/providers/Microsoft.Storage/storageAccounts/storageaccountname", + storage_account_name="storageaccountname", ) ] } diff --git a/tests/providers/azure/services/monitor/monitor_storage_account_with_activity_logs_is_private/monitor_storage_account_with_activity_logs_is_private_test.py b/tests/providers/azure/services/monitor/monitor_storage_account_with_activity_logs_is_private/monitor_storage_account_with_activity_logs_is_private_test.py new file mode 100644 index 00000000..366ab875 --- /dev/null +++ b/tests/providers/azure/services/monitor/monitor_storage_account_with_activity_logs_is_private/monitor_storage_account_with_activity_logs_is_private_test.py @@ -0,0 +1,158 @@ +from unittest import mock + +from prowler.providers.azure.services.monitor.monitor_service import DiagnosticSetting +from prowler.providers.azure.services.storage.storage_service import Account +from tests.providers.azure.azure_fixtures import AZURE_SUBSCRIPTION + + +class Test_monitor_storage_account_with_activity_logs_is_private: + def test_monitor_storage_account_with_activity_logs_is_private_no_subscriptions( + self, + ): + monitor_client = mock.MagicMock + monitor_client.diagnostics_settings = {} + + with mock.patch( + "prowler.providers.azure.services.monitor.monitor_storage_account_with_activity_logs_is_private.monitor_storage_account_with_activity_logs_is_private.monitor_client", + new=monitor_client, + ): + from prowler.providers.azure.services.monitor.monitor_storage_account_with_activity_logs_is_private.monitor_storage_account_with_activity_logs_is_private import ( + monitor_storage_account_with_activity_logs_is_private, + ) + + check = monitor_storage_account_with_activity_logs_is_private() + result = check.execute() + assert len(result) == 0 + + def test_no_diagnostic_settings(self): + monitor_client = mock.MagicMock + monitor_client.diagnostics_settings = {AZURE_SUBSCRIPTION: []} + with mock.patch( + "prowler.providers.azure.services.monitor.monitor_storage_account_with_activity_logs_is_private.monitor_storage_account_with_activity_logs_is_private.monitor_client", + new=monitor_client, + ): + from prowler.providers.azure.services.monitor.monitor_storage_account_with_activity_logs_is_private.monitor_storage_account_with_activity_logs_is_private import ( + monitor_storage_account_with_activity_logs_is_private, + ) + + check = monitor_storage_account_with_activity_logs_is_private() + result = check.execute() + assert len(result) == 0 + + def test_diagnostic_settings_configured(self): + monitor_client = mock.MagicMock + storage_client = mock.MagicMock + monitor_client.diagnostics_settings = { + AZURE_SUBSCRIPTION: [ + DiagnosticSetting( + id="id", + logs=[ + mock.MagicMock(category="Administrative", enabled=True), + mock.MagicMock(category="Security", enabled=True), + mock.MagicMock(category="ServiceHealth", enabled=False), + mock.MagicMock(category="Alert", enabled=True), + mock.MagicMock(category="Recommendation", enabled=False), + mock.MagicMock(category="Policy", enabled=True), + mock.MagicMock(category="Autoscale", enabled=False), + ], + storage_account_id="/subscriptions/1234a5-123a-123a-123a-1234567890ab/resourceGroups/rg/providers/Microsoft.Storage/storageAccounts/storageaccountname1", + storage_account_name="storageaccountname1", + ), + DiagnosticSetting( + id="id2", + logs=[ + mock.MagicMock(category="Administrative", enabled=True), + mock.MagicMock(category="Security", enabled=True), + mock.MagicMock(category="ServiceHealth", enabled=False), + mock.MagicMock(category="Alert", enabled=True), + mock.MagicMock(category="Recommendation", enabled=False), + mock.MagicMock(category="Policy", enabled=True), + mock.MagicMock(category="Autoscale", enabled=False), + ], + storage_account_id="/subscriptions/1224a5-123a-123a-123a-1234567890ab/resourceGroups/rg/providers/Microsoft.Storage/storageAccounts/storageaccountname2", + storage_account_name="storageaccountname2", + ), + ] + } + storage_client.storage_accounts = { + AZURE_SUBSCRIPTION: [ + Account( + id="/subscriptions/1234a5-123a-123a-123a-1234567890ab/resourceGroups/rg/providers/Microsoft.Storage/storageAccounts/storageaccountname1", + name="storageaccountname1", + resouce_group_name="rg", + enable_https_traffic_only=True, + infrastructure_encryption="Enabled", + allow_blob_public_access=True, + network_rule_set="AllowAll", + encryption_type="Microsoft.Storage", + minimum_tls_version="TLS1_2", + private_endpoint_connections=[], + key_expiration_period_in_days=365, + blob_properties=mock.MagicMock( + id="id", + name="name", + type="type", + default_service_version="default_service_version", + container_delete_retention_policy="container_delete_retention_policy", + ), + ), + Account( + id="/subscriptions/1224a5-123a-123a-123a-1234567890ab/resourceGroups/rg/providers/Microsoft.Storage/storageAccounts/storageaccountname2", + name="storageaccountname2", + resouce_group_name="rg", + enable_https_traffic_only=False, + infrastructure_encryption="Enabled", + allow_blob_public_access=False, + network_rule_set="AllowAll", + encryption_type="Microsoft.Storage", + minimum_tls_version="TLS1_2", + private_endpoint_connections=[], + key_expiration_period_in_days=365, + blob_properties=mock.MagicMock( + id="id", + name="name", + type="type", + default_service_version="default_service_version", + container_delete_retention_policy="container_delete_retention_policy", + ), + ), + ] + } + + with mock.patch( + "prowler.providers.azure.services.monitor.monitor_storage_account_with_activity_logs_is_private.monitor_storage_account_with_activity_logs_is_private.monitor_client", + new=monitor_client, + ): + with mock.patch( + "prowler.providers.azure.services.monitor.monitor_storage_account_with_activity_logs_is_private.monitor_storage_account_with_activity_logs_is_private.storage_client", + new=storage_client, + ): + from prowler.providers.azure.services.monitor.monitor_storage_account_with_activity_logs_is_private.monitor_storage_account_with_activity_logs_is_private import ( + monitor_storage_account_with_activity_logs_is_private, + ) + + check = monitor_storage_account_with_activity_logs_is_private() + result = check.execute() + assert len(result) == 2 + assert result[0].subscription == AZURE_SUBSCRIPTION + assert result[0].status == "FAIL" + assert ( + result[0].resource_id + == "/subscriptions/1234a5-123a-123a-123a-1234567890ab/resourceGroups/rg/providers/Microsoft.Storage/storageAccounts/storageaccountname1" + ) + assert result[0].resource_name == "storageaccountname1" + assert ( + result[0].status_extended + == f"Blob public access enabled in storage account {storage_client.storage_accounts[AZURE_SUBSCRIPTION][0].name} storing activity logs in subscription {AZURE_SUBSCRIPTION}." + ) + assert result[1].subscription == AZURE_SUBSCRIPTION + assert result[1].status == "PASS" + assert ( + result[1].resource_id + == "/subscriptions/1224a5-123a-123a-123a-1234567890ab/resourceGroups/rg/providers/Microsoft.Storage/storageAccounts/storageaccountname2" + ) + assert result[1].resource_name == "storageaccountname2" + assert ( + result[1].status_extended + == f"Blob public access disabled in storage account {storage_client.storage_accounts[AZURE_SUBSCRIPTION][1].name} storing activity logs in subscription {AZURE_SUBSCRIPTION}." + )