From f5b4b80d63f214bcf103188231d51c5da2b361e8 Mon Sep 17 00:00:00 2001 From: Jenna Goldstrich Date: Wed, 17 Apr 2024 09:44:01 -0700 Subject: [PATCH] Fix panics when trying to use confidential VMs or disk encryption sets with SIG Sourced Images (#407) --- ...ry_test.TestConfidentialVM03.approved.json | 207 ++++++++++++++++++ ...SourcedWithDiskEncryptionSet.approved.json | 200 +++++++++++++++++ builder/azure/arm/template_factory_test.go | 72 ++++++ .../azure/common/template/template_builder.go | 10 +- 4 files changed, 488 insertions(+), 1 deletion(-) create mode 100644 builder/azure/arm/template_factory_test.TestConfidentialVM03.approved.json create mode 100644 builder/azure/arm/template_factory_test.TestSigSourcedWithDiskEncryptionSet.approved.json diff --git a/builder/azure/arm/template_factory_test.TestConfidentialVM03.approved.json b/builder/azure/arm/template_factory_test.TestConfidentialVM03.approved.json new file mode 100644 index 00000000..aec97568 --- /dev/null +++ b/builder/azure/arm/template_factory_test.TestConfidentialVM03.approved.json @@ -0,0 +1,207 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "adminPassword": { + "type": "securestring" + }, + "adminUsername": { + "type": "string" + }, + "commandToExecute": { + "type": "string" + }, + "dataDiskName": { + "type": "string" + }, + "dnsNameForPublicIP": { + "type": "string" + }, + "nicName": { + "type": "string" + }, + "nsgName": { + "type": "string" + }, + "osDiskName": { + "type": "string" + }, + "publicIPAddressName": { + "type": "string" + }, + "storageAccountBlobEndpoint": { + "type": "string" + }, + "subnetName": { + "type": "string" + }, + "virtualNetworkName": { + "type": "string" + }, + "vmName": { + "type": "string" + }, + "vmSize": { + "type": "string" + } + }, + "resources": [ + { + "apiVersion": "[variables('networkApiVersion')]", + "location": "[variables('location')]", + "name": "[parameters('publicIPAddressName')]", + "properties": { + "dnsSettings": { + "domainNameLabel": "[parameters('dnsNameForPublicIP')]" + }, + "publicIPAllocationMethod": "[variables('publicIPAddressType')]" + }, + "type": "Microsoft.Network/publicIPAddresses" + }, + { + "apiVersion": "[variables('networkApiVersion')]", + "location": "[variables('location')]", + "name": "[variables('virtualNetworkName')]", + "properties": { + "addressSpace": { + "addressPrefixes": [ + "[variables('addressPrefix')]" + ] + }, + "subnets": [ + { + "name": "[variables('subnetName')]", + "properties": { + "addressPrefix": "[variables('subnetAddressPrefix')]" + } + } + ] + }, + "type": "Microsoft.Network/virtualNetworks" + }, + { + "apiVersion": "[variables('networkApiVersion')]", + "dependsOn": [ + "[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'))]", + "[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]" + ], + "location": "[variables('location')]", + "name": "[parameters('nicName')]", + "properties": { + "ipConfigurations": [ + { + "name": "ipconfig", + "properties": { + "privateIPAllocationMethod": "Dynamic", + "publicIPAddress": { + "id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddressName'))]" + }, + "subnet": { + "id": "[variables('subnetRef')]" + } + } + } + ] + }, + "type": "Microsoft.Network/networkInterfaces" + }, + { + "apiVersion": "[variables('computeApiVersion')]", + "dependsOn": [ + "[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]" + ], + "location": "[variables('location')]", + "name": "[parameters('vmName')]", + "properties": { + "diagnosticsProfile": { + "bootDiagnostics": { + "enabled": false + } + }, + "hardwareProfile": { + "vmSize": "[parameters('vmSize')]" + }, + "networkProfile": { + "networkInterfaces": [ + { + "id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]" + } + ] + }, + "osProfile": { + "adminPassword": "[parameters('adminPassword')]", + "adminUsername": "[parameters('adminUsername')]", + "computerName": "[parameters('vmName')]", + "linuxConfiguration": { + "ssh": { + "publicKeys": [ + { + "keyData": "", + "path": "[variables('sshKeyPath')]" + } + ] + } + } + }, + "securityProfile": { + "securityType": "ConfidentialVM", + "uefiSettings": { + "secureBootEnabled": true, + "vTpmEnabled": true + } + }, + "storageProfile": { + "imageReference": { + "id": "/subscriptions/subscription/resourceGroups/rg_name/providers/Microsoft.Compute/galleries/gallery_name/images/image_name/versions/2024.01.01" + }, + "osDisk": { + "caching": "ReadWrite", + "createOption": "FromImage", + "managedDisk": { + "securityProfile": { + "securityEncryptionType": "VMGuestStateOnly" + } + }, + "name": "[parameters('osDiskName')]", + "osType": "Linux" + } + } + }, + "type": "Microsoft.Compute/virtualMachines" + }, + { + "apiVersion": "[variables('computeApiVersion')]", + "condition": "[not(empty(parameters('commandToExecute')))]", + "dependsOn": [ + "[resourceId('Microsoft.Compute/virtualMachines/', parameters('vmName'))]" + ], + "location": "[variables('location')]", + "name": "[concat(parameters('vmName'), '/extension-customscript')]", + "properties": { + "autoUpgradeMinorVersion": true, + "publisher": "Microsoft.Compute", + "settings": { + "commandToExecute": "[parameters('commandToExecute')]" + }, + "type": "CustomScriptExtension", + "typeHandlerVersion": "1.10" + }, + "type": "Microsoft.Compute/virtualMachines/extensions" + } + ], + "variables": { + "addressPrefix": "10.0.0.0/16", + "computeApiVersion": "2023-03-01", + "location": "[resourceGroup().location]", + "networkApiVersion": "2023-04-01", + "publicIPAddressType": "Dynamic", + "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", + "subnetAddressPrefix": "10.0.0.0/24", + "subnetName": "[parameters('subnetName')]", + "subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]", + "virtualNetworkName": "[parameters('virtualNetworkName')]", + "virtualNetworkResourceGroup": "[resourceGroup().name]", + "vmStorageAccountContainerName": "images", + "vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]" + } +} \ No newline at end of file diff --git a/builder/azure/arm/template_factory_test.TestSigSourcedWithDiskEncryptionSet.approved.json b/builder/azure/arm/template_factory_test.TestSigSourcedWithDiskEncryptionSet.approved.json new file mode 100644 index 00000000..71fa3643 --- /dev/null +++ b/builder/azure/arm/template_factory_test.TestSigSourcedWithDiskEncryptionSet.approved.json @@ -0,0 +1,200 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "adminPassword": { + "type": "securestring" + }, + "adminUsername": { + "type": "string" + }, + "commandToExecute": { + "type": "string" + }, + "dataDiskName": { + "type": "string" + }, + "dnsNameForPublicIP": { + "type": "string" + }, + "nicName": { + "type": "string" + }, + "nsgName": { + "type": "string" + }, + "osDiskName": { + "type": "string" + }, + "publicIPAddressName": { + "type": "string" + }, + "storageAccountBlobEndpoint": { + "type": "string" + }, + "subnetName": { + "type": "string" + }, + "virtualNetworkName": { + "type": "string" + }, + "vmName": { + "type": "string" + }, + "vmSize": { + "type": "string" + } + }, + "resources": [ + { + "apiVersion": "[variables('networkApiVersion')]", + "location": "[variables('location')]", + "name": "[parameters('publicIPAddressName')]", + "properties": { + "dnsSettings": { + "domainNameLabel": "[parameters('dnsNameForPublicIP')]" + }, + "publicIPAllocationMethod": "[variables('publicIPAddressType')]" + }, + "type": "Microsoft.Network/publicIPAddresses" + }, + { + "apiVersion": "[variables('networkApiVersion')]", + "location": "[variables('location')]", + "name": "[variables('virtualNetworkName')]", + "properties": { + "addressSpace": { + "addressPrefixes": [ + "[variables('addressPrefix')]" + ] + }, + "subnets": [ + { + "name": "[variables('subnetName')]", + "properties": { + "addressPrefix": "[variables('subnetAddressPrefix')]" + } + } + ] + }, + "type": "Microsoft.Network/virtualNetworks" + }, + { + "apiVersion": "[variables('networkApiVersion')]", + "dependsOn": [ + "[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'))]", + "[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]" + ], + "location": "[variables('location')]", + "name": "[parameters('nicName')]", + "properties": { + "ipConfigurations": [ + { + "name": "ipconfig", + "properties": { + "privateIPAllocationMethod": "Dynamic", + "publicIPAddress": { + "id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddressName'))]" + }, + "subnet": { + "id": "[variables('subnetRef')]" + } + } + } + ] + }, + "type": "Microsoft.Network/networkInterfaces" + }, + { + "apiVersion": "[variables('computeApiVersion')]", + "dependsOn": [ + "[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]" + ], + "location": "[variables('location')]", + "name": "[parameters('vmName')]", + "properties": { + "diagnosticsProfile": { + "bootDiagnostics": { + "enabled": false + } + }, + "hardwareProfile": { + "vmSize": "[parameters('vmSize')]" + }, + "networkProfile": { + "networkInterfaces": [ + { + "id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]" + } + ] + }, + "osProfile": { + "adminPassword": "[parameters('adminPassword')]", + "adminUsername": "[parameters('adminUsername')]", + "computerName": "[parameters('vmName')]", + "linuxConfiguration": { + "ssh": { + "publicKeys": [ + { + "keyData": "", + "path": "[variables('sshKeyPath')]" + } + ] + } + } + }, + "storageProfile": { + "imageReference": { + "id": "/subscriptions/subscription/resourceGroups/rg_name/providers/Microsoft.Compute/galleries/gallery_name/images/image_name/versions/2024.01.01" + }, + "osDisk": { + "caching": "ReadWrite", + "createOption": "FromImage", + "managedDisk": { + "diskEncryptionSet": { + "id": "my_id" + } + }, + "name": "[parameters('osDiskName')]", + "osType": "Linux" + } + } + }, + "type": "Microsoft.Compute/virtualMachines" + }, + { + "apiVersion": "[variables('computeApiVersion')]", + "condition": "[not(empty(parameters('commandToExecute')))]", + "dependsOn": [ + "[resourceId('Microsoft.Compute/virtualMachines/', parameters('vmName'))]" + ], + "location": "[variables('location')]", + "name": "[concat(parameters('vmName'), '/extension-customscript')]", + "properties": { + "autoUpgradeMinorVersion": true, + "publisher": "Microsoft.Compute", + "settings": { + "commandToExecute": "[parameters('commandToExecute')]" + }, + "type": "CustomScriptExtension", + "typeHandlerVersion": "1.10" + }, + "type": "Microsoft.Compute/virtualMachines/extensions" + } + ], + "variables": { + "addressPrefix": "10.0.0.0/16", + "computeApiVersion": "2023-03-01", + "location": "[resourceGroup().location]", + "networkApiVersion": "2023-04-01", + "publicIPAddressType": "Dynamic", + "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", + "subnetAddressPrefix": "10.0.0.0/24", + "subnetName": "[parameters('subnetName')]", + "subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]", + "virtualNetworkName": "[parameters('virtualNetworkName')]", + "virtualNetworkResourceGroup": "[resourceGroup().name]", + "vmStorageAccountContainerName": "images", + "vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]" + } +} \ No newline at end of file diff --git a/builder/azure/arm/template_factory_test.go b/builder/azure/arm/template_factory_test.go index c3f1dede..19a3cfd9 100644 --- a/builder/azure/arm/template_factory_test.go +++ b/builder/azure/arm/template_factory_test.go @@ -748,6 +748,41 @@ func TestTrustedLaunch01(t *testing.T) { approvaltests.VerifyJSONStruct(t, deployment.Properties.Template) } +func TestSigSourcedWithDiskEncryptionSet(t *testing.T) { + m := map[string]interface{}{ + "build_resource_group_name": "rg_name", + "disk_encryption_set_id": "my_id", + "shared_image_gallery_destination": map[string]interface{}{ + "subscription": "subscription", + "resource_group": "rg_name", + "gallery_name": "gallery_name", + "image_name": "image_name", + "image_version": "2024.01.02", + }, + "shared_image_gallery": map[string]interface{}{ + "subscription": "subscription", + "resource_group": "rg_name", + "gallery_name": "gallery_name", + "image_name": "image_name", + "image_version": "2024.01.01", + }, + "os_type": constants.Target_Linux, + "communicator": "none", + } + + var c Config + _, err := c.Prepare(m, getPackerConfiguration(), getPackerSSHPasswordCommunicatorConfiguration()) + if err != nil { + t.Fatal(err) + } + deployment, err := GetVirtualMachineDeployment(&c) + if err != nil { + t.Fatal(err) + } + + approvaltests.VerifyJSONStruct(t, deployment.Properties.Template) +} + func TestConfidentialVM01(t *testing.T) { m := map[string]interface{}{ "secure_boot_enabled": "true", @@ -816,6 +851,43 @@ func TestConfidentialVM02(t *testing.T) { approvaltests.VerifyJSONStruct(t, deployment.Properties.Template) } +func TestConfidentialVM03(t *testing.T) { + m := map[string]interface{}{ + "secure_boot_enabled": "true", + "vtpm_enabled": "true", + "security_type": "ConfidentialVM", + "build_resource_group_name": "rg_name", + "shared_image_gallery_destination": map[string]interface{}{ + "subscription": "subscription", + "resource_group": "rg_name", + "gallery_name": "gallery_name", + "image_name": "image_name", + "image_version": "2024.01.02", + }, + "shared_image_gallery": map[string]interface{}{ + "subscription": "subscription", + "resource_group": "rg_name", + "gallery_name": "gallery_name", + "image_name": "image_name", + "image_version": "2024.01.01", + }, + "os_type": constants.Target_Linux, + "communicator": "none", + } + + var c Config + _, err := c.Prepare(m, getPackerConfiguration(), getPackerSSHPasswordCommunicatorConfiguration()) + if err != nil { + t.Fatal(err) + } + deployment, err := GetVirtualMachineDeployment(&c) + if err != nil { + t.Fatal(err) + } + + approvaltests.VerifyJSONStruct(t, deployment.Properties.Template) +} + func TestEncryptionAtHost01(t *testing.T) { m := getArmBuilderConfiguration() m["encryption_at_host"] = "true" diff --git a/builder/azure/common/template/template_builder.go b/builder/azure/common/template/template_builder.go index a3e28e99..bc734d25 100644 --- a/builder/azure/common/template/template_builder.go +++ b/builder/azure/common/template/template_builder.go @@ -215,7 +215,6 @@ func (s *TemplateBuilder) SetSharedGalleryImage(location, imageID string, cachin profile.OsDisk.OsType = s.osType profile.OsDisk.Vhd = nil profile.OsDisk.Caching = cachingType - return nil } @@ -324,12 +323,18 @@ func (s *TemplateBuilder) SetDiskEncryptionSetID(diskEncryptionSetID string, sec profile := resource.Properties.StorageProfile if securityType != nil && *securityType == hashiVMSDK.SecurityTypesConfidentialVM { + if profile.OsDisk.ManagedDisk == nil { + profile.OsDisk.ManagedDisk = &ManagedDisk{} + } profile.OsDisk.ManagedDisk.SecurityProfile = &hashiVMSDK.VMDiskSecurityProfile{} profile.OsDisk.ManagedDisk.SecurityProfile.SecurityEncryptionType = securityEncryptionType profile.OsDisk.ManagedDisk.SecurityProfile.DiskEncryptionSet = &hashiVMSDK.SubResource{ Id: &diskEncryptionSetID, } } else { + if profile.OsDisk.ManagedDisk == nil { + profile.OsDisk.ManagedDisk = &ManagedDisk{} + } profile.OsDisk.ManagedDisk.DiskEncryptionSet = &DiskEncryptionSetParameters{ ID: &diskEncryptionSetID, } @@ -345,6 +350,9 @@ func (s *TemplateBuilder) SetDiskEncryptionWithPaaSKey(securityType *hashiVMSDK. } if securityType != nil && *securityType == hashiVMSDK.SecurityTypesConfidentialVM { profile := resource.Properties.StorageProfile + if profile.OsDisk.ManagedDisk == nil { + profile.OsDisk.ManagedDisk = &ManagedDisk{} + } profile.OsDisk.ManagedDisk.SecurityProfile = &hashiVMSDK.VMDiskSecurityProfile{} profile.OsDisk.ManagedDisk.SecurityProfile.SecurityEncryptionType = securityEncryptionType }