diff --git a/prowler/providers/azure/services/vm/vm_service.py b/prowler/providers/azure/services/vm/vm_service.py index 4a84f7cb8f..879b799167 100644 --- a/prowler/providers/azure/services/vm/vm_service.py +++ b/prowler/providers/azure/services/vm/vm_service.py @@ -31,6 +31,7 @@ class VirtualMachines(AzureService): resource_id=vm.id, resource_name=vm.name, storage_profile=getattr(vm, "storage_profile", None), + security_profile=vm.security_profile, ) } ) @@ -76,11 +77,24 @@ class VirtualMachines(AzureService): return disks +@dataclass +class UefiSettings: + secure_boot_enabled: bool + v_tpm_enabled: bool + + +@dataclass +class SecurityProfile: + security_type: str + uefi_settings: UefiSettings + + @dataclass class VirtualMachine: resource_id: str resource_name: str storage_profile: StorageProfile + security_profile: SecurityProfile @dataclass diff --git a/prowler/providers/azure/services/vm/vm_trusted_launch_enabled/__init__.py b/prowler/providers/azure/services/vm/vm_trusted_launch_enabled/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/prowler/providers/azure/services/vm/vm_trusted_launch_enabled/vm_trusted_launch_enabled.metadata.json b/prowler/providers/azure/services/vm/vm_trusted_launch_enabled/vm_trusted_launch_enabled.metadata.json new file mode 100644 index 0000000000..6b0222968b --- /dev/null +++ b/prowler/providers/azure/services/vm/vm_trusted_launch_enabled/vm_trusted_launch_enabled.metadata.json @@ -0,0 +1,30 @@ +{ + "Provider": "azure", + "CheckID": "vm_trusted_launch_enabled", + "CheckTitle": "Ensure Trusted Launch is enabled on Virtual Machines", + "CheckType": [], + "ServiceName": "vm", + "SubServiceName": "", + "ResourceIdTemplate": "", + "Severity": "high", + "ResourceType": "Microsoft.Compute/virtualMachines", + "Description": "When Secure Boot and vTPM are enabled together, they provide a strong foundation for protecting your VM from boot attacks. For example, if an attacker attempts to replace the bootloader with a malicious version, Secure Boot will prevent the VM from booting. If the attacker is able to bypass Secure Boot and install a malicious bootloader, vTPM can be used to detect the intrusion and alert you.", + "Risk": "Secure Boot and vTPM work together to protect your VM from a variety of boot attacks, including bootkits, rootkits, and firmware rootkits. Not enabling Trusted Launch in Azure VM can lead to increased vulnerability to rootkits and boot-level malware, reduced ability to detect and prevent unauthorized changes to the boot process, and a potential compromise of system integrity and data security.", + "RelatedUrl": "https://learn.microsoft.com/en-us/azure/virtual-machines/trusted-launch-existing-vm?tabs=portal", + "Remediation": { + "Code": { + "CLI": "", + "NativeIaC": "", + "Other": "", + "Terraform": "" + }, + "Recommendation": { + "Text": "1. Go to Virtual Machines 2. For each VM, under Settings, click on Configuration on the left blade 3. Under Security Type, select 'Trusted Launch Virtual Machines' 4. Make sure Enable Secure Boot & Enable vTPM are checked 5. Click on Apply.", + "Url": "https://learn.microsoft.com/en-us/azure/virtual-machines/trusted-launch-existing-vm?tabs=portal#enable-trusted-launch-on-existing-vm" + } + }, + "Categories": [], + "DependsOn": [], + "RelatedTo": [], + "Notes": "Secure Boot and vTPM are not currently supported for Azure Generation 1 VMs. IMPORTANT: Before enabling Secure Boot and vTPM on a Generation 2 VM which does not already have both enabled, it is highly recommended to create a restore point of the VM prior to remediation." +} diff --git a/prowler/providers/azure/services/vm/vm_trusted_launch_enabled/vm_trusted_launch_enabled.py b/prowler/providers/azure/services/vm/vm_trusted_launch_enabled/vm_trusted_launch_enabled.py new file mode 100644 index 0000000000..9c26d87d9c --- /dev/null +++ b/prowler/providers/azure/services/vm/vm_trusted_launch_enabled/vm_trusted_launch_enabled.py @@ -0,0 +1,28 @@ +from prowler.lib.check.models import Check, Check_Report_Azure +from prowler.providers.azure.services.vm.vm_client import vm_client + + +class vm_trusted_launch_enabled(Check): + def execute(self) -> Check_Report_Azure: + findings = [] + + for subscription_name, vms in vm_client.virtual_machines.items(): + for vm_id, vm in vms.items(): + report = Check_Report_Azure(self.metadata()) + report.status = "FAIL" + report.subscription = subscription_name + report.resource_name = vm.resource_name + report.resource_id = vm_id + report.status_extended = f"VM {vm.resource_name} has trusted launch disabled in subscription {subscription_name}" + + if ( + vm.security_profile.security_type == "TrustedLaunch" + and vm.security_profile.uefi_settings.secure_boot_enabled + and vm.security_profile.uefi_settings.v_tpm_enabled + ): + report.status = "PASS" + report.status_extended = f"VM {vm.resource_name} has trusted launch enabled in subscription {subscription_name}" + + findings.append(report) + + return findings diff --git a/tests/providers/azure/services/vm/vm_ensure_using_managed_disks/vm_ensure_using_managed_disks_test.py b/tests/providers/azure/services/vm/vm_ensure_using_managed_disks/vm_ensure_using_managed_disks_test.py index 4c0c51b6b4..598ca257a5 100644 --- a/tests/providers/azure/services/vm/vm_ensure_using_managed_disks/vm_ensure_using_managed_disks_test.py +++ b/tests/providers/azure/services/vm/vm_ensure_using_managed_disks/vm_ensure_using_managed_disks_test.py @@ -46,6 +46,13 @@ class Test_vm_ensure_using_managed_disks: vm_id: VirtualMachine( resource_id="/subscriptions/resource_id", resource_name="VMTest", + security_profile=mock.MagicMock( + security_type="TrustedLaunch", + uefi_settings=mock.MagicMock( + secure_boot_enabled=True, + v_tpm_enabled=True, + ), + ), storage_profile=mock.MagicMock( os_disk=mock.MagicMock( create_option="FromImage", @@ -53,7 +60,7 @@ class Test_vm_ensure_using_managed_disks: ), data_disks=[], ), - ) + ), } } @@ -85,6 +92,13 @@ class Test_vm_ensure_using_managed_disks: vm_id: VirtualMachine( resource_id="/subscriptions/resource_id", resource_name="VMTest", + security_profile=mock.MagicMock( + security_type="TrustedLaunch", + uefi_settings=mock.MagicMock( + secure_boot_enabled=True, + v_tpm_enabled=True, + ), + ), storage_profile=mock.MagicMock( os_disk=mock.MagicMock( create_option="FromImage", @@ -124,6 +138,13 @@ class Test_vm_ensure_using_managed_disks: vm_id: VirtualMachine( resource_id="/subscriptions/resource_id", resource_name="VMTest", + security_profile=mock.MagicMock( + security_type="TrustedLaunch", + uefi_settings=mock.MagicMock( + secure_boot_enabled=True, + v_tpm_enabled=True, + ), + ), storage_profile=mock.MagicMock( os_disk=mock.MagicMock( create_option="FromImage", diff --git a/tests/providers/azure/services/vm/vm_service_test.py b/tests/providers/azure/services/vm/vm_service_test.py index d5d07e8775..6a6fc95597 100644 --- a/tests/providers/azure/services/vm/vm_service_test.py +++ b/tests/providers/azure/services/vm/vm_service_test.py @@ -1,3 +1,4 @@ +from unittest import mock from unittest.mock import patch from azure.mgmt.compute.models import ManagedDiskParameters, OSDisk, StorageProfile @@ -19,6 +20,13 @@ def mock_vm_get_virtual_machines(_): "vm_id-1": VirtualMachine( resource_id="/subscriptions/resource_id", resource_name="VMTest", + security_profile=mock.MagicMock( + security_type="TrustedLaunch", + uefi_settings=mock.MagicMock( + secure_boot_enabled=True, + v_tpm_enabled=True, + ), + ), storage_profile=StorageProfile( os_disk=OSDisk( create_option="FromImage", @@ -77,6 +85,24 @@ class Test_AppInsights_Service: ].resource_name == "VMTest" ) + assert ( + virtual_machines.virtual_machines[AZURE_SUBSCRIPTION][ + "vm_id-1" + ].security_profile.security_type + == "TrustedLaunch" + ) + assert ( + virtual_machines.virtual_machines[AZURE_SUBSCRIPTION][ + "vm_id-1" + ].security_profile.uefi_settings.secure_boot_enabled + is True + ) + assert ( + virtual_machines.virtual_machines[AZURE_SUBSCRIPTION][ + "vm_id-1" + ].security_profile.uefi_settings.v_tpm_enabled + is True + ) assert ( virtual_machines.virtual_machines[AZURE_SUBSCRIPTION][ "vm_id-1" diff --git a/tests/providers/azure/services/vm/vm_trusted_launch_enabled/vm_trusted_launch_enabled_test.py b/tests/providers/azure/services/vm/vm_trusted_launch_enabled/vm_trusted_launch_enabled_test.py new file mode 100644 index 0000000000..f9cabfe9af --- /dev/null +++ b/tests/providers/azure/services/vm/vm_trusted_launch_enabled/vm_trusted_launch_enabled_test.py @@ -0,0 +1,131 @@ +from unittest import mock +from uuid import uuid4 + +from prowler.providers.azure.services.vm.vm_service import VirtualMachine +from tests.providers.azure.azure_fixtures import AZURE_SUBSCRIPTION + + +class Test_vm_trusted_launch_enabled: + def test_vm_no_subscriptions(self): + vm_client = mock.MagicMock + vm_client.virtual_machines = {} + + with mock.patch( + "prowler.providers.azure.services.vm.vm_trusted_launch_enabled.vm_trusted_launch_enabled.vm_client", + new=vm_client, + ): + from prowler.providers.azure.services.vm.vm_trusted_launch_enabled.vm_trusted_launch_enabled import ( + vm_trusted_launch_enabled, + ) + + check = vm_trusted_launch_enabled() + result = check.execute() + assert len(result) == 0 + + def test_vm_no_vm(self): + vm_client = mock.MagicMock + vm_client.virtual_machines = {AZURE_SUBSCRIPTION: {}} + + with mock.patch( + "prowler.providers.azure.services.vm.vm_trusted_launch_enabled.vm_trusted_launch_enabled.vm_client", + new=vm_client, + ): + from prowler.providers.azure.services.vm.vm_trusted_launch_enabled.vm_trusted_launch_enabled import ( + vm_trusted_launch_enabled, + ) + + check = vm_trusted_launch_enabled() + result = check.execute() + assert len(result) == 0 + + def test_vm_trusted_launch_enabled(self): + vm_id = str(uuid4()) + vm_client = mock.MagicMock + vm_client.virtual_machines = { + AZURE_SUBSCRIPTION: { + vm_id: VirtualMachine( + resource_id="/subscriptions/resource_id", + resource_name="VMTest", + security_profile=mock.MagicMock( + security_type="TrustedLaunch", + uefi_settings=mock.MagicMock( + secure_boot_enabled=True, + v_tpm_enabled=True, + ), + ), + storage_profile=mock.MagicMock( + os_disk=mock.MagicMock( + create_option="FromImage", + managed_disk=mock.MagicMock(id="managed_disk_id"), + ), + data_disks=[], + ), + ) + } + } + + with mock.patch( + "prowler.providers.azure.services.vm.vm_trusted_launch_enabled.vm_trusted_launch_enabled.vm_client", + new=vm_client, + ): + from prowler.providers.azure.services.vm.vm_trusted_launch_enabled.vm_trusted_launch_enabled import ( + vm_trusted_launch_enabled, + ) + + check = vm_trusted_launch_enabled() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "PASS" + assert result[0].subscription == AZURE_SUBSCRIPTION + assert result[0].resource_name == "VMTest" + assert result[0].resource_id == vm_id + assert ( + result[0].status_extended + == f"VM VMTest has trusted launch enabled in subscription {AZURE_SUBSCRIPTION}" + ) + + def test_vm_trusted_launch_disabled(self): + vm_id = str(uuid4()) + vm_client = mock.MagicMock + vm_client.virtual_machines = { + AZURE_SUBSCRIPTION: { + vm_id: VirtualMachine( + resource_id="/subscriptions/resource_id", + resource_name="VMTest", + security_profile=mock.MagicMock( + security_type="TrustedLaunch", + uefi_settings=mock.MagicMock( + secure_boot_enabled=False, + v_tpm_enabled=False, + ), + ), + storage_profile=mock.MagicMock( + os_disk=mock.MagicMock( + create_option="FromImage", + managed_disk=mock.MagicMock(id="managed_disk_id"), + ), + data_disks=[], + ), + ) + } + } + + with mock.patch( + "prowler.providers.azure.services.vm.vm_trusted_launch_enabled.vm_trusted_launch_enabled.vm_client", + new=vm_client, + ): + from prowler.providers.azure.services.vm.vm_trusted_launch_enabled.vm_trusted_launch_enabled import ( + vm_trusted_launch_enabled, + ) + + check = vm_trusted_launch_enabled() + result = check.execute() + assert len(result) == 1 + assert result[0].status == "FAIL" + assert result[0].subscription == AZURE_SUBSCRIPTION + assert result[0].resource_name == "VMTest" + assert result[0].resource_id == vm_id + assert ( + result[0].status_extended + == f"VM VMTest has trusted launch disabled in subscription {AZURE_SUBSCRIPTION}" + )