create on Azure: Allow toggling between CVMs / Trusted Launch VMs (#401)

This commit is contained in:
Malte Poll 2022-08-25 15:24:31 +02:00 committed by GitHub
parent 45beec15f5
commit 716ba52588
11 changed files with 105 additions and 14 deletions

View File

@ -32,6 +32,7 @@ func (c *Client) CreateInstances(ctx context.Context, input CreateInstancesInput
Image: input.Image,
UserAssingedIdentity: input.UserAssingedIdentity,
LoadBalancerBackendAddressPool: azure.BackendAddressPoolWorkerName + "-" + c.uid,
ConfidentialVM: input.ConfidentialVM,
}
// Create control plane scale set
@ -45,6 +46,7 @@ func (c *Client) CreateInstances(ctx context.Context, input CreateInstancesInput
Image: input.Image,
UserAssingedIdentity: input.UserAssingedIdentity,
LoadBalancerBackendAddressPool: azure.BackendAddressPoolControlPlaneName + "-" + c.uid,
ConfidentialVM: input.ConfidentialVM,
}
var wg sync.WaitGroup
@ -99,6 +101,7 @@ type CreateInstancesInput struct {
StateDiskType string
Image string
UserAssingedIdentity string
ConfidentialVM bool
}
// CreateInstancesVMs creates instances based on standalone VMs.
@ -216,6 +219,7 @@ func (c *Client) createScaleSet(ctx context.Context, input CreateScaleSetInput)
ResourceGroup: c.resourceGroup,
LoadBalancerName: c.loadBalancerName,
LoadBalancerBackendAddressPool: input.LoadBalancerBackendAddressPool,
ConfidentialVM: input.ConfidentialVM,
}.Azure()
_, err = c.scaleSetsAPI.BeginCreateOrUpdate(
@ -295,6 +299,7 @@ type CreateScaleSetInput struct {
Image string
UserAssingedIdentity string
LoadBalancerBackendAddressPool string
ConfidentialVM bool
}
// CreateResourceGroup creates a resource group.

View File

@ -147,6 +147,7 @@ func TestCreateInstances(t *testing.T) {
InstanceType: "type",
Image: "image",
UserAssingedIdentity: "identity",
ConfidentialVM: true,
},
},
"error when creating scale set": {
@ -161,6 +162,7 @@ func TestCreateInstances(t *testing.T) {
InstanceType: "type",
Image: "image",
UserAssingedIdentity: "identity",
ConfidentialVM: true,
},
wantErr: true,
},
@ -176,6 +178,7 @@ func TestCreateInstances(t *testing.T) {
InstanceType: "type",
Image: "image",
UserAssingedIdentity: "identity",
ConfidentialVM: true,
},
wantErr: true,
},
@ -190,6 +193,7 @@ func TestCreateInstances(t *testing.T) {
InstanceType: "type",
Image: "image",
UserAssingedIdentity: "identity",
ConfidentialVM: true,
},
wantErr: true,
},

View File

@ -1,7 +1,7 @@
package azure
// InstanceTypes are valid Azure instance types.
var InstanceTypes = []string{
// CVMInstanceTypes are valid Azure CVM instance types.
var CVMInstanceTypes = []string{
// CVMs (3rd Generation EPYC 7763v processors)
// DCasv5-series
"Standard_DC2as_v5",
@ -42,3 +42,45 @@ var InstanceTypes = []string{
"Standard_EC64ads_v5",
"Standard_EC96ads_v5",
}
// TrustedLaunchInstanceTypes are valid Azure Trusted Launch instance types.
var TrustedLaunchInstanceTypes = []string{
// Trusted Launch (2nd Generation AMD EPYC 7452 or 3rd Generation EPYC 7763v processors)
// Dav4-series
"Standard_D2a_v4",
"Standard_D4a_v4",
"Standard_D8a_v4",
"Standard_D16a_v4",
"Standard_D32a_v4",
"Standard_D48a_v4",
"Standard_D64a_v4",
"Standard_D96a_v4",
// Dasv4-series
"Standard_D2as_v4",
"Standard_D4as_v4",
"Standard_D8as_v4",
"Standard_D16as_v4",
"Standard_D32as_v4",
"Standard_D48as_v4",
"Standard_D64as_v4",
"Standard_D96as_v4",
// Eav4-series
"Standard_E2a_v4",
"Standard_E4a_v4",
"Standard_E8a_v4",
"Standard_E16a_v4",
"Standard_E32a_v4",
"Standard_E48a_v4",
"Standard_E64a_v4",
"Standard_E96a_v4",
// Easv4-series
"Standard_E2as_v4",
"Standard_E4as_v4",
"Standard_E8as_v4",
"Standard_E16as_v4",
"Standard_E20as_v4",
"Standard_E32as_v4",
"Standard_E48as_v4",
"Standard_E64as_v4",
"Standard_E96as_v4",
}

View File

@ -27,10 +27,20 @@ type ScaleSet struct {
UserAssignedIdentity string
LoadBalancerName string
LoadBalancerBackendAddressPool string
ConfidentialVM bool
}
// Azure returns the Azure representation of ScaleSet.
func (s ScaleSet) Azure() armcomputev2.VirtualMachineScaleSet {
securityType := armcomputev2.SecurityTypesTrustedLaunch
var diskSecurityProfile *armcomputev2.VMDiskSecurityProfile
if s.ConfidentialVM {
securityType = armcomputev2.SecurityTypesConfidentialVM
diskSecurityProfile = &armcomputev2.VMDiskSecurityProfile{
SecurityEncryptionType: to.Ptr(armcomputev2.SecurityEncryptionTypesVMGuestStateOnly),
}
}
return armcomputev2.VirtualMachineScaleSet{
Name: to.Ptr(s.Name),
Location: to.Ptr(s.Location),
@ -70,9 +80,7 @@ func (s ScaleSet) Azure() armcomputev2.VirtualMachineScaleSet {
},
OSDisk: &armcomputev2.VirtualMachineScaleSetOSDisk{
ManagedDisk: &armcomputev2.VirtualMachineScaleSetManagedDiskParameters{
SecurityProfile: &armcomputev2.VMDiskSecurityProfile{
SecurityEncryptionType: to.Ptr(armcomputev2.SecurityEncryptionTypesVMGuestStateOnly),
},
SecurityProfile: diskSecurityProfile,
},
CreateOption: to.Ptr(armcomputev2.DiskCreateOptionTypesFromImage),
},
@ -111,7 +119,7 @@ func (s ScaleSet) Azure() armcomputev2.VirtualMachineScaleSet {
},
},
SecurityProfile: &armcomputev2.SecurityProfile{
SecurityType: to.Ptr(armcomputev2.SecurityTypesConfidentialVM),
SecurityType: to.Ptr(securityType),
UefiSettings: &armcomputev2.UefiSettings{VTpmEnabled: to.Ptr(true), SecureBootEnabled: to.Ptr(true)},
},
DiagnosticsProfile: &armcomputev2.DiagnosticsProfile{

View File

@ -23,6 +23,7 @@ func TestFirewallPermissions(t *testing.T) {
Password: "password",
Image: "image",
UserAssignedIdentity: "user-identity",
ConfidentialVM: true,
}
scaleSetAzure := scaleSet.Azure()

View File

@ -171,6 +171,7 @@ func (c *Creator) createAzure(ctx context.Context, cl azureclient, config *confi
StateDiskType: config.Provider.Azure.StateDiskType,
Image: config.Provider.Azure.Image,
UserAssingedIdentity: config.Provider.Azure.UserAssignedIdentity,
ConfidentialVM: *config.Provider.Azure.ConfidentialVM,
}
if err := cl.CreateInstances(ctx, createInput); err != nil {
return state.ConstellationState{}, err

View File

@ -40,12 +40,15 @@ func NewCreateCmd() *cobra.Command {
must(cmd.RegisterFlagCompletionFunc("instance-type", instanceTypeCompletion))
cmd.SetHelpTemplate(cmd.HelpTemplate() + fmt.Sprintf(`
Azure instance types:
Azure Confidential VM instance types:
%v
Azure Trusted Launch instance types:
%v
GCP instance types:
%v
`, formatInstanceTypes(azure.InstanceTypes), formatInstanceTypes(gcp.InstanceTypes)))
`, formatInstanceTypes(azure.CVMInstanceTypes), formatInstanceTypes(azure.TrustedLaunchInstanceTypes), formatInstanceTypes(gcp.InstanceTypes)))
return cmd
}
@ -78,6 +81,10 @@ func create(cmd *cobra.Command, creator cloudCreator, fileHandler file.Handler,
cmd.Println("Configured image does not look like a released production image. Double check image before deploying to production.")
}
if config.IsAzureNonCVM() {
cmd.Println("Disabling Confidential VMs is insecure. Use only for evaluation purposes.")
}
if !flags.yes {
// Ask user to confirm action.
cmd.Printf("The following Constellation cluster will be created:\n")
@ -186,7 +193,7 @@ func defaultInstanceType(provider cloudprovider.Provider) string {
case cloudprovider.GCP:
return gcp.InstanceTypes[0]
case cloudprovider.Azure:
return azure.InstanceTypes[0]
return azure.CVMInstanceTypes[0]
default:
return ""
}
@ -244,7 +251,10 @@ func instanceTypeCompletion(cmd *cobra.Command, args []string, toComplete string
case "gcp":
return gcp.InstanceTypes, cobra.ShellCompDirectiveNoFileComp
case "azure":
return azure.InstanceTypes, cobra.ShellCompDirectiveNoFileComp
var azureInstanceTypes []string
azureInstanceTypes = append(azureInstanceTypes, azure.CVMInstanceTypes...)
azureInstanceTypes = append(azureInstanceTypes, azure.TrustedLaunchInstanceTypes...)
return azureInstanceTypes, cobra.ShellCompDirectiveNoFileComp
default:
return []string{}, cobra.ShellCompDirectiveError
}

View File

@ -361,7 +361,7 @@ func TestInstanceTypeCompletion(t *testing.T) {
}{
"azure": {
args: []string{"azure"},
wantResult: azure.InstanceTypes,
wantResult: append(append([]string{}, azure.CVMInstanceTypes...), azure.TrustedLaunchInstanceTypes...),
wantShellCD: cobra.ShellCompDirectiveNoFileComp,
},
"gcp": {

View File

@ -41,12 +41,18 @@ func validInstanceTypeForProvider(cmd *cobra.Command, insType string, provider c
cmd.SilenceUsage = false
return fmt.Errorf("%s isn't a valid GCP instance type", insType)
case cloudprovider.Azure:
for _, instanceType := range azure.InstanceTypes {
for _, instanceType := range azure.CVMInstanceTypes {
if insType == instanceType {
return nil
}
}
cmd.SetUsageTemplate("Azure instance types:\n" + formatInstanceTypes(azure.InstanceTypes))
for _, instanceType := range azure.TrustedLaunchInstanceTypes {
if insType == instanceType {
return nil
}
}
cmd.SetUsageTemplate("Azure CVM instance types:\n" + formatInstanceTypes(azure.CVMInstanceTypes) +
"\n\nAzure Trusted Launch instance types:\n" + formatInstanceTypes(azure.TrustedLaunchInstanceTypes))
cmd.SilenceUsage = false
return fmt.Errorf("%s isn't a valid Azure instance type", insType)
default:

View File

@ -142,6 +142,9 @@ type AzureConfig struct {
// description: |
// Authorize spawned VMs to access Azure API. See: https://docs.edgeless.systems/constellation/latest/#/getting-started/install?id=azure
UserAssignedIdentity string `yaml:"userAssignedIdentity" validate:"required"`
// description: |
// Use VMs with security type Confidential VM. If set to false, Trusted Launch VMs will be used instead. See: https://docs.microsoft.com/en-us/azure/confidential-computing/confidential-vm-overview
ConfidentialVM *bool `yaml:"confidentialVM" validate:"required"`
}
// GCPConfig are GCP specific configuration values used by the CLI.
@ -230,6 +233,7 @@ func Default() *Config {
StateDiskType: "StandardSSD_LRS", // TODO: Replace with Premium_LRS when we replace the default VM size (Standard_D2a_v4) since the size does not support Premium_LRS
Measurements: copyPCRMap(azurePCRs),
EnforcedMeasurements: []uint32{8, 9, 11, 12},
ConfidentialVM: func() *bool { b := true; return &b }(),
},
GCP: &GCPConfig{
Project: "",
@ -357,6 +361,11 @@ func (c *Config) IsImageDebug() bool {
}
}
// IsAzureNonCVM checks whether the chosen provider is azure and confidential VMs are disabled.
func (c *Config) IsAzureNonCVM() bool {
return c.Provider.Azure != nil && c.Provider.Azure.ConfidentialVM != nil && !*c.Provider.Azure.ConfidentialVM
}
// FromFile returns config file with `name` read from `fileHandler` by parsing
// it as YAML.
func FromFile(fileHandler file.Handler, name string) (*Config, error) {

View File

@ -168,7 +168,7 @@ func init() {
FieldName: "azure",
},
}
AzureConfigDoc.Fields = make([]encoder.Doc, 8)
AzureConfigDoc.Fields = make([]encoder.Doc, 9)
AzureConfigDoc.Fields[0].Name = "subscription"
AzureConfigDoc.Fields[0].Type = "string"
AzureConfigDoc.Fields[0].Note = ""
@ -209,6 +209,11 @@ func init() {
AzureConfigDoc.Fields[7].Note = ""
AzureConfigDoc.Fields[7].Description = "Authorize spawned VMs to access Azure API. See: https://docs.edgeless.systems/constellation/latest/#/getting-started/install?id=azure"
AzureConfigDoc.Fields[7].Comments[encoder.LineComment] = "Authorize spawned VMs to access Azure API. See: https://docs.edgeless.systems/constellation/latest/#/getting-started/install?id=azure"
AzureConfigDoc.Fields[8].Name = "confidentialVM"
AzureConfigDoc.Fields[8].Type = "bool"
AzureConfigDoc.Fields[8].Note = ""
AzureConfigDoc.Fields[8].Description = "Use Azure VMs with security type Confidential VM. If set to false, Trusted Launch VMs will be used instead. See: https://docs.microsoft.com/en-us/azure/confidential-computing/confidential-vm-overview"
AzureConfigDoc.Fields[8].Comments[encoder.LineComment] = "Use Azure VMs with security type Confidential VM. If set to false, Trusted Launch VMs will be used instead. See: https://docs.microsoft.com/en-us/azure/confidential-computing/confidential-vm-overview"
GCPConfigDoc.Type = "GCPConfig"
GCPConfigDoc.Comments[encoder.LineComment] = "GCPConfig are GCP specific configuration values used by the CLI."