Skip to content

Commit

Permalink
Add support for confidential VMs
Browse files Browse the repository at this point in the history
This change adds support for Confidential VMs and Trusted launch for
VMs.

Azure Confidential VMs are defined by their SecurityProfile.SecurityType
ConfidentialVM, which should be defined along with the
OSDisk.ManagedDisk.SecurityProfile.SecurityEncryptionType field.

Trusted launch for VMs is defined by the SecurityProfile.SecurityType
TrustedLaunch, which should be defined along with the
SecurityProfile.UefiSettings section, i.e. the SecureBootEnabled and
VTpmEnabled fields.

Signed-off-by: Michail Resvanis <mresvani@redhat.com>
  • Loading branch information
mresvanis committed Jun 12, 2023
1 parent f20a204 commit 3582327
Show file tree
Hide file tree
Showing 14 changed files with 1,404 additions and 28 deletions.
65 changes: 65 additions & 0 deletions api/v1beta1/azuremachine_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ func ValidateAzureMachineSpec(spec AzureMachineSpec) field.ErrorList {
allErrs = append(allErrs, errs...)
}

if errs := ValidateConfidentialCompute(spec.OSDisk.ManagedDisk, spec.SecurityProfile, field.NewPath("securityProfile")); len(errs) > 0 {
allErrs = append(allErrs, errs...)
}

if errs := ValidateSSHKey(spec.SSHPublicKey, field.NewPath("sshPublicKey")); len(errs) > 0 {
allErrs = append(allErrs, errs...)
}
Expand Down Expand Up @@ -234,6 +238,18 @@ func validateManagedDisk(m *ManagedDiskParameters, fieldPath *field.Path, isOSDi

if m != nil {
allErrs = append(allErrs, validateStorageAccountType(m.StorageAccountType, fieldPath.Child("StorageAccountType"), isOSDisk)...)

// DiskEncryptionSet can only be set when SecurityEncryptionType is set to DiskWithVMGuestState
// https://learn.microsoft.com/en-us/rest/api/compute/virtual-machines/create-or-update?tabs=HTTP#securityencryptiontypes
if isOSDisk && m.SecurityProfile != nil && m.SecurityProfile.DiskEncryptionSet != nil {
if m.SecurityProfile.SecurityEncryptionType != SecurityEncryptionTypeDiskWithVMGuestState {
allErrs = append(allErrs, field.Invalid(
fieldPath.Child("securityProfile").Child("diskEncryptionSet"),
m.SecurityProfile.DiskEncryptionSet.ID,
"diskEncryptionSet is only supported when securityEncryptionType is set to DiskWithVMGuestState",
))
}
}
}

return allErrs
Expand Down Expand Up @@ -378,3 +394,52 @@ func ValidateDiagnostics(diagnostics *Diagnostics, fieldPath *field.Path) field.

return allErrs
}

// ValidateConfidentialCompute validates the configuration options when the machine is a Confidential VM.
// https://learn.microsoft.com/en-us/rest/api/compute/virtual-machines/create-or-update?tabs=HTTP#vmdisksecurityprofile
// https://learn.microsoft.com/en-us/rest/api/compute/virtual-machines/create-or-update?tabs=HTTP#securityencryptiontypes
// https://learn.microsoft.com/en-us/rest/api/compute/virtual-machines/create-or-update?tabs=HTTP#uefisettings
func ValidateConfidentialCompute(managedDisk *ManagedDiskParameters, profile *SecurityProfile, fieldPath *field.Path) field.ErrorList {
var allErrs field.ErrorList

var securityEncryptionType SecurityEncryptionType

if managedDisk != nil && managedDisk.SecurityProfile != nil {
securityEncryptionType = managedDisk.SecurityProfile.SecurityEncryptionType
}

if profile != nil && securityEncryptionType != "" {
// SecurityEncryptionType can only be set for Confindential VMs
if profile.SecurityType != SecurityTypesConfidentialVM {
allErrs = append(allErrs, field.Invalid(fieldPath.Child("SecurityType"), profile.SecurityType,
fmt.Sprintf("SecurityType should be set to '%s' when securityEncryptionType is defined", SecurityTypesConfidentialVM)))
}

// Confidential VMs require vTPM to be enabled, irrespective of the SecurityEncryptionType used
if profile.UefiSettings == nil {
allErrs = append(allErrs, field.Invalid(fieldPath.Child("UefiSettings"), profile.UefiSettings,
"UefiSettings should be set when securityEncryptionType is defined"))
}

if profile.UefiSettings != nil && (profile.UefiSettings.VTpmEnabled == nil || !*profile.UefiSettings.VTpmEnabled) {
allErrs = append(allErrs, field.Invalid(fieldPath.Child("VTpmEnabled"), profile.UefiSettings.VTpmEnabled,
"VTpmEnabled should be set to true when securityEncryptionType is defined"))
}

if securityEncryptionType == SecurityEncryptionTypeDiskWithVMGuestState {
// DiskWithVMGuestState encryption type is not compatible with EncryptionAtHost
if profile.EncryptionAtHost != nil && *profile.EncryptionAtHost {
allErrs = append(allErrs, field.Invalid(fieldPath.Child("EncryptionAtHost"), profile.EncryptionAtHost,
fmt.Sprintf("EncryptionAtHost cannot be set to 'true' when securityEncryptionType is set to '%s'", SecurityEncryptionTypeDiskWithVMGuestState)))
}

// DiskWithVMGuestState encryption type requires SecureBoot to be enabled
if profile.UefiSettings != nil && (profile.UefiSettings.SecureBootEnabled == nil || !*profile.UefiSettings.SecureBootEnabled) {
allErrs = append(allErrs, field.Invalid(fieldPath.Child("SecureBootEnabled"), profile.UefiSettings.SecureBootEnabled,
fmt.Sprintf("SecureBootEnabled should be set to true when securityEncryptionType is set to '%s'", SecurityEncryptionTypeDiskWithVMGuestState)))
}
}
}

return allErrs
}
183 changes: 183 additions & 0 deletions api/v1beta1/azuremachine_validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -868,3 +868,186 @@ func TestAzureMachine_ValidateNetwork(t *testing.T) {
})
}
}

func TestAzureMachine_ValidateConfidentialCompute(t *testing.T) {
g := NewWithT(t)

tests := []struct {
name string
managedDisk *ManagedDiskParameters
securityProfile *SecurityProfile
wantErr bool
}{
{
name: "valid configuration without confidential compute",
managedDisk: &ManagedDiskParameters{
SecurityProfile: &VMDiskSecurityProfile{
SecurityEncryptionType: "",
},
},
securityProfile: nil,
wantErr: false,
},
{
name: "valid configuration without confidential compute and host encryption enabled",
managedDisk: &ManagedDiskParameters{
SecurityProfile: &VMDiskSecurityProfile{
SecurityEncryptionType: "",
},
},
securityProfile: &SecurityProfile{
EncryptionAtHost: pointer.Bool(true),
},
wantErr: false,
},
{
name: "valid configuration with VMGuestStateOnly encryption and secure boot disabled",
managedDisk: &ManagedDiskParameters{
SecurityProfile: &VMDiskSecurityProfile{
SecurityEncryptionType: SecurityEncryptionTypeVMGuestStateOnly,
},
},
securityProfile: &SecurityProfile{
SecurityType: SecurityTypesConfidentialVM,
UefiSettings: &UefiSettings{
VTpmEnabled: pointer.Bool(true),
SecureBootEnabled: pointer.Bool(false),
},
},
wantErr: false,
},
{
name: "valid configuration with VMGuestStateOnly encryption and secure boot enabled",
managedDisk: &ManagedDiskParameters{
SecurityProfile: &VMDiskSecurityProfile{
SecurityEncryptionType: SecurityEncryptionTypeVMGuestStateOnly,
},
},
securityProfile: &SecurityProfile{
SecurityType: SecurityTypesConfidentialVM,
UefiSettings: &UefiSettings{
VTpmEnabled: pointer.Bool(true),
SecureBootEnabled: pointer.Bool(true),
},
},
wantErr: false,
},
{
name: "valid configuration with VMGuestStateOnly encryption and EncryptionAtHost enabled",
managedDisk: &ManagedDiskParameters{
SecurityProfile: &VMDiskSecurityProfile{
SecurityEncryptionType: SecurityEncryptionTypeVMGuestStateOnly,
},
},
securityProfile: &SecurityProfile{
EncryptionAtHost: pointer.Bool(true),
SecurityType: SecurityTypesConfidentialVM,
UefiSettings: &UefiSettings{
VTpmEnabled: pointer.Bool(true),
},
},
wantErr: false,
},
{
name: "valid configuration with DiskWithVMGuestState encryption",
managedDisk: &ManagedDiskParameters{
SecurityProfile: &VMDiskSecurityProfile{
SecurityEncryptionType: SecurityEncryptionTypeDiskWithVMGuestState,
},
},
securityProfile: &SecurityProfile{
SecurityType: SecurityTypesConfidentialVM,
UefiSettings: &UefiSettings{
SecureBootEnabled: pointer.Bool(true),
VTpmEnabled: pointer.Bool(true),
},
},
wantErr: false,
},
{
name: "invalid configuration with DiskWithVMGuestState encryption and EncryptionAtHost enabled",
managedDisk: &ManagedDiskParameters{
SecurityProfile: &VMDiskSecurityProfile{
SecurityEncryptionType: SecurityEncryptionTypeDiskWithVMGuestState,
},
},
securityProfile: &SecurityProfile{
EncryptionAtHost: pointer.Bool(true),
},
wantErr: true,
},
{
name: "invalid configuration with DiskWithVMGuestState encryption and vTPM disabled",
managedDisk: &ManagedDiskParameters{
SecurityProfile: &VMDiskSecurityProfile{
SecurityEncryptionType: SecurityEncryptionTypeDiskWithVMGuestState,
},
},
securityProfile: &SecurityProfile{
SecurityType: SecurityTypesConfidentialVM,
UefiSettings: &UefiSettings{
VTpmEnabled: pointer.Bool(false),
SecureBootEnabled: pointer.Bool(false),
},
},
wantErr: true,
},
{
name: "invalid configuration with DiskWithVMGuestState encryption and secure boot disabled",
managedDisk: &ManagedDiskParameters{
SecurityProfile: &VMDiskSecurityProfile{
SecurityEncryptionType: SecurityEncryptionTypeDiskWithVMGuestState,
},
},
securityProfile: &SecurityProfile{
SecurityType: SecurityTypesConfidentialVM,
UefiSettings: &UefiSettings{
VTpmEnabled: pointer.Bool(true),
SecureBootEnabled: pointer.Bool(false),
},
},
wantErr: true,
},
{
name: "invalid configuration with DiskWithVMGuestState encryption and SecurityType not set to ConfidentialVM",
managedDisk: &ManagedDiskParameters{
SecurityProfile: &VMDiskSecurityProfile{
SecurityEncryptionType: SecurityEncryptionTypeDiskWithVMGuestState,
},
},
securityProfile: &SecurityProfile{
UefiSettings: &UefiSettings{
VTpmEnabled: pointer.Bool(true),
SecureBootEnabled: pointer.Bool(true),
},
},
wantErr: true,
},
{
name: "invalid configuration with VMGuestStateOnly encryption and SecurityType not set to ConfidentialVM",
managedDisk: &ManagedDiskParameters{
SecurityProfile: &VMDiskSecurityProfile{
SecurityEncryptionType: SecurityEncryptionTypeVMGuestStateOnly,
},
},
securityProfile: &SecurityProfile{
UefiSettings: &UefiSettings{
VTpmEnabled: pointer.Bool(true),
SecureBootEnabled: pointer.Bool(true),
},
},
wantErr: true,
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
err := ValidateConfidentialCompute(tc.managedDisk, tc.securityProfile, field.NewPath("securityProfile"))
if tc.wantErr {
g.Expect(err).NotTo(BeEmpty())
} else {
g.Expect(err).To(BeEmpty())
}
})
}
}
96 changes: 96 additions & 0 deletions api/v1beta1/azuremachine_webhook_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,71 @@ func TestAzureMachine_ValidateCreate(t *testing.T) {
machine: createMachineWithNetworkConfig("", nil, []NetworkInterface{{SubnetName: "subnet", PrivateIPConfigs: 1}}),
wantErr: false,
},
{
name: "azuremachine without confidential compute properties and encryption at host enabled",
machine: createMachineWithConfidentialCompute("", "", true, false, false),
wantErr: false,
},
{
name: "azuremachine with confidential compute VMGuestStateOnly encryption and encryption at host enabled",
machine: createMachineWithConfidentialCompute(SecurityEncryptionTypeVMGuestStateOnly, SecurityTypesConfidentialVM, true, false, false),
wantErr: true,
},
{
name: "azuremachine with confidential compute DiskWithVMGuestState encryption and encryption at host enabled",
machine: createMachineWithConfidentialCompute(SecurityEncryptionTypeDiskWithVMGuestState, SecurityTypesConfidentialVM, true, true, true),
wantErr: true,
},
{
name: "azuremachine with confidential compute VMGuestStateOnly encryption, vTPM and SecureBoot enabled",
machine: createMachineWithConfidentialCompute(SecurityEncryptionTypeVMGuestStateOnly, SecurityTypesConfidentialVM, false, true, true),
wantErr: false,
},
{
name: "azuremachine with confidential compute VMGuestStateOnly encryption enabled, vTPM enabled and SecureBoot disabled",
machine: createMachineWithConfidentialCompute(SecurityEncryptionTypeVMGuestStateOnly, SecurityTypesConfidentialVM, false, true, false),
wantErr: false,
},
{
name: "azuremachine with confidential compute VMGuestStateOnly encryption enabled, vTPM disabled and SecureBoot enabled",
machine: createMachineWithConfidentialCompute(SecurityEncryptionTypeVMGuestStateOnly, SecurityTypesConfidentialVM, false, false, true),
wantErr: true,
},
{
name: "azuremachine with confidential compute VMGuestStateOnly encryption enabled, vTPM enabled, SecureBoot disabled and SecurityType empty",
machine: createMachineWithConfidentialCompute(SecurityEncryptionTypeVMGuestStateOnly, "", false, true, false),
wantErr: true,
},
{
name: "azuremachine with confidential compute VMGuestStateOnly encryption enabled, vTPM and SecureBoot empty",
machine: createMachineWithConfidentialCompute(SecurityEncryptionTypeVMGuestStateOnly, SecurityTypesConfidentialVM, false, false, false),
wantErr: true,
},
{
name: "azuremachine with confidential compute DiskWithVMGuestState encryption, vTPM and SecureBoot enabled",
machine: createMachineWithConfidentialCompute(SecurityEncryptionTypeDiskWithVMGuestState, SecurityTypesConfidentialVM, false, true, true),
wantErr: false,
},
{
name: "azuremachine with confidential compute DiskWithVMGuestState encryption enabled, vTPM enabled and SecureBoot disabled",
machine: createMachineWithConfidentialCompute(SecurityEncryptionTypeDiskWithVMGuestState, SecurityTypesConfidentialVM, false, true, false),
wantErr: true,
},
{
name: "azuremachine with confidential compute DiskWithVMGuestState encryption enabled, vTPM disabled and SecureBoot enabled",
machine: createMachineWithConfidentialCompute(SecurityEncryptionTypeDiskWithVMGuestState, SecurityTypesConfidentialVM, false, false, true),
wantErr: true,
},
{
name: "azuremachine with confidential compute DiskWithVMGuestState encryption enabled, vTPM disabled and SecureBoot disabled",
machine: createMachineWithConfidentialCompute(SecurityEncryptionTypeDiskWithVMGuestState, SecurityTypesConfidentialVM, false, false, false),
wantErr: true,
},
{
name: "azuremachine with confidential compute DiskWithVMGuestState encryption enabled, vTPM enabled, SecureBoot disabled and SecurityType empty",
machine: createMachineWithConfidentialCompute(SecurityEncryptionTypeDiskWithVMGuestState, "", false, true, false),
wantErr: true,
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
Expand Down Expand Up @@ -956,3 +1021,34 @@ func createMachineWithDiagnostics(diagnosticsType BootDiagnosticsStorageAccountT
},
}
}

func createMachineWithConfidentialCompute(securityEncryptionType SecurityEncryptionType, securityType SecurityTypes, encryptionAtHost, vTpmEnabled, secureBootEnabled bool) *AzureMachine {
securityProfile := &SecurityProfile{
EncryptionAtHost: &encryptionAtHost,
SecurityType: securityType,
UefiSettings: &UefiSettings{
VTpmEnabled: &vTpmEnabled,
SecureBootEnabled: &secureBootEnabled,
},
}

osDisk := OSDisk{
DiskSizeGB: pointer.Int32(30),
OSType: LinuxOS,
ManagedDisk: &ManagedDiskParameters{
StorageAccountType: "Premium_LRS",
SecurityProfile: &VMDiskSecurityProfile{
SecurityEncryptionType: securityEncryptionType,
},
},
CachingType: string(compute.PossibleCachingTypesValues()[0]),
}

return &AzureMachine{
Spec: AzureMachineSpec{
SSHPublicKey: validSSHPublicKey,
OSDisk: osDisk,
SecurityProfile: securityProfile,
},
}
}
Loading

0 comments on commit 3582327

Please sign in to comment.