From d7fbcce48c9b0b6cd59be30fc2a8b30305638753 Mon Sep 17 00:00:00 2001 From: Hugo966 <148140670+Hugo966@users.noreply.github.com> Date: Fri, 15 Mar 2024 14:09:32 +0100 Subject: [PATCH] feat(azure): New check related with diagnostics settings in subscriptions (#3539) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Hugo Gálvez Ureña --- .../__init__.py | 0 ...r_diagnostic_settings_exists.metadata.json | 30 ++++ .../monitor_diagnostic_settings_exists.py | 27 ++++ ...monitor_diagnostic_settings_exists_test.py | 149 ++++++++++++++++++ 4 files changed, 206 insertions(+) create mode 100644 prowler/providers/azure/services/monitor/monitor_diagnostic_settings_exists/__init__.py create mode 100644 prowler/providers/azure/services/monitor/monitor_diagnostic_settings_exists/monitor_diagnostic_settings_exists.metadata.json create mode 100644 prowler/providers/azure/services/monitor/monitor_diagnostic_settings_exists/monitor_diagnostic_settings_exists.py create mode 100644 tests/providers/azure/services/monitor/monitor_diagnostic_settings_exists/monitor_diagnostic_settings_exists_test.py diff --git a/prowler/providers/azure/services/monitor/monitor_diagnostic_settings_exists/__init__.py b/prowler/providers/azure/services/monitor/monitor_diagnostic_settings_exists/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/prowler/providers/azure/services/monitor/monitor_diagnostic_settings_exists/monitor_diagnostic_settings_exists.metadata.json b/prowler/providers/azure/services/monitor/monitor_diagnostic_settings_exists/monitor_diagnostic_settings_exists.metadata.json new file mode 100644 index 0000000000..933be943c4 --- /dev/null +++ b/prowler/providers/azure/services/monitor/monitor_diagnostic_settings_exists/monitor_diagnostic_settings_exists.metadata.json @@ -0,0 +1,30 @@ +{ + "Provider": "azure", + "CheckID": "monitor_diagnostic_settings_exists", + "CheckTitle": "Ensure that a 'Diagnostic Setting' exists for Subscription Activity Logs ", + "CheckType": [], + "ServiceName": "monitor", + "SubServiceName": "", + "ResourceIdTemplate": "", + "Severity": "medium", + "ResourceType": "Monitor", + "Description": "Enable Diagnostic settings for exporting activity logs. Diagnostic settings are available for each individual resource within a subscription. Settings should be configured for all appropriate resources for your environment.", + "Risk": "A diagnostic setting controls how a diagnostic log is exported. By default, logs are retained only for 90 days. Diagnostic settings should be defined so that logs can be exported and stored for a longer duration in order to analyze security activities within an Azure subscription.", + "RelatedUrl": "https://learn.microsoft.com/en-us/cli/azure/monitor/diagnostic-settings?view=azure-cli-latest", + "Remediation": { + "Code": { + "CLI": "az monitor diagnostic-settings subscription create --subscription --name --location <[- -event-hub --event-hub-auth-rule ] [-- storage-account ] [--workspace ] --logs '' (e.g. [{category:Security,enabled:true},{category:Administrative,enabled:true},{cat egory:Alert,enabled:true},{category:Policy,enabled:true}])", + "NativeIaC": "", + "Other": "https://www.trendmicro.com/cloudoneconformity-staging/knowledge-base/azure/Monitor/subscription-activity-log-diagnostic-settings.html#trendmicro", + "Terraform": "" + }, + "Recommendation": { + "Text": "To enable Diagnostic Settings on a Subscription: 1. Go to Monitor 2. Click on Activity Log 3. Click on Export Activity Logs 4. Click + Add diagnostic setting 5. Enter a Diagnostic setting name 6. Select Categories for the diagnostic settings 7. Select the appropriate Destination details (this may be Log Analytics, Storage Account, Event Hub, or Partner solution) 8. Click Save To enable Diagnostic Settings on a specific resource: 1. Go to Monitor 2. Click Diagnostic settings 3. Click on the resource that has a diagnostics status of disabled 4. Select Add Diagnostic Setting 5. Enter a Diagnostic setting name 6. Select the appropriate log, metric, and destination. (this may be Log Analytics, Storage Account, Event Hub, or Partner solution) 7. Click save Repeat these step for all resources as needed.", + "Url": "https://docs.microsoft.com/en-us/azure/monitoring-and-diagnostics/monitoring-overview-activity-logs#export-the-activity-log-with-a-log-profile" + } + }, + "Categories": [], + "DependsOn": [], + "RelatedTo": [], + "Notes": "By default, diagnostic setting is not set." +} diff --git a/prowler/providers/azure/services/monitor/monitor_diagnostic_settings_exists/monitor_diagnostic_settings_exists.py b/prowler/providers/azure/services/monitor/monitor_diagnostic_settings_exists/monitor_diagnostic_settings_exists.py new file mode 100644 index 0000000000..e26d834868 --- /dev/null +++ b/prowler/providers/azure/services/monitor/monitor_diagnostic_settings_exists/monitor_diagnostic_settings_exists.py @@ -0,0 +1,27 @@ +from prowler.lib.check.models import Check, Check_Report_Azure +from prowler.providers.azure.services.monitor.monitor_client import monitor_client + + +class monitor_diagnostic_settings_exists(Check): + def execute(self) -> Check_Report_Azure: + findings = [] + + for ( + subscription_name, + diagnostic_settings, + ) in monitor_client.diagnostics_settings.items(): + report = Check_Report_Azure(self.metadata()) + report.subscription = subscription_name + report.status = "FAIL" + report.status_extended = ( + f"No diagnostic settings found in subscription {subscription_name}." + ) + if diagnostic_settings: + report.status = "PASS" + report.status_extended = ( + f"Diagnostic settings found in subscription {subscription_name}." + ) + + findings.append(report) + + return findings diff --git a/tests/providers/azure/services/monitor/monitor_diagnostic_settings_exists/monitor_diagnostic_settings_exists_test.py b/tests/providers/azure/services/monitor/monitor_diagnostic_settings_exists/monitor_diagnostic_settings_exists_test.py new file mode 100644 index 0000000000..a4033ce8d5 --- /dev/null +++ b/tests/providers/azure/services/monitor/monitor_diagnostic_settings_exists/monitor_diagnostic_settings_exists_test.py @@ -0,0 +1,149 @@ +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_diagnostic_settings_exists: + + def test_monitor_diagnostic_settings_exists_no_subscriptions( + self, + ): + monitor_client = mock.MagicMock + monitor_client.diagnostics_settings = {} + + with mock.patch( + "prowler.providers.azure.services.monitor.monitor_diagnostic_settings_exists.monitor_diagnostic_settings_exists.monitor_client", + new=monitor_client, + ): + from prowler.providers.azure.services.monitor.monitor_diagnostic_settings_exists.monitor_diagnostic_settings_exists import ( + monitor_diagnostic_settings_exists, + ) + + check = monitor_diagnostic_settings_exists() + 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_diagnostic_settings_exists.monitor_diagnostic_settings_exists.monitor_client", + new=monitor_client, + ): + from prowler.providers.azure.services.monitor.monitor_diagnostic_settings_exists.monitor_diagnostic_settings_exists import ( + monitor_diagnostic_settings_exists, + ) + + check = monitor_diagnostic_settings_exists() + result = check.execute() + assert len(result) == 1 + assert result[0].subscription == AZURE_SUBSCRIPTION + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"No diagnostic settings found in subscription {AZURE_SUBSCRIPTION}." + ) + + 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.CustomerManagedKeyVault", + 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_diagnostic_settings_exists.monitor_diagnostic_settings_exists.monitor_client", + new=monitor_client, + ): + with mock.patch( + "prowler.providers.azure.services.monitor.monitor_diagnostic_settings_exists.monitor_diagnostic_settings_exists.monitor_client", + new=monitor_client, + ): + from prowler.providers.azure.services.monitor.monitor_diagnostic_settings_exists.monitor_diagnostic_settings_exists import ( + monitor_diagnostic_settings_exists, + ) + + check = monitor_diagnostic_settings_exists() + result = check.execute() + assert len(result) == 1 + assert result[0].subscription == AZURE_SUBSCRIPTION + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == f"Diagnostic settings found in subscription {AZURE_SUBSCRIPTION}." + )