Skip to content

Commit

Permalink
Merge pull request #330 from mjura/bootDiskKmsKey
Browse files Browse the repository at this point in the history
Add support for Customer Managed Encryption Key
  • Loading branch information
mjura authored Feb 20, 2024
2 parents 8b58555 + b2e3d2c commit 509e4c6
Show file tree
Hide file tree
Showing 7 changed files with 136 additions and 8 deletions.
13 changes: 13 additions & 0 deletions charts/gke-operator-crd/templates/crds.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,16 @@ spec:
clusterName:
nullable: true
type: string
customerManagedEncryptionKey:
nullable: true
properties:
keyName:
nullable: true
type: string
ringName:
nullable: true
type: string
type: object
description:
nullable: true
type: string
Expand Down Expand Up @@ -144,6 +154,9 @@ spec:
config:
nullable: true
properties:
bootDiskKmsKey:
nullable: true
type: string
diskSizeGb:
type: integer
diskType:
Expand Down
17 changes: 9 additions & 8 deletions controller/gke-cluster-config-handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -658,14 +658,15 @@ func BuildUpstreamClusterState(cluster *gkeapi.Cluster) (*gkev1.GKEClusterConfig

if np.Config != nil {
newNP.Config = &gkev1.GKENodeConfig{
DiskSizeGb: np.Config.DiskSizeGb,
DiskType: np.Config.DiskType,
ImageType: np.Config.ImageType,
Labels: np.Config.Labels,
LocalSsdCount: np.Config.LocalSsdCount,
MachineType: np.Config.MachineType,
Preemptible: np.Config.Preemptible,
Tags: np.Config.Tags,
BootDiskKmsKey: np.Config.BootDiskKmsKey,
DiskSizeGb: np.Config.DiskSizeGb,
DiskType: np.Config.DiskType,
ImageType: np.Config.ImageType,
Labels: np.Config.Labels,
LocalSsdCount: np.Config.LocalSsdCount,
MachineType: np.Config.MachineType,
Preemptible: np.Config.Preemptible,
Tags: np.Config.Tags,
}

newNP.Config.Taints = make([]gkev1.GKENodeTaintConfig, 0, len(np.Config.Taints))
Expand Down
25 changes: 25 additions & 0 deletions pkg/apis/gke.cattle.io/v1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,11 @@ type GKEClusterConfigSpec struct {
// including your nodes, scaling, security, and other preconfigured settings.
// +optional
AutopilotConfig *GKEAutopilotConfig `json:"autopilotConfig,omitempty"`

// The Customer Managed Encryption Key Config used to encrypt the boot disk attached
// to each node in the node pool.
// +optional
CustomerManagedEncryptionKey *CMEKConfig `json:"customerManagedEncryptionKey,omitempty"`
}

type GKEIPAllocationPolicy struct {
Expand Down Expand Up @@ -266,6 +271,13 @@ type GKENodePoolAutoscaling struct {
}

type GKENodeConfig struct {
// BootDiskKmsKey is the Customer Managed Encryption Key used to encrypt
// the boot disk attached to each node in the node pool. This should be
// of the form
// projects/[PROJECT_ID]/locations/[LOCATION]/keyRings/[RING_NAME]/cryptoKeys/[KEY_NAME]
// +optional
BootDiskKmsKey string `json:"bootDiskKmsKey,omitempty"`

// DiskSizeGb is the size of the node's disk in gigabytes.
// +optional
DiskSizeGb int64 `json:"diskSizeGb,omitempty"`
Expand Down Expand Up @@ -359,3 +371,16 @@ type GKENodePoolManagement struct {
type GKEAutopilotConfig struct {
Enabled bool `json:"enabled,omitempty"`
}

// The Customer Managed Encryption Key Config struct
// used to provide ring name and key name for encryption
// the boot disk attached to each node in the node pool.
// +optional
type CMEKConfig struct {
// KeyName is the name of the key to use for encryption.
// It has to be provided together with RingName.
KeyName string `json:"keyName,omitempty"`
// RingName is the name of the ring to use for encryption.
// It has to be provided together with KeyName.
RingName string `json:"ringName,omitempty"`
}
21 changes: 21 additions & 0 deletions pkg/apis/gke.cattle.io/v1/zz_generated_deepcopy.go

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

17 changes: 17 additions & 0 deletions pkg/gke/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,13 @@ func validateCreateRequest(ctx context.Context, gkeClient services.GKEClusterSer
}
}

if config.Spec.CustomerManagedEncryptionKey != nil {
if config.Spec.CustomerManagedEncryptionKey.RingName == "" ||
config.Spec.CustomerManagedEncryptionKey.KeyName == "" {
return fmt.Errorf("ringName and keyName are required to enable boot disk encryption for Node Pools")
}
}

operation, err := gkeClient.ClusterList(
ctx, LocationRRN(config.Spec.ProjectID, Location(config.Spec.Region, config.Spec.Zone)))
if err != nil {
Expand Down Expand Up @@ -337,6 +344,16 @@ func newGKENodePoolFromConfig(np *gkev1.GKENodePoolConfig, config *gkev1.GKEClus
AutoUpgrade: np.Management.AutoUpgrade,
},
}
if config.Spec.CustomerManagedEncryptionKey != nil &&
config.Spec.CustomerManagedEncryptionKey.RingName != "" &&
config.Spec.CustomerManagedEncryptionKey.KeyName != "" {
ret.Config.BootDiskKmsKey = BootDiskRRN(
config.Spec.ProjectID,
Location(config.Spec.Region, config.Spec.Zone),
config.Spec.CustomerManagedEncryptionKey.RingName,
config.Spec.CustomerManagedEncryptionKey.KeyName,
)
}
if config.Spec.IPAllocationPolicy != nil && config.Spec.IPAllocationPolicy.UseIPAliases {
ret.MaxPodsConstraint = &gkeapi.MaxPodsConstraint{
MaxPodsPerNode: *np.MaxPodsConstraint,
Expand Down
45 changes: 45 additions & 0 deletions pkg/gke/create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,43 @@ var _ = Describe("CreateCluster", func() {
Expect(managedCluster.Name).To(Equal(config.Spec.ClusterName))
})

It("should successfully create cluster with customer managment encryption key", func() {
config.Spec.CustomerManagedEncryptionKey = &gkev1.CMEKConfig{
KeyName: "test-key",
RingName: "test-keyring",
}
createClusterRequest := newClusterCreateRequest(config)
clusterServiceMock.EXPECT().
ClusterCreate(
ctx,
LocationRRN(config.Spec.ProjectID, Location(config.Spec.Region, config.Spec.Zone)),
createClusterRequest).
Return(&gkeapi.Operation{}, nil)

clusterServiceMock.EXPECT().
ClusterList(
ctx,
LocationRRN(config.Spec.ProjectID, Location(config.Spec.Region, config.Spec.Zone))).
Return(&gkeapi.ListClustersResponse{}, nil)

err := Create(ctx, clusterServiceMock, config)
Expect(err).ToNot(HaveOccurred())

clusterServiceMock.EXPECT().
ClusterGet(
ctx,
ClusterRRN(config.Spec.ProjectID, Location(config.Spec.Region, config.Spec.Zone),
config.Spec.ClusterName)).
Return(
&gkeapi.Cluster{
Name: "test-cluster",
}, nil)

managedCluster, err := GetCluster(ctx, clusterServiceMock, &config.Spec)
Expect(err).ToNot(HaveOccurred())
Expect(managedCluster.Name).To(Equal(config.Spec.ClusterName))
})

It("should fail to create cluster", func() {
clusterServiceMock.EXPECT().
ClusterList(
Expand Down Expand Up @@ -156,6 +193,14 @@ var _ = Describe("CreateCluster", func() {
Expect(managedCluster.Name).To(Equal(config.Spec.ClusterName))
})

It("should fail create cluster with customer managment encryption key", func() {
config.Spec.CustomerManagedEncryptionKey = &gkev1.CMEKConfig{
KeyName: "test-key",
}
err := Create(ctx, clusterServiceMock, config)
Expect(err).To(HaveOccurred())
})

It("should fail to create autopilot cluster with nodepools", func() {
config.Spec.ClusterName = "test-autopilot-cluster"
config.Spec.AutopilotConfig = &gkev1.GKEAutopilotConfig{
Expand Down
6 changes: 6 additions & 0 deletions pkg/gke/relative_resource_name.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,9 @@ func ClusterRRN(projectID, location, clusterName string) string {
func NodePoolRRN(projectID, location, clusterName, nodePool string) string {
return fmt.Sprintf("%s/nodePools/%s", ClusterRRN(projectID, location, clusterName), nodePool)
}

// BootDiskRRN returns a Relative Resource Name of a disk key in the region or zone for the
// specified project
func BootDiskRRN(projectID, location, ringName, keyName string) string {
return fmt.Sprintf("projects/%s/locations/%s/keyRings/%s/cryptoKeys/%s", projectID, location, ringName, keyName)
}

0 comments on commit 509e4c6

Please sign in to comment.