Skip to content

Commit

Permalink
Support Bring Your Own (Encryption) Key (BYOK)
Browse files Browse the repository at this point in the history
This patch adds support for bringing your own encryption key used
to encrypt/recrypt VMs.
  • Loading branch information
akutz committed Sep 25, 2024
1 parent 82ce71c commit 52597ae
Show file tree
Hide file tree
Showing 71 changed files with 6,259 additions and 280 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ config/crd/external-crds/cns.vmware.com_*
!config/crd/external-crds/cns.vmware.com_storagepolicyquotas.yaml
!config/crd/external-crds/cns.vmware.com_storagepolicyusages.yaml

# Created by the VSCode ginkgo plug-in
ginkgo.report

.DS_Store
.cache

Expand Down
5 changes: 5 additions & 0 deletions api/v1alpha1/virtualmachine_conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -822,6 +822,10 @@ func Convert_v1alpha3_VirtualMachineStatus_To_v1alpha1_VirtualMachineStatus(
return nil
}

func restore_v1alpha3_VirtualMachineCryptoSpec(dst, src *vmopv1.VirtualMachine) {
dst.Spec.Crypto = src.Spec.Crypto
}

func restore_v1alpha3_VirtualMachineImage(dst, src *vmopv1.VirtualMachine) {
dst.Spec.Image = src.Spec.Image
dst.Spec.ImageName = src.Spec.ImageName
Expand Down Expand Up @@ -1239,6 +1243,7 @@ func (src *VirtualMachine) ConvertTo(dstRaw ctrlconversion.Hub) error {
restore_v1alpha3_VirtualMachineInstanceUUID(dst, restored)
restore_v1alpha3_VirtualMachineGuestID(dst, restored)
restore_v1alpha3_VirtualMachineCdrom(dst, restored)
restore_v1alpha3_VirtualMachineCryptoSpec(dst, restored)

// END RESTORE

Expand Down
65 changes: 65 additions & 0 deletions api/v1alpha1/virtualmachine_conversion_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1702,4 +1702,69 @@ func TestVirtualMachineConversion(t *testing.T) {
g.Expect(apiequality.Semantic.DeepEqual(hub, expectedHub)).To(BeTrue(), cmp.Diff(hub, expectedHub))
})
})

t.Run("VirtualMachine and spec.crypto", func(t *testing.T) {

t.Run("hub-spoke-hub", func(t *testing.T) {

t.Run("spec.crypto is empty", func(t *testing.T) {
g := NewWithT(t)
hub := vmopv1.VirtualMachine{
Spec: vmopv1.VirtualMachineSpec{
Crypto: vmopv1.VirtualMachineCryptoSpec{},
},
}
hubSpokeHub(g, &hub, &vmopv1a1.VirtualMachine{})
})

t.Run("spec.crypto.className is non-empty", func(t *testing.T) {
g := NewWithT(t)
hub := vmopv1.VirtualMachine{
Spec: vmopv1.VirtualMachineSpec{
Crypto: vmopv1.VirtualMachineCryptoSpec{
ClassName: "fake",
},
},
}
hubSpokeHub(g, &hub, &vmopv1a1.VirtualMachine{})
})

t.Run("spec.crypto.useDefaultKeyProvider is &true", func(t *testing.T) {
g := NewWithT(t)
hub := vmopv1.VirtualMachine{
Spec: vmopv1.VirtualMachineSpec{
Crypto: vmopv1.VirtualMachineCryptoSpec{
UseDefaultKeyProvider: &[]bool{true}[0],
},
},
}
hubSpokeHub(g, &hub, &vmopv1a1.VirtualMachine{})
})

t.Run("spec.crypto.useDefaultKeyProvider is &false", func(t *testing.T) {
g := NewWithT(t)
hub := vmopv1.VirtualMachine{
Spec: vmopv1.VirtualMachineSpec{
Crypto: vmopv1.VirtualMachineCryptoSpec{
UseDefaultKeyProvider: &[]bool{false}[0],
},
},
}
hubSpokeHub(g, &hub, &vmopv1a1.VirtualMachine{})
})

t.Run("spec.crypto is completely filled out", func(t *testing.T) {
g := NewWithT(t)
hub := vmopv1.VirtualMachine{
Spec: vmopv1.VirtualMachineSpec{
Crypto: vmopv1.VirtualMachineCryptoSpec{
ClassName: "fake",
UseDefaultKeyProvider: &[]bool{false}[0],
},
},
}
hubSpokeHub(g, &hub, &vmopv1a1.VirtualMachine{})
})
})
})
}
2 changes: 2 additions & 0 deletions api/v1alpha1/zz_generated.conversion.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions api/v1alpha2/virtualmachine_conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,10 @@ func Convert_v1alpha3_VirtualMachine_To_v1alpha2_VirtualMachine(
return nil
}

func restore_v1alpha3_VirtualMachineCryptoSpec(dst, src *vmopv1.VirtualMachine) {
dst.Spec.Crypto = src.Spec.Crypto
}

func restore_v1alpha3_VirtualMachineImage(dst, src *vmopv1.VirtualMachine) {
dst.Spec.Image = src.Spec.Image
dst.Spec.ImageName = src.Spec.ImageName
Expand Down Expand Up @@ -293,6 +297,7 @@ func (src *VirtualMachine) ConvertTo(dstRaw ctrlconversion.Hub) error {
restore_v1alpha3_VirtualMachineSpecNetworkDomainName(dst, restored)
restore_v1alpha3_VirtualMachineGuestID(dst, restored)
restore_v1alpha3_VirtualMachineCdrom(dst, restored)
restore_v1alpha3_VirtualMachineCryptoSpec(dst, restored)

// END RESTORE

Expand Down
65 changes: 65 additions & 0 deletions api/v1alpha2/virtualmachine_conversion_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -607,4 +607,69 @@ func TestVirtualMachineConversion(t *testing.T) {
hubSpokeHub(g, &hub, &vmopv1.VirtualMachine{}, &vmopv1a2.VirtualMachine{})
})
})

t.Run("VirtualMachine and spec.crypto", func(t *testing.T) {

t.Run("hub-spoke-hub", func(t *testing.T) {

t.Run("spec.crypto is empty", func(t *testing.T) {
g := NewWithT(t)
hub := vmopv1.VirtualMachine{
Spec: vmopv1.VirtualMachineSpec{
Crypto: vmopv1.VirtualMachineCryptoSpec{},
},
}
hubSpokeHub(g, &hub, &vmopv1.VirtualMachine{}, &vmopv1a2.VirtualMachine{})
})

t.Run("spec.crypto.className is non-empty", func(t *testing.T) {
g := NewWithT(t)
hub := vmopv1.VirtualMachine{
Spec: vmopv1.VirtualMachineSpec{
Crypto: vmopv1.VirtualMachineCryptoSpec{
ClassName: "fake",
},
},
}
hubSpokeHub(g, &hub, &vmopv1.VirtualMachine{}, &vmopv1a2.VirtualMachine{})
})

t.Run("spec.crypto.useDefaultKeyProvider is &true", func(t *testing.T) {
g := NewWithT(t)
hub := vmopv1.VirtualMachine{
Spec: vmopv1.VirtualMachineSpec{
Crypto: vmopv1.VirtualMachineCryptoSpec{
UseDefaultKeyProvider: &[]bool{true}[0],
},
},
}
hubSpokeHub(g, &hub, &vmopv1.VirtualMachine{}, &vmopv1a2.VirtualMachine{})
})

t.Run("spec.crypto.useDefaultKeyProvider is &false", func(t *testing.T) {
g := NewWithT(t)
hub := vmopv1.VirtualMachine{
Spec: vmopv1.VirtualMachineSpec{
Crypto: vmopv1.VirtualMachineCryptoSpec{
UseDefaultKeyProvider: &[]bool{false}[0],
},
},
}
hubSpokeHub(g, &hub, &vmopv1.VirtualMachine{}, &vmopv1a2.VirtualMachine{})
})

t.Run("spec.crypto is completely filled out", func(t *testing.T) {
g := NewWithT(t)
hub := vmopv1.VirtualMachine{
Spec: vmopv1.VirtualMachineSpec{
Crypto: vmopv1.VirtualMachineCryptoSpec{
ClassName: "fake",
UseDefaultKeyProvider: &[]bool{false}[0],
},
},
}
hubSpokeHub(g, &hub, &vmopv1.VirtualMachine{}, &vmopv1a2.VirtualMachine{})
})
})
})
}
2 changes: 2 additions & 0 deletions api/v1alpha2/zz_generated.conversion.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

103 changes: 103 additions & 0 deletions api/v1alpha3/virtualmachine_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ const (
// VirtualMachineConditionPlacementReady indicates that the placement decision for the VM is ready.
VirtualMachineConditionPlacementReady = "VirtualMachineConditionPlacementReady"

// VirtualMachineEncryptionSynced indicates that the VirtualMachine's
// encryption state is synced to the desired encryption state.
VirtualMachineEncryptionSynced = "VirtualMachineEncryptionSynced"

// VirtualMachineConditionCreated indicates that the VM has been created.
VirtualMachineConditionCreated = "VirtualMachineCreated"

Expand Down Expand Up @@ -357,6 +361,65 @@ type VirtualMachineCdromSpec struct {
AllowGuestControl *bool `json:"allowGuestControl,omitempty"`
}

// VirtualMachineCryptoSpec defines the desired state of a VirtualMachine's
// encryption state.
type VirtualMachineCryptoSpec struct {
// +optional

// ClassName describes the name of the EncryptionClass resource
// used to encrypt this VM.
//
// Please note, this field is not required to encrypt the VM. If the
// underlying platform has a default key provider, the VM may still be fully
// or partially encrypted depending on the specified storage and VM classes.
//
// If there is a default key provider and an encryption storage class is
// selected, the VM's home files and non-PVC disks will be encrypted.
//
// If there is a default key provider and a and a VM Class with a virtual,
// trusted platform module (vTPM) is selected, the VM's home files will be
// encrypted.
//
// If the underlying vSphere platform does not have a default key provider,
// then this field is required when specifying an encryption storage class
// and/or a VM Class with a vTPM.
ClassName string `json:"className,omitempty"`

// +optional
// +kubebuilder:default=true

// UseDefaultKeyProvider describes the desired behavior for when an explicit
// EncryptionClass is not provided.
//
// When an explicit EncryptionClass is not provided and this value is true:
//
// - Deploying a VirtualMachine with an encryption storage policy or vTPM
// will be encrypted using the default key provider.
//
// - If a VirtualMachine is not encrypted, uses an encryption storage
// policy or has a virtual, trusted platform module (vTPM), there is a
// default key provider, the VM will be encrypted using the default key
// provider.
//
// - If a VirtualMachine is encrypted with a provider other than the default
// key provider, the VM will be rekeyed using the default key provider.
//
// When an explicit EncryptionClass is not provided and this value is false:
//
// - Deploying a VirtualMachine with an encryption storage policy or vTPM
// will fail.
//
// - If a VirtualMachine is encrypted with a provider other than the default
// key provider, the VM will be not be rekeyed.
//
// Please note, this could result in a VirtualMachine that cannot be
// powered on since it is encrypted using a provider that may have been
// removed.
//
// Defaults to true if omitted.
UseDefaultKeyProvider *bool `json:"useDefaultKeyProvider,omitempty"`
}

// VirtualMachineSpec defines the desired state of a VirtualMachine.
type VirtualMachineSpec struct {
// +optional
Expand Down Expand Up @@ -439,6 +502,11 @@ type VirtualMachineSpec struct {

// +optional

// Crypto describes the desired encryption state of the VirtualMachine.
Crypto VirtualMachineCryptoSpec `json:"crypto,omitempty"`

// +optional

// StorageClass describes the name of a Kubernetes StorageClass resource
// used to configure this VM's storage-related attributes.
//
Expand Down Expand Up @@ -694,6 +762,35 @@ type VirtualMachineAdvancedSpec struct {
ChangeBlockTracking *bool `json:"changeBlockTracking,omitempty"`
}

type VirtualMachineEncryptionType string

const (
VirtualMachineEncryptionTypeConfig VirtualMachineEncryptionType = "Config"
VirtualMachineEncryptionTypeDisks VirtualMachineEncryptionType = "Disks"
)

type VirtualMachineCryptoStatus struct {
// +optional

// Encrypted describes the observed state of the VirtualMachine's
// encryption.
Encrypted []VirtualMachineEncryptionType `json:"encrypted,omitempty"`

// +optional

// ProviderID describes the provider ID used to encrypt the VirtualMachine.
// Please note, this field will be empty if the VirtualMachine is not
// encrypted.
ProviderID string `json:"providerID,omitempty"`

// +optional

// KeyID describes the key ID used to encrypt the VirtualMachine.
// Please note, this field will be empty if the VirtualMachine is not
// encrypted.
KeyID string `json:"keyID,omitempty"`
}

// VirtualMachineStatus defines the observed state of a VirtualMachine instance.
type VirtualMachineStatus struct {
// +optional
Expand All @@ -720,6 +817,12 @@ type VirtualMachineStatus struct {

// +optional

// Crypto describes the observed state of the VirtualMachine's encryption
// configuration.
Crypto *VirtualMachineCryptoStatus `json:"crypto,omitempty"`

// +optional

// Network describes the observed state of the VM's network configuration.
// Please note much of the network status information is only available if
// the guest has VM Tools installed.
Expand Down
Loading

0 comments on commit 52597ae

Please sign in to comment.