From f9cccc59cea5ee02650a84941f007b09f9424ff8 Mon Sep 17 00:00:00 2001 From: Bryan Cox Date: Mon, 4 Dec 2023 13:20:46 -0500 Subject: [PATCH 1/2] Add ability to encrypt OS disks in Azure VMs Adds the ability to encrypt the OS disks in the Azure VMs, using a DiskEncryptionSet resource. The DiskEncryptionSet ID can be passed in when creating a new Hosted Cluster or creating a new NodePool. Signed-off-by: Bryan Cox --- api/hypershift/v1alpha1/nodepool_types.go | 3 + api/hypershift/v1beta1/nodepool_types.go | 3 + .../v1alpha1/azurenodepoolplatform.go | 9 +++ .../v1beta1/azurenodepoolplatform.go | 9 +++ cmd/cluster/azure/create.go | 62 ++++++++++------- cmd/cluster/core/create.go | 16 ++--- .../hypershift.openshift.io_nodepools.yaml | 8 +++ cmd/nodepool/azure/create.go | 67 +++++++++---------- docs/content/reference/api.md | 12 ++++ examples/fixtures/example.go | 22 +++--- examples/fixtures/example_azure.go | 28 ++++---- hack/app-sre/saas_template.yaml | 8 +++ .../controllers/nodepool/azure.go | 20 ++++-- 13 files changed, 170 insertions(+), 97 deletions(-) diff --git a/api/hypershift/v1alpha1/nodepool_types.go b/api/hypershift/v1alpha1/nodepool_types.go index dae7f03190..6f0afecbbf 100644 --- a/api/hypershift/v1alpha1/nodepool_types.go +++ b/api/hypershift/v1alpha1/nodepool_types.go @@ -883,6 +883,9 @@ type AzureNodePoolPlatform struct { // in a location that does not support AvailabilityZone. // +optional AvailabilityZone string `json:"availabilityZone,omitempty"` + // DiskEncryptionSetID is the ID of the DiskEncryptionSet resource to use to encrypt the OS disks for the VMs. + // +optional + DiskEncryptionSetID string `json:"diskEncryptionSetID,omitempty"` } // We define our own condition type since metav1.Condition has validation diff --git a/api/hypershift/v1beta1/nodepool_types.go b/api/hypershift/v1beta1/nodepool_types.go index 1ab9581869..42921cea6f 100644 --- a/api/hypershift/v1beta1/nodepool_types.go +++ b/api/hypershift/v1beta1/nodepool_types.go @@ -876,6 +876,9 @@ type AzureNodePoolPlatform struct { // in a location that does not support AvailabilityZone. // +optional AvailabilityZone string `json:"availabilityZone,omitempty"` + // DiskEncryptionSetID is the ID of the DiskEncryptionSet resource to use to encrypt the OS disks for the VMs. + // +optional + DiskEncryptionSetID string `json:"diskEncryptionSetID,omitempty"` } // We define our own condition type since metav1.Condition has validation diff --git a/client/applyconfiguration/hypershift/v1alpha1/azurenodepoolplatform.go b/client/applyconfiguration/hypershift/v1alpha1/azurenodepoolplatform.go index 536ec42227..9adb49c3c8 100644 --- a/client/applyconfiguration/hypershift/v1alpha1/azurenodepoolplatform.go +++ b/client/applyconfiguration/hypershift/v1alpha1/azurenodepoolplatform.go @@ -25,6 +25,7 @@ type AzureNodePoolPlatformApplyConfiguration struct { DiskSizeGB *int32 `json:"diskSizeGB,omitempty"` DiskStorageAccountType *string `json:"diskStorageAccountType,omitempty"` AvailabilityZone *string `json:"availabilityZone,omitempty"` + DiskEncryptionSetID *string `json:"diskEncryptionSetID,omitempty"` } // AzureNodePoolPlatformApplyConfiguration constructs an declarative configuration of the AzureNodePoolPlatform type for use with @@ -72,3 +73,11 @@ func (b *AzureNodePoolPlatformApplyConfiguration) WithAvailabilityZone(value str b.AvailabilityZone = &value return b } + +// WithDiskEncryptionSetID sets the DiskEncryptionSetID field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DiskEncryptionSetID field is set to the value of the last call. +func (b *AzureNodePoolPlatformApplyConfiguration) WithDiskEncryptionSetID(value string) *AzureNodePoolPlatformApplyConfiguration { + b.DiskEncryptionSetID = &value + return b +} diff --git a/client/applyconfiguration/hypershift/v1beta1/azurenodepoolplatform.go b/client/applyconfiguration/hypershift/v1beta1/azurenodepoolplatform.go index e5f01dd428..e0e1bef744 100644 --- a/client/applyconfiguration/hypershift/v1beta1/azurenodepoolplatform.go +++ b/client/applyconfiguration/hypershift/v1beta1/azurenodepoolplatform.go @@ -25,6 +25,7 @@ type AzureNodePoolPlatformApplyConfiguration struct { DiskSizeGB *int32 `json:"diskSizeGB,omitempty"` DiskStorageAccountType *string `json:"diskStorageAccountType,omitempty"` AvailabilityZone *string `json:"availabilityZone,omitempty"` + DiskEncryptionSetID *string `json:"diskEncryptionSetID,omitempty"` } // AzureNodePoolPlatformApplyConfiguration constructs an declarative configuration of the AzureNodePoolPlatform type for use with @@ -72,3 +73,11 @@ func (b *AzureNodePoolPlatformApplyConfiguration) WithAvailabilityZone(value str b.AvailabilityZone = &value return b } + +// WithDiskEncryptionSetID sets the DiskEncryptionSetID field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DiskEncryptionSetID field is set to the value of the last call. +func (b *AzureNodePoolPlatformApplyConfiguration) WithDiskEncryptionSetID(value string) *AzureNodePoolPlatformApplyConfiguration { + b.DiskEncryptionSetID = &value + return b +} diff --git a/cmd/cluster/azure/create.go b/cmd/cluster/azure/create.go index c9165342e6..c84da4a515 100644 --- a/cmd/cluster/azure/create.go +++ b/cmd/cluster/azure/create.go @@ -5,9 +5,7 @@ import ( "fmt" "net/url" "os" - "os/signal" "strings" - "syscall" hyperv1 "github.com/openshift/hypershift/api/hypershift/v1beta1" "github.com/openshift/hypershift/cmd/cluster/core" @@ -30,35 +28,37 @@ func NewCreateCommand(opts *core.CreateOptions) *cobra.Command { opts.AzurePlatform.Location = "eastus" opts.AzurePlatform.InstanceType = "Standard_D4s_v4" opts.AzurePlatform.DiskSizeGB = 120 + cmd.Flags().StringVar(&opts.AzurePlatform.CredentialsFile, "azure-creds", opts.AzurePlatform.CredentialsFile, "Path to an Azure credentials file (required)") cmd.Flags().StringVar(&opts.AzurePlatform.Location, "location", opts.AzurePlatform.Location, "Location for the cluster") cmd.Flags().StringVar(&opts.AzurePlatform.EncryptionKeyID, "encryption-key-id", opts.AzurePlatform.EncryptionKeyID, "etcd encryption key identifier in the form of https://.vault.azure.net/keys//") cmd.Flags().StringVar(&opts.AzurePlatform.InstanceType, "instance-type", opts.AzurePlatform.InstanceType, "The instance type to use for nodes") cmd.Flags().Int32Var(&opts.AzurePlatform.DiskSizeGB, "root-disk-size", opts.AzurePlatform.DiskSizeGB, "The size of the root disk for machines in the NodePool (minimum 16)") - cmd.Flags().StringSliceVar(&opts.AzurePlatform.AvailabilityZones, "availablity-zones", opts.AzurePlatform.AvailabilityZones, "The availablity zones in which NodePools will be created. Must be left unspecified if the region does not support AZs. If set, one nodepool per zone will be created.") + cmd.Flags().StringSliceVar(&opts.AzurePlatform.AvailabilityZones, "availability-zones", opts.AzurePlatform.AvailabilityZones, "The availability zones in which NodePools will be created. Must be left unspecified if the region does not support AZs. If set, one nodepool per zone will be created.") cmd.Flags().StringVar(&opts.AzurePlatform.ResourceGroupName, "resource-group-name", opts.AzurePlatform.ResourceGroupName, "A resource group name to create the HostedCluster infrastructure resources under.") + cmd.Flags().StringVar(&opts.AzurePlatform.DiskEncryptionSetID, "disk-encryption-set-id", opts.AzurePlatform.DiskEncryptionSetID, "The Disk Encryption Set ID to use to encrypt the OS disks for the VMs.") _ = cmd.MarkFlagRequired("azure-creds") _ = cmd.MarkPersistentFlagRequired("pull-secret") - cmd.Run = func(cmd *cobra.Command, args []string) { - ctx, cancel := context.WithCancel(context.Background()) + cmd.RunE = func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() if opts.Timeout > 0 { - ctx, cancel = context.WithTimeout(context.Background(), opts.Timeout) + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, opts.Timeout) + defer cancel() } - defer cancel() - sigs := make(chan os.Signal, 1) - signal.Notify(sigs, syscall.SIGINT) - go func() { - <-sigs - cancel() - }() + err := validate(opts) + if err != nil { + return err + } - if err := CreateCluster(ctx, opts); err != nil { + if err = CreateCluster(ctx, opts); err != nil { opts.Log.Error(err, "Failed to create cluster") - os.Exit(1) + return err } + return nil } return cmd @@ -109,17 +109,18 @@ func applyPlatformSpecificsValues(ctx context.Context, exampleOptions *apifixtur exampleOptions.InfraID = infra.InfraID exampleOptions.ExternalDNSDomain = opts.ExternalDNSDomain exampleOptions.Azure = &apifixtures.ExampleAzureOptions{ - Location: infra.Location, - ResourceGroupName: infra.ResourceGroupName, - VnetName: infra.VnetName, - VnetID: infra.VNetID, - SubnetName: infra.SubnetName, - BootImageID: infra.BootImageID, - MachineIdentityID: infra.MachineIdentityID, - InstanceType: opts.AzurePlatform.InstanceType, - SecurityGroupName: infra.SecurityGroupName, - DiskSizeGB: opts.AzurePlatform.DiskSizeGB, - AvailabilityZones: opts.AzurePlatform.AvailabilityZones, + Location: infra.Location, + ResourceGroupName: infra.ResourceGroupName, + VnetName: infra.VnetName, + VnetID: infra.VNetID, + SubnetName: infra.SubnetName, + BootImageID: infra.BootImageID, + MachineIdentityID: infra.MachineIdentityID, + InstanceType: opts.AzurePlatform.InstanceType, + SecurityGroupName: infra.SecurityGroupName, + DiskSizeGB: opts.AzurePlatform.DiskSizeGB, + AvailabilityZones: opts.AzurePlatform.AvailabilityZones, + DiskEncryptionSetID: opts.AzurePlatform.DiskEncryptionSetID, } if opts.AzurePlatform.EncryptionKeyID != "" { @@ -178,3 +179,12 @@ func lookupRHCOSImage(ctx context.Context, arch string, image string, pullSecret return rhcosImage, nil } + +// validate validates the core create options passed in by the user +func validate(opts *core.CreateOptions) error { + // Resource group name is required when using DiskEncryptionSetID + if opts.AzurePlatform.DiskEncryptionSetID != "" && opts.AzurePlatform.ResourceGroupName == "" { + return fmt.Errorf("validate: resource-group-name is required when using disk-encryption-set-id") + } + return nil +} diff --git a/cmd/cluster/core/create.go b/cmd/cluster/core/create.go index fa014f4b40..8219466bc6 100644 --- a/cmd/cluster/core/create.go +++ b/cmd/cluster/core/create.go @@ -162,17 +162,17 @@ type AWSPlatformOptions struct { } type AzurePlatformOptions struct { - CredentialsFile string - Location string - EncryptionKeyID string - InstanceType string - DiskSizeGB int32 - AvailabilityZones []string - ResourceGroupName string + CredentialsFile string + Location string + EncryptionKeyID string + InstanceType string + DiskSizeGB int32 + AvailabilityZones []string + ResourceGroupName string + DiskEncryptionSetID string } func createCommonFixture(ctx context.Context, opts *CreateOptions) (*apifixtures.ExampleOptions, error) { - // allow client side defaulting when release image is empty but release stream is set. if len(opts.ReleaseImage) == 0 && len(opts.ReleaseStream) != 0 { defaultVersion, err := version.LookupDefaultOCPVersion(opts.ReleaseStream) diff --git a/cmd/install/assets/hypershift-operator/hypershift.openshift.io_nodepools.yaml b/cmd/install/assets/hypershift-operator/hypershift.openshift.io_nodepools.yaml index 4dc82f394c..1c071e21bb 100644 --- a/cmd/install/assets/hypershift-operator/hypershift.openshift.io_nodepools.yaml +++ b/cmd/install/assets/hypershift-operator/hypershift.openshift.io_nodepools.yaml @@ -484,6 +484,10 @@ spec: specified for clusters in a location that does not support AvailabilityZone. type: string + diskEncryptionSetID: + description: DiskEncryptionSetID is the ID of the DiskEncryptionSet + resource to use to encrypt the OS disks for the VMs. + type: string diskSizeGB: default: 120 format: int32 @@ -1463,6 +1467,10 @@ spec: specified for clusters in a location that does not support AvailabilityZone. type: string + diskEncryptionSetID: + description: DiskEncryptionSetID is the ID of the DiskEncryptionSet + resource to use to encrypt the OS disks for the VMs. + type: string diskSizeGB: default: 120 format: int32 diff --git a/cmd/nodepool/azure/create.go b/cmd/nodepool/azure/create.go index c9340d5b85..5f96efba95 100644 --- a/cmd/nodepool/azure/create.go +++ b/cmd/nodepool/azure/create.go @@ -2,65 +2,62 @@ package azure import ( "context" - "os" - "os/signal" - "syscall" + "fmt" hyperv1 "github.com/openshift/hypershift/api/hypershift/v1beta1" - "github.com/openshift/hypershift/cmd/log" "github.com/openshift/hypershift/cmd/nodepool/core" + "github.com/spf13/cobra" crclient "sigs.k8s.io/controller-runtime/pkg/client" ) +type AzurePlatformCreateOptions struct { + InstanceType string + DiskSize int32 + AvailabilityZone string + ResourceGroupName string + DiskEncryptionSetID string +} + func NewCreateCommand(coreOpts *core.CreateNodePoolOptions) *cobra.Command { + platformOpts := &AzurePlatformCreateOptions{ + InstanceType: "Standard_D4s_v4", + DiskSize: 120, + } + cmd := &cobra.Command{ Use: "azure", - Short: "Creates an Azure nodepool", + Short: "Creates basic functional NodePool resources for Azure platform", SilenceUsage: true, } - o := &opts{ - instanceType: "Standard_D4s_v4", - diskSize: 120, - } - cmd.Flags().StringVar(&o.instanceType, "instance-type", o.instanceType, "The instance type to use for the nodepool") - cmd.Flags().Int32Var(&o.diskSize, "root-disk-size", o.diskSize, "The size of the root disk for machines in the NodePool (minimum 16)") - cmd.Flags().StringVar(&o.availabilityZone, "availability-zone", o.availabilityZone, "The availabilityZone for the nodepool. Must be left unspecified if in a region that doesn't support AZs") - cmd.Run = func(cmd *cobra.Command, args []string) { - ctx, cancel := context.WithCancel(context.Background()) - sigs := make(chan os.Signal, 1) - signal.Notify(sigs, syscall.SIGINT) - go func() { - <-sigs - cancel() - }() + cmd.Flags().StringVar(&platformOpts.InstanceType, "instance-type", platformOpts.InstanceType, "The instance type to use for the nodepool") + cmd.Flags().Int32Var(&platformOpts.DiskSize, "root-disk-size", platformOpts.DiskSize, "The size of the root disk for machines in the NodePool (minimum 16)") + cmd.Flags().StringVar(&platformOpts.AvailabilityZone, "availability-zone", platformOpts.AvailabilityZone, "The availabilityZone for the nodepool. Must be left unspecified if in a region that doesn't support AZs") + cmd.Flags().StringVar(&platformOpts.ResourceGroupName, "resource-group-name", platformOpts.ResourceGroupName, "A resource group name to create the HostedCluster infrastructure resources under.") + cmd.Flags().StringVar(&platformOpts.DiskEncryptionSetID, "disk-encryption-set-id", platformOpts.DiskEncryptionSetID, "The Disk Encryption Set ID to use to encrypt the OS disks for the VMs.") - if err := coreOpts.CreateNodePool(ctx, o); err != nil { - log.Log.Error(err, "Failed to create nodepool") - os.Exit(1) - } - } + cmd.RunE = coreOpts.CreateRunFunc(platformOpts) return cmd } -type opts struct { - instanceType string - diskSize int32 - availabilityZone string -} +func (o *AzurePlatformCreateOptions) UpdateNodePool(_ context.Context, nodePool *hyperv1.NodePool, _ *hyperv1.HostedCluster, _ crclient.Client) error { + // Resource group name is required when using DiskEncryptionSetID + if o.DiskEncryptionSetID != "" && o.ResourceGroupName == "" { + return fmt.Errorf("UpdateNodePool: resource-group-name is required when using disk-encryption-set-id") + } -func (o *opts) UpdateNodePool(ctx context.Context, nodePool *hyperv1.NodePool, hcluster *hyperv1.HostedCluster, client crclient.Client) error { nodePool.Spec.Platform.Type = hyperv1.AzurePlatform nodePool.Spec.Platform.Azure = &hyperv1.AzureNodePoolPlatform{ - VMSize: o.instanceType, - DiskSizeGB: o.diskSize, - AvailabilityZone: o.availabilityZone, + VMSize: o.InstanceType, + DiskSizeGB: o.DiskSize, + AvailabilityZone: o.AvailabilityZone, + DiskEncryptionSetID: o.DiskEncryptionSetID, } return nil } -func (o opts) Type() hyperv1.PlatformType { +func (o *AzurePlatformCreateOptions) Type() hyperv1.PlatformType { return hyperv1.AzurePlatform } diff --git a/docs/content/reference/api.md b/docs/content/reference/api.md index 7fa422bca6..80459e2a63 100644 --- a/docs/content/reference/api.md +++ b/docs/content/reference/api.md @@ -2337,6 +2337,18 @@ string in a location that does not support AvailabilityZone.

+ + +diskEncryptionSetID
+ +string + + + +(Optional) +

DiskEncryptionSetID is the ID of the DiskEncryptionSet resource to use to encrypt the OS disks for the VMs.

+ + ###AzurePlatformSpec { #hypershift.openshift.io/v1beta1.AzurePlatformSpec } diff --git a/examples/fixtures/example.go b/examples/fixtures/example.go index 57ee0eb7fc..2c88841025 100644 --- a/examples/fixtures/example.go +++ b/examples/fixtures/example.go @@ -461,9 +461,9 @@ func (o ExampleOptions) Resources() *ExampleResources { } } - var etcdStorgageClass *string = nil + var etcdStorageClass *string = nil if len(o.EtcdStorageClass) > 0 { - etcdStorgageClass = pointer.String(o.EtcdStorageClass) + etcdStorageClass = pointer.String(o.EtcdStorageClass) } cluster := &hyperv1.HostedCluster{ TypeMeta: metav1.TypeMeta{ @@ -485,7 +485,7 @@ func (o ExampleOptions) Resources() *ExampleResources { Storage: hyperv1.ManagedEtcdStorageSpec{ Type: hyperv1.PersistentVolumeEtcdStorage, PersistentVolume: &hyperv1.PersistentVolumeEtcdStorageSpec{ - StorageClassName: etcdStorgageClass, + StorageClassName: etcdStorageClass, Size: &hyperv1.DefaultPersistentVolumeEtcdStorageSize, }, }, @@ -692,10 +692,11 @@ func (o ExampleOptions) Resources() *ExampleResources { nodePool.Spec.Management.UpgradeType = hyperv1.UpgradeTypeReplace } nodePool.Spec.Platform.Azure = &hyperv1.AzureNodePoolPlatform{ - VMSize: o.Azure.InstanceType, - ImageID: o.Azure.BootImageID, - DiskSizeGB: o.Azure.DiskSizeGB, - AvailabilityZone: availabilityZone, + VMSize: o.Azure.InstanceType, + ImageID: o.Azure.BootImageID, + DiskSizeGB: o.Azure.DiskSizeGB, + AvailabilityZone: availabilityZone, + DiskEncryptionSetID: o.Azure.DiskEncryptionSetID, } nodePools = append(nodePools, nodePool) } @@ -706,9 +707,10 @@ func (o ExampleOptions) Resources() *ExampleResources { nodePool.Spec.Management.UpgradeType = hyperv1.UpgradeTypeReplace } nodePool.Spec.Platform.Azure = &hyperv1.AzureNodePoolPlatform{ - VMSize: o.Azure.InstanceType, - ImageID: o.Azure.BootImageID, - DiskSizeGB: o.Azure.DiskSizeGB, + VMSize: o.Azure.InstanceType, + ImageID: o.Azure.BootImageID, + DiskSizeGB: o.Azure.DiskSizeGB, + DiskEncryptionSetID: o.Azure.DiskEncryptionSetID, } nodePools = append(nodePools, nodePool) } diff --git a/examples/fixtures/example_azure.go b/examples/fixtures/example_azure.go index 8699c1157c..9fd63f23c8 100644 --- a/examples/fixtures/example_azure.go +++ b/examples/fixtures/example_azure.go @@ -3,20 +3,20 @@ package fixtures import "github.com/openshift/hypershift/cmd/util" type ExampleAzureOptions struct { - Creds util.AzureCreds - Location string - ResourceGroupName string - VnetName string - VnetID string - SubnetName string - BootImageID string - MachineIdentityID string - InstanceType string - SecurityGroupName string - DiskSizeGB int32 - AvailabilityZones []string - - EncryptionKey *AzureEncryptionKey + Creds util.AzureCreds + Location string + ResourceGroupName string + VnetName string + VnetID string + SubnetName string + BootImageID string + MachineIdentityID string + InstanceType string + SecurityGroupName string + DiskSizeGB int32 + AvailabilityZones []string + DiskEncryptionSetID string + EncryptionKey *AzureEncryptionKey } type AzureEncryptionKey struct { diff --git a/hack/app-sre/saas_template.yaml b/hack/app-sre/saas_template.yaml index e1748b95e2..7ab17aee30 100644 --- a/hack/app-sre/saas_template.yaml +++ b/hack/app-sre/saas_template.yaml @@ -51609,6 +51609,10 @@ objects: be specified for clusters in a location that does not support AvailabilityZone. type: string + diskEncryptionSetID: + description: DiskEncryptionSetID is the ID of the DiskEncryptionSet + resource to use to encrypt the OS disks for the VMs. + type: string diskSizeGB: default: 120 format: int32 @@ -52599,6 +52603,10 @@ objects: be specified for clusters in a location that does not support AvailabilityZone. type: string + diskEncryptionSetID: + description: DiskEncryptionSetID is the ID of the DiskEncryptionSet + resource to use to encrypt the OS disks for the VMs. + type: string diskSizeGB: default: 120 format: int32 diff --git a/hypershift-operator/controllers/nodepool/azure.go b/hypershift-operator/controllers/nodepool/azure.go index 84538cee2e..83bd9ac94a 100644 --- a/hypershift-operator/controllers/nodepool/azure.go +++ b/hypershift-operator/controllers/nodepool/azure.go @@ -5,12 +5,13 @@ import ( "crypto/rsa" "encoding/base64" "fmt" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" + + hyperv1 "github.com/openshift/hypershift/api/hypershift/v1beta1" "golang.org/x/crypto/ssh" utilpointer "k8s.io/utils/pointer" capiazure "sigs.k8s.io/cluster-api-provider-azure/api/v1beta1" - - hyperv1 "github.com/openshift/hypershift/api/hypershift/v1beta1" ) func azureMachineTemplateSpec(hcluster *hyperv1.HostedCluster, nodePool *hyperv1.NodePool, existing capiazure.AzureMachineTemplateSpec) (*capiazure.AzureMachineTemplateSpec, error) { @@ -24,7 +25,7 @@ func azureMachineTemplateSpec(hcluster *hyperv1.HostedCluster, nodePool *hyperv1 return nil, fmt.Errorf("failed to generate a SSH key: %w", err) } } - return &capiazure.AzureMachineTemplateSpec{Template: capiazure.AzureMachineTemplateResource{Spec: capiazure.AzureMachineSpec{ + azureMachineTemplate := &capiazure.AzureMachineTemplateSpec{Template: capiazure.AzureMachineTemplateResource{Spec: capiazure.AzureMachineSpec{ VMSize: nodePool.Spec.Platform.Azure.VMSize, Image: &capiazure.Image{ID: utilpointer.String(bootImage(hcluster, nodePool))}, OSDisk: capiazure.OSDisk{ @@ -40,7 +41,18 @@ func azureMachineTemplateSpec(hcluster *hyperv1.HostedCluster, nodePool *hyperv1 UserAssignedIdentities: []capiazure.UserAssignedIdentity{{ProviderID: hcluster.Spec.Platform.Azure.MachineIdentityID}}, SSHPublicKey: sshKey, FailureDomain: failureDomain(nodePool), - }}}, nil + }}} + + if nodePool.Spec.Platform.Azure.DiskEncryptionSetID != "" { + azureMachineTemplate.Template.Spec.OSDisk.ManagedDisk.DiskEncryptionSet = &capiazure.DiskEncryptionSetParameters{ + ID: nodePool.Spec.Platform.Azure.DiskEncryptionSetID, + } + azureMachineTemplate.Template.Spec.SecurityProfile = &capiazure.SecurityProfile{ + EncryptionAtHost: to.Ptr(true), + } + } + + return azureMachineTemplate, nil } func generateSSHPubkey() (string, error) { From 2acb5ff6b757e17569630f74b9aa1a5a402ffe60 Mon Sep 17 00:00:00 2001 From: Bryan Cox Date: Tue, 5 Dec 2023 08:43:35 -0500 Subject: [PATCH 2/2] Add doc on encrypting OS disks on Azure VMs Signed-off-by: Bryan Cox --- .../create-azure-cluster-with-options.md | 102 ++++++++++++++++++ .../how-to/azure/create-azure-cluster.md | 26 ----- docs/mkdocs.yml | 1 + 3 files changed, 103 insertions(+), 26 deletions(-) create mode 100644 docs/content/how-to/azure/create-azure-cluster-with-options.md diff --git a/docs/content/how-to/azure/create-azure-cluster-with-options.md b/docs/content/how-to/azure/create-azure-cluster-with-options.md new file mode 100644 index 0000000000..7ead4c9bbf --- /dev/null +++ b/docs/content/how-to/azure/create-azure-cluster-with-options.md @@ -0,0 +1,102 @@ +# Create an Azure cluster with Additional Options +This document describes how to set up an Azure cluster with Hypershift with additional flag options. + +Creating an Azure cluster with Hypershift without any additional flag options can be found [here](create-azure-cluster.md). + +## Prerequisites +See the Prerequisites section in [Create an Azure Cluster](./create-azure-cluster.md#prerequisites) + +## Creating the Cluster in an Existing Resource Group +If you want to use an existing resource group you've created in Azure, you can pass the name into the `--resource-group-name` flag. This will create all needed Azure infrastructure in specified resource group. + +``` +hypershift create cluster azure \ +--name \ +--pull-secret \ +--azure-creds \ +--location \ +--base-domain \ +--release-image \ +--node-pool-replicas \ +--resource-group-name +``` + +If you need to delete your hosted cluster, you will need to also use the `--resource-group-name` flag on the delete command. + +``` +hypershift destroy cluster azure \ +--name \ +--azure-creds \ +--resource-group-name +``` + +!!! note + + If you delete your hosted cluster, it will end up deleting any existing resources prior to when the hosted cluster was created as well as the resource group itself. + +## Encrypting the OS Disks on Azure VMs +There are a few prerequisites for encrypting the OS disks on the Azure VMs: + +1. Create your own resource group +2. Create an Azure Key Vault, with purge protection required, within the resource group +3. Create a key in the vault to use to create a DiskEncryptionSet +4. Create a DiskEncryptionSet with key in the vault and grant it permissions to assess the key vault + +!!! note + + You will need to use the `resource-group-name` flag when using the `DiskEncryptionSetID` flag. + +After performing these steps, you just need to provide the DiskEncryptionSet ID when creating a hosted cluster. + +### CLI Example +``` +hypershift create cluster azure \ +--name \ +--pull-secret \ +--azure-creds \ +--location \ +--base-domain \ +--release-image \ +--node-pool-replicas \ +--resource-group-name \ +--disk-encryption-set-id +``` + +You can also pass in the DiskEncryptionSet ID when creating a NodePool. + +``` +hypershift create nodepool azure \ +--name \ +--cluster-name \ +--resource-group-name \ +--disk-encryption-set-id +``` + +### NodePool CR Example +The DiskEncryptionSet ID can also be set directly through the NodePool CR. + +``` +apiVersion: hypershift.openshift.io/v1beta1 +kind: NodePool +metadata: + creationTimestamp: null + name: + namespace: clusters +spec: + arch: amd64 + clusterName: + management: + autoRepair: false + upgradeType: Replace + platform: + azure: + diskEncryptionSetID: + diskSizeGB: 120 + vmsize: Standard_D4s_v4 + type: Azure + release: + image: + replicas: +status: + replicas: 0 +``` diff --git a/docs/content/how-to/azure/create-azure-cluster.md b/docs/content/how-to/azure/create-azure-cluster.md index ef32231851..b36c27d8b8 100644 --- a/docs/content/how-to/azure/create-azure-cluster.md +++ b/docs/content/how-to/azure/create-azure-cluster.md @@ -36,29 +36,3 @@ hypershift create cluster azure --pull-secret \ --node-pool-replicas 3 \ --external-dns-domain= ``` - -## Creating the Cluster in an Existing Resource Group -If you want to use an existing resource group you've created in Azure, you can pass the name into the `--resource-group-name` flag. This will create all needed Azure infrastructure in specified resource group. - -``` -hypershift create cluster azure --pull-secret \ ---name \ ---azure-creds \ ---location eastus --base-domain \ ---release-image \ ---node-pool-replicas 3 \ ---resource-group-name -``` - -If you need to delete your hosted cluster, you will need to also use the `--resource-group-name` flag on the delete command. - -``` -hypershift destroy cluster azure \ ---name $CLUSTER_NAME \ ---azure-creds $AZURE_CREDS \ ---resource-group-name -``` - -!!! note - - If you delete your hosted cluster, it will end up deleting any existing resources prior to when the hosted cluster was created as well as the resource group itself. \ No newline at end of file diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 0815375f55..2cf39a6978 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -74,6 +74,7 @@ nav: - how-to/aws/create-aws-hosted-cluster-arm-workers.md - 'Azure': - how-to/azure/create-azure-cluster.md + - how-to/azure/create-azure-cluster-with-options.md - 'Agent': - how-to/agent/create-agent-cluster.md - 'Disconnected':