diff --git a/docs/en/rules/Azure.Fleet.SecureBoot.md b/docs/en/rules/Azure.Fleet.SecureBoot.md new file mode 100644 index 00000000000..c089c290761 --- /dev/null +++ b/docs/en/rules/Azure.Fleet.SecureBoot.md @@ -0,0 +1,104 @@ +--- +reviewed: 2026-07-14 +severity: Important +pillar: Security +category: SE:08 Hardening resources +resource: Azure Fleet +resourceType: Microsoft.AzureFleet/fleets +online version: https://azure.github.io/PSRule.Rules.Azure/en/rules/Azure.Fleet.SecureBoot/ +--- + +# Azure Fleet Secure Boot is not enabled + +## SYNOPSIS + +Operating systems or drivers may be maliciously modified or injected if an actor gains access to VM/ OS storage or build media. + +## DESCRIPTION + +Azure Fleets deploy and manage virtual machines at scale using a base virtual machine profile. +A malicious actor may attempt to tamper or inject operating system and driver components to gain access to resources and persist between reboots. + +When a fleet VM is started, Azure is able to verify if: + +1. The operating system and drivers originate from a trusted source. +2. These components are in their original unaltered state. + +Azure is able to perform this verification by Secure Boot and Trusted Launch features. +These features verify the cryptographic signatures of early boot components before they start. + +Secure Boot and Trusted Launch are on by default for many configurations. +However, if you are running an older configuration these features may need to be enabled. + +## RECOMMENDATION + +Consider enabling Trusted Launch or Confidential VM with Secure Boot for Azure Fleet base virtual machine profiles to protect against boot-level attacks. + +## EXAMPLES + +### Configure with Bicep + +To deploy Azure Fleets that pass this rule: + +- Set the `properties.computeProfile.baseVirtualMachineProfile.securityProfile.securityType` property to `TrustedLaunch` or `ConfidentialVM`. +- Set the `properties.computeProfile.baseVirtualMachineProfile.securityProfile.uefiSettings.secureBootEnabled` property to `true`. + +For example: + +```bicep +resource fleet 'Microsoft.AzureFleet/fleets@2024-11-01' = { + name: name + location: location + properties: { + computeProfile: { + baseVirtualMachineProfile: { + securityProfile: { + securityType: 'TrustedLaunch' + uefiSettings: { + secureBootEnabled: true + vTpmEnabled: true + } + } + } + } + } +} +``` + +### Configure with Azure template + +To deploy Azure Fleets that pass this rule: + +- Set the `properties.computeProfile.baseVirtualMachineProfile.securityProfile.securityType` property to `TrustedLaunch` or `ConfidentialVM`. +- Set the `properties.computeProfile.baseVirtualMachineProfile.securityProfile.uefiSettings.secureBootEnabled` property to `true`. + +For example: + +```json +{ + "type": "Microsoft.AzureFleet/fleets", + "apiVersion": "2024-11-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "properties": { + "computeProfile": { + "baseVirtualMachineProfile": { + "securityProfile": { + "securityType": "TrustedLaunch", + "uefiSettings": { + "secureBootEnabled": true, + "vTpmEnabled": true + } + } + } + } + } +} +``` + +## LINKS + +- [SE:08 Hardening resources](https://learn.microsoft.com/security/benchmark/azure/security-baselines-platform#se-08-hardening-resources) +- [Trusted launch for Azure virtual machines](https://learn.microsoft.com/azure/virtual-machines/trusted-launch) +- [Security profile](https://learn.microsoft.com/azure/templates/microsoft.azurefleet/fleets#securityprofile) +- [Azure deployment reference](https://learn.microsoft.com/azure/templates/microsoft.azurefleet/fleets) diff --git a/src/PSRule.Rules.Azure/en/PSRule-rules.psd1 b/src/PSRule.Rules.Azure/en/PSRule-rules.psd1 index 890758609f7..06db5cdc5b5 100644 --- a/src/PSRule.Rules.Azure/en/PSRule-rules.psd1 +++ b/src/PSRule.Rules.Azure/en/PSRule-rules.psd1 @@ -79,6 +79,8 @@ ReplicaInSecondaryNotFound = "A replica in a secondary region was not found." VMSSPublicKey = "The virtual machine scale set '{0}' should have password authentication disabled." FleetPublicKey = "The Azure Fleet '{0}' should have password authentication disabled." + FleetSecureBoot = "The Azure Fleet '{0}' should set the 'securityType' property to 'TrustedLaunch' or 'ConfidentialVM'." + FleetSecureBootEnabled = "The Azure Fleet '{0}' should have Secure Boot enabled." ACRSoftDeletePolicy = "The container registry '{0}' should have soft delete policy enabled." ACRSoftDeletePolicyRetention = "The container registry '{0}' should have retention period value between one to 90 days for the soft delete policy." ContainerRegistryAuditDiagnosticSetting = "Minimum one diagnostic setting should have ({0}) configured or category group ({1}) configured." diff --git a/src/PSRule.Rules.Azure/rules/Azure.Fleet.Rule.ps1 b/src/PSRule.Rules.Azure/rules/Azure.Fleet.Rule.ps1 index b0f8c37903a..2f4ac50edb7 100644 --- a/src/PSRule.Rules.Azure/rules/Azure.Fleet.Rule.ps1 +++ b/src/PSRule.Rules.Azure/rules/Azure.Fleet.Rule.ps1 @@ -13,4 +13,13 @@ Rule 'Azure.Fleet.PublicKey' -Ref 'AZR-000541' -Type 'Microsoft.AzureFleet/fleet Reason($LocalizedData.FleetPublicKey, $PSRule.TargetName) } + +# Synopsis: Azure Fleets should use Trusted Launch with Secure Boot enabled. +Rule 'Azure.Fleet.SecureBoot' -Ref 'AZR-000545' -Type 'Microsoft.AzureFleet/fleets' -Tag @{ release = 'GA'; ruleSet = '2026_06'; 'Azure.WAF/pillar' = 'Security'; } -Labels @{ 'Azure.WAF/maturity' = 'L2' } { + $Assert.In($TargetObject, 'properties.computeProfile.baseVirtualMachineProfile.securityProfile.securityType', @('TrustedLaunch', 'ConfidentialVM')). + Reason($LocalizedData.FleetSecureBoot, $PSRule.TargetName) + $Assert.HasFieldValue($TargetObject, 'properties.computeProfile.baseVirtualMachineProfile.securityProfile.uefiSettings.secureBootEnabled', $True). + Reason($LocalizedData.FleetSecureBootEnabled, $PSRule.TargetName) +} + #endregion Rules diff --git a/tests/PSRule.Rules.Azure.Tests/Azure.Fleet.Tests.ps1 b/tests/PSRule.Rules.Azure.Tests/Azure.Fleet.Tests.ps1 index 2a73bdcdec8..d0a9d9888ed 100644 --- a/tests/PSRule.Rules.Azure.Tests/Azure.Fleet.Tests.ps1 +++ b/tests/PSRule.Rules.Azure.Tests/Azure.Fleet.Tests.ps1 @@ -56,5 +56,21 @@ Describe 'Azure.Fleet' -Tag 'Fleet' { $ruleResult.Length | Should -Be 1; $ruleResult.TargetName | Should -Be 'fleet-001'; } + + It 'Azure.Fleet.SecureBoot' { + $filteredResult = $result | Where-Object { $_.RuleName -eq 'Azure.Fleet.SecureBoot' }; + + # Fail + $ruleResult = @($filteredResult | Where-Object { $_.Outcome -eq 'Fail' }); + $ruleResult | Should -Not -BeNullOrEmpty; + $ruleResult.Length | Should -Be 3; + $ruleResult.TargetName | Should -BeIn 'fleet-002', 'fleet-003', 'fleet-004'; + + # Pass + $ruleResult = @($filteredResult | Where-Object { $_.Outcome -eq 'Pass' }); + $ruleResult | Should -Not -BeNullOrEmpty; + $ruleResult.Length | Should -Be 1; + $ruleResult.TargetName | Should -Be 'fleet-001'; + } } } diff --git a/tests/PSRule.Rules.Azure.Tests/Resources.Fleet.json b/tests/PSRule.Rules.Azure.Tests/Resources.Fleet.json index a5b25f8e378..c270ac4cd2f 100644 --- a/tests/PSRule.Rules.Azure.Tests/Resources.Fleet.json +++ b/tests/PSRule.Rules.Azure.Tests/Resources.Fleet.json @@ -64,6 +64,14 @@ } } ] + }, + "securityProfile": { + "securityType": "TrustedLaunch", + "encryptionAtHost": true, + "uefiSettings": { + "secureBootEnabled": true, + "vTpmEnabled": true + } } } }, @@ -253,14 +261,6 @@ "Properties": { "computeProfile": { "baseVirtualMachineProfile": { - "securityProfile": { - "securityType": "TrustedLaunch", - "encryptionAtHost": true, - "uefiSettings": { - "secureBootEnabled": true, - "vTpmEnabled": true - } - }, "osProfile": { "computerNamePrefix": "fleet", "adminUsername": "azureuser",