From 46c4742ba83e8989fa07b48678013c5d5d91f45b Mon Sep 17 00:00:00 2001 From: Abhinav Pandey Date: Wed, 20 Sep 2023 16:42:28 -0700 Subject: [PATCH] Extend Etcd Encryption API and add more validations and defaults --- cmd/eksctl-anywhere/cmd/createcluster.go | 4 + cmd/eksctl-anywhere/cmd/upgradecluster.go | 4 + .../anywhere.eks.amazonaws.com_clusters.yaml | 17 ++ config/manifest/eksa-components.yaml | 17 ++ go.sum | 6 - pkg/api/v1alpha1/cluster_defaults.go | 1 + pkg/api/v1alpha1/cluster_defaults_test.go | 69 +++++++ pkg/api/v1alpha1/cluster_webhook.go | 12 +- pkg/api/v1alpha1/cluster_webhook_test.go | 187 +++++++++++++++--- pkg/api/v1alpha1/etcdencryption.go | 93 +++++++++ pkg/api/v1alpha1/etcdencryption_types.go | 11 ++ pkg/api/v1alpha1/etcdncryption.go | 15 -- pkg/api/v1alpha1/zz_generated.deepcopy.go | 13 +- 13 files changed, 396 insertions(+), 53 deletions(-) create mode 100644 pkg/api/v1alpha1/etcdencryption.go delete mode 100644 pkg/api/v1alpha1/etcdncryption.go diff --git a/cmd/eksctl-anywhere/cmd/createcluster.go b/cmd/eksctl-anywhere/cmd/createcluster.go index 4a7f70ea8f2c..dbc2f0564042 100644 --- a/cmd/eksctl-anywhere/cmd/createcluster.go +++ b/cmd/eksctl-anywhere/cmd/createcluster.go @@ -84,6 +84,10 @@ func (cc *createClusterOptions) createCluster(cmd *cobra.Command, _ []string) er } } + if clusterConfig.Spec.EtcdEncryption != nil { + return errors.New("etcdEncryption is not supported during cluster creation") + } + docker := executables.BuildDockerExecutable() if err := validations.CheckMinimumDockerVersion(ctx, docker); err != nil { diff --git a/cmd/eksctl-anywhere/cmd/upgradecluster.go b/cmd/eksctl-anywhere/cmd/upgradecluster.go index 6a1d3cdce8f7..12e2098ab953 100644 --- a/cmd/eksctl-anywhere/cmd/upgradecluster.go +++ b/cmd/eksctl-anywhere/cmd/upgradecluster.go @@ -84,6 +84,10 @@ func (uc *upgradeClusterOptions) upgradeCluster(cmd *cobra.Command) error { } } + if err := v1alpha1.ValidateEtcdEncryptionConfig(clusterConfig.Spec.EtcdEncryption); err != nil { + return err + } + if _, err := uc.commonValidations(ctx); err != nil { return fmt.Errorf("common validations failed due to: %v", err) } diff --git a/config/crd/bases/anywhere.eks.amazonaws.com_clusters.yaml b/config/crd/bases/anywhere.eks.amazonaws.com_clusters.yaml index ce610a6a9c85..2126ac2e27db 100644 --- a/config/crd/bases/anywhere.eks.amazonaws.com_clusters.yaml +++ b/config/crd/bases/anywhere.eks.amazonaws.com_clusters.yaml @@ -230,11 +230,28 @@ spec: description: KMS defines the configuration for KMS Encryption provider. properties: + cachesize: + description: CacheSize defines the maximum number + of encrypted objects to be cached in memory. The + default value is 1000. You can set this to a negative + value to disable caching. + format: int32 + type: integer + name: + description: Name defines the name of KMS plugin to + be used. + type: string socketListenAddress: description: SocketListenAddress defines a UNIX socket address that the KMS provider listens on. type: string + timeout: + description: Timeout for kube-apiserver to wait for + KMS plugin. Default is 3s. + format: int64 + type: integer required: + - name - socketListenAddress type: object required: diff --git a/config/manifest/eksa-components.yaml b/config/manifest/eksa-components.yaml index c787c9f6311f..717ef83e578b 100644 --- a/config/manifest/eksa-components.yaml +++ b/config/manifest/eksa-components.yaml @@ -3866,11 +3866,28 @@ spec: description: KMS defines the configuration for KMS Encryption provider. properties: + cachesize: + description: CacheSize defines the maximum number + of encrypted objects to be cached in memory. The + default value is 1000. You can set this to a negative + value to disable caching. + format: int32 + type: integer + name: + description: Name defines the name of KMS plugin to + be used. + type: string socketListenAddress: description: SocketListenAddress defines a UNIX socket address that the KMS provider listens on. type: string + timeout: + description: Timeout for kube-apiserver to wait for + KMS plugin. Default is 3s. + format: int64 + type: integer required: + - name - socketListenAddress type: object required: diff --git a/go.sum b/go.sum index 31660090555c..7c2f864983db 100644 --- a/go.sum +++ b/go.sum @@ -522,8 +522,6 @@ github.com/aws/aws-sdk-go v1.38.40/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2z github.com/aws/aws-sdk-go v1.42.23 h1:V0V5hqMEyVelgpu1e4gMPVCJ+KhmscdNxP/NWP1iCOA= github.com/aws/aws-sdk-go v1.42.23/go.mod h1:gyRszuZ/icHmHAVE4gc/r+cfCmhA1AD+vqfWbgI+eHs= github.com/aws/aws-sdk-go-v2 v1.16.2/go.mod h1:ytwTPBG6fXTZLxxeeCCWj2/EMYp/xDUgX+OET6TLNNU= -github.com/aws/aws-sdk-go-v2 v1.16.14 h1:db6GvO4Z2UqHt5gvT0lr6J5x5P+oQ7bdRzczVaRekMU= -github.com/aws/aws-sdk-go-v2 v1.16.14/go.mod h1:s/G+UV29dECbF5rf+RNj1xhlmvoNurGSr+McVSRj59w= github.com/aws/aws-sdk-go-v2 v1.21.0 h1:gMT0IW+03wtYJhRqTVYn0wLzwdnK9sRMcxmtfGzRdJc= github.com/aws/aws-sdk-go-v2 v1.21.0/go.mod h1:/RfNgGmRxI+iFOB1OeJUyxiU+9s88k3pfHvDagGEp0M= github.com/aws/aws-sdk-go-v2/config v1.15.3 h1:5AlQD0jhVXlGzwo+VORKiUuogkG7pQcLJNzIzK7eodw= @@ -532,11 +530,9 @@ github.com/aws/aws-sdk-go-v2/credentials v1.11.2 h1:RQQ5fzclAKJyY5TvF+fkjJEwzK4h github.com/aws/aws-sdk-go-v2/credentials v1.11.2/go.mod h1:j8YsY9TXTm31k4eFhspiQicfXPLZ0gYXA50i4gxPE8g= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.3 h1:LWPg5zjHV9oz/myQr4wMs0gi4CjnDN/ILmyZUFYXZsU= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.3/go.mod h1:uk1vhHHERfSVCUnqSqz8O48LBYDSC+k6brng09jcMOk= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.9 h1:onz/VaaxZ7Z4V+WIN9Txly9XLTmoOh1oJ8XcAC3pako= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.9/go.mod h1:AnVH5pvai0pAF4lXRq0bmhbes1u9R8wTE+g+183bZNM= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.41 h1:22dGT7PneFMx4+b3pz7lMTRyN8ZKH7M2cW4GP9yUS2g= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.41/go.mod h1:CrObHAuPneJBlfEJ5T3szXOUkLEThaGfvnhTf33buas= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.3 h1:9stUQR/u2KXU6HkFJYlqnZEjBnbgrVbG6I5HN09xZh0= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.3/go.mod h1:ssOhaLpRlh88H3UmEcsBoVKq309quMvm3Ds8e9d4eJM= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.35 h1:SijA0mgjV8E+8G45ltVHs0fvKpTj8xmZJ3VwhGKtUSI= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.35/go.mod h1:SJC1nEVVva1g3pHAIdCp7QsRIkMmLAgoDquQ9Rr8kYw= @@ -561,8 +557,6 @@ github.com/aws/etcdadm-bootstrap-provider v1.0.7-rc3/go.mod h1:PN7CO02LPlWz02Bjs github.com/aws/etcdadm-controller v1.0.6-rc3 h1:hTu0pagWPU467scMtaR2rmaNIgMcFMNeGYZAJvFa8g0= github.com/aws/etcdadm-controller v1.0.6-rc3/go.mod h1:60QVQeYClyeV22MpI+SMBDx/dXVf/pZNdyiWDM2OBZc= github.com/aws/smithy-go v1.11.2/go.mod h1:3xHYmszWVx2c0kIwQeEVf9uSm4fYZt67FBJnwub1bgM= -github.com/aws/smithy-go v1.13.2 h1:TBLKyeJfXTrTXRHmsv4qWt9IQGYyWThLYaJWSahTOGE= -github.com/aws/smithy-go v1.13.2/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= github.com/aws/smithy-go v1.14.2 h1:MJU9hqBGbvWZdApzpvoF2WAIJDbtjK2NDJSiJP7HblQ= github.com/aws/smithy-go v1.14.2/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= diff --git a/pkg/api/v1alpha1/cluster_defaults.go b/pkg/api/v1alpha1/cluster_defaults.go index 785071f4dab1..6763ad31f674 100644 --- a/pkg/api/v1alpha1/cluster_defaults.go +++ b/pkg/api/v1alpha1/cluster_defaults.go @@ -13,6 +13,7 @@ var clusterDefaults = []func(*Cluster) error{ setRegistryMirrorConfigDefaults, setWorkerNodeGroupDefaults, setCNIConfigDefault, + setEtcdEncryptionConfigDefaults, } func setClusterDefaults(cluster *Cluster) error { diff --git a/pkg/api/v1alpha1/cluster_defaults_test.go b/pkg/api/v1alpha1/cluster_defaults_test.go index 52809f66b2dd..4fcae09d08c3 100644 --- a/pkg/api/v1alpha1/cluster_defaults_test.go +++ b/pkg/api/v1alpha1/cluster_defaults_test.go @@ -244,6 +244,75 @@ func TestSetClusterDefaults(t *testing.T) { }, wantErr: "", }, + { + name: "etcd encryption - no cachesize and timeout specified", + in: &Cluster{ + TypeMeta: metav1.TypeMeta{ + Kind: ClusterKind, + APIVersion: SchemeBuilder.GroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "eksa-unit-test", + }, + Spec: ClusterSpec{ + KubernetesVersion: Kube119, + WorkerNodeGroupConfigurations: []WorkerNodeGroupConfiguration{{ + Name: "worker-0", + Count: ptr.Int(1), + }}, + EtcdEncryption: &[]EtcdEncryption{ + { + Providers: []EtcdEncryptionProvider{ + { + KMS: &KMS{ + Name: "test-config", + SocketListenAddress: "unix:///kms/socket/path", + }, + }, + }, + Resources: []string{"secrets"}, + }, + }, + }, + }, + wantCluster: &Cluster{ + TypeMeta: metav1.TypeMeta{ + Kind: ClusterKind, + APIVersion: SchemeBuilder.GroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "eksa-unit-test", + }, + Spec: ClusterSpec{ + KubernetesVersion: Kube119, + ClusterNetwork: ClusterNetwork{ + CNIConfig: &CNIConfig{ + Cilium: nil, + }, + }, + WorkerNodeGroupConfigurations: []WorkerNodeGroupConfiguration{{ + Name: "worker-0", + Count: ptr.Int(1), + }}, + EtcdEncryption: &[]EtcdEncryption{ + { + Providers: []EtcdEncryptionProvider{ + { + KMS: &KMS{ + Name: "test-config", + SocketListenAddress: "unix:///kms/socket/path", + CacheSize: defaultKMSCacheSize, + Timeout: &defaultKMSTimeout, + }, + }, + }, + Resources: []string{"secrets"}, + }, + }, + }, + }, + wantErr: "", + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/pkg/api/v1alpha1/cluster_webhook.go b/pkg/api/v1alpha1/cluster_webhook.go index c9b5687da64a..703fbcd0ef44 100644 --- a/pkg/api/v1alpha1/cluster_webhook.go +++ b/pkg/api/v1alpha1/cluster_webhook.go @@ -66,12 +66,12 @@ func (r *Cluster) ValidateCreate() error { return apierrors.NewBadRequest("creating new cluster on existing cluster is not supported for self managed clusters") } - if err := r.Validate(); err != nil { - allErrs = append(allErrs, field.Invalid(field.NewPath("spec"), r.Spec, err.Error())) + if r.Spec.EtcdEncryption != nil { + allErrs = append(allErrs, field.Invalid(field.NewPath("spec.etcdEncryption"), r.Spec.EtcdEncryption, "etcdEncryption is not supported during cluster creation")) } - if r.Spec.EtcdEncryption != nil { - allErrs = append(allErrs, field.Invalid(field.NewPath("spec"), r.Spec, "etcdEncryption is not supported during cluster creation")) + if err := r.Validate(); err != nil { + allErrs = append(allErrs, field.Invalid(field.NewPath("spec"), r.Spec, err.Error())) } if len(allErrs) != 0 { @@ -107,8 +107,8 @@ func (r *Cluster) ValidateUpdate(old runtime.Object) error { allErrs = append(allErrs, ValidateWorkerKubernetesVersionSkew(r, oldCluster)...) - if err := validateEtcdEncryptionConfig(r.Spec.EtcdEncryption); err != nil { - allErrs = append(allErrs, field.Invalid(field.NewPath("spec"), r.Spec, err.Error())) + if err := ValidateEtcdEncryptionConfig(r.Spec.EtcdEncryption); err != nil { + allErrs = append(allErrs, field.Invalid(field.NewPath("spec.etcdEncryption"), r.Spec.EtcdEncryption, err.Error())) } if len(allErrs) != 0 { diff --git a/pkg/api/v1alpha1/cluster_webhook_test.go b/pkg/api/v1alpha1/cluster_webhook_test.go index 1012d58d8375..feefe38afae1 100644 --- a/pkg/api/v1alpha1/cluster_webhook_test.go +++ b/pkg/api/v1alpha1/cluster_webhook_test.go @@ -6,6 +6,7 @@ import ( "testing" . "github.com/onsi/gomega" + "github.com/pkg/errors" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" _ "k8s.io/apimachinery/pkg/runtime" @@ -1187,9 +1188,7 @@ func TestClusterCreateEtcdEncryption(t *testing.T) { Count: 3, Endpoint: &v1alpha1.Endpoint{Host: "1.1.1.1/1"}, }, ExternalEtcdConfiguration: &v1alpha1.ExternalEtcdConfiguration{Count: 3}, - EtcdEncryption: &[]v1alpha1.EtcdEncryption{ - {}, - }, + EtcdEncryption: &[]v1alpha1.EtcdEncryption{}, ManagementCluster: v1alpha1.ManagementCluster{ Name: "management-cluster", }, @@ -1202,34 +1201,129 @@ func TestClusterCreateEtcdEncryption(t *testing.T) { func TestClusterUpdateEtcdEncryption(t *testing.T) { features.ClearCache() - workerConfiguration := append([]v1alpha1.WorkerNodeGroupConfiguration{}, v1alpha1.WorkerNodeGroupConfiguration{Count: ptr.Int(5)}) - baseCluster := &v1alpha1.Cluster{ - Spec: v1alpha1.ClusterSpec{ - WorkerNodeGroupConfigurations: workerConfiguration, - KubernetesVersion: v1alpha1.Kube119, - ControlPlaneConfiguration: v1alpha1.ControlPlaneConfiguration{ - Count: 3, Endpoint: &v1alpha1.Endpoint{Host: "1.1.1.1/1"}, - }, - ExternalEtcdConfiguration: &v1alpha1.ExternalEtcdConfiguration{Count: 3}, - - ManagementCluster: v1alpha1.ManagementCluster{ - Name: "management-cluster", - }, - }, - } + resources := []string{"secrets"} tests := []struct { testName string - expectedErr string + expectedErr error encryptionConfig *[]v1alpha1.EtcdEncryption }{ { - testName: "zero_encryption_configs", + testName: "empty_encryption_configs", + expectedErr: errors.New("etcdEncryption cannot be empty"), encryptionConfig: &[]v1alpha1.EtcdEncryption{}, - expectedErr: "invalid number of encryption providers, currently only 1 encryption provider is supported", }, { - testName: "two_encryption_configs", + testName: "invalid_encryption_config_empty_provider", + expectedErr: errors.New("etcdEncryption[0].providers cannot be empty"), + encryptionConfig: &[]v1alpha1.EtcdEncryption{ + { + Providers: []v1alpha1.EtcdEncryptionProvider{}, + Resources: resources, + }, + }, + }, + { + testName: "invalid_kms_empty_config", + expectedErr: errors.New("etcdEncryption[0].providers[0] is invalid: kms cannot be nil"), + encryptionConfig: &[]v1alpha1.EtcdEncryption{ + { + Providers: []v1alpha1.EtcdEncryptionProvider{ + { + KMS: nil, + }, + }, + Resources: resources, + }, + }, + }, + { + testName: "invalid_kms_config_empty_name", + expectedErr: errors.New("etcdEncryption[0].providers[0] is invalid: kms.name cannot be empty"), + encryptionConfig: &[]v1alpha1.EtcdEncryption{ + { + Providers: []v1alpha1.EtcdEncryptionProvider{ + { + KMS: &v1alpha1.KMS{ + Name: "", + SocketListenAddress: "unix:///abc", + }, + }, + }, + Resources: resources, + }, + }, + }, + { + testName: "invalid_kms_config_empty_socket_address", + expectedErr: errors.New("etcdEncryption[0].providers[0] is invalid: kms.socketListenAddress cannot be empty"), + encryptionConfig: &[]v1alpha1.EtcdEncryption{ + { + Providers: []v1alpha1.EtcdEncryptionProvider{ + { + KMS: &v1alpha1.KMS{ + Name: "test-kms-config", + SocketListenAddress: "", + }, + }, + }, + Resources: resources, + }, + }, + }, + { + testName: "invalid_kms_config_invalid_socket_address_path", + expectedErr: errors.New("etcdEncryption[0].providers[0] is invalid: kms.socketListenAddress is malformed"), + encryptionConfig: &[]v1alpha1.EtcdEncryption{ + { + Providers: []v1alpha1.EtcdEncryptionProvider{ + { + KMS: &v1alpha1.KMS{ + Name: "test-kms-config", + SocketListenAddress: "unix:// invalid", + }, + }, + }, + Resources: resources, + }, + }, + }, + { + testName: "invalid_kms_config_invalid_socket_address_scheme", + expectedErr: errors.New("etcdEncryption[0].providers[0] is invalid: kms.socketListenAddress has unsupported scheme: linux"), + encryptionConfig: &[]v1alpha1.EtcdEncryption{ + { + Providers: []v1alpha1.EtcdEncryptionProvider{ + { + KMS: &v1alpha1.KMS{ + Name: "test-kms-config", + SocketListenAddress: "linux:///invalid/path", + }, + }, + }, + Resources: resources, + }, + }, + }, + { + testName: "invalid_config_empty_resources", + expectedErr: errors.New("etcdEncryption[0].resources cannot be empty"), + encryptionConfig: &[]v1alpha1.EtcdEncryption{ + { + Providers: []v1alpha1.EtcdEncryptionProvider{ + { + KMS: &v1alpha1.KMS{ + Name: "test_config", + SocketListenAddress: "unix:///abc", + }, + }, + }, + }, + }, + }, + { + testName: "two_encryption_configs", + expectedErr: errors.New("etcdEncryption config is invalid, only 1 encryption config is supported currently"), encryptionConfig: &[]v1alpha1.EtcdEncryption{ { Providers: []v1alpha1.EtcdEncryptionProvider{}, @@ -1240,19 +1334,62 @@ func TestClusterUpdateEtcdEncryption(t *testing.T) { Resources: []string{}, }, }, - expectedErr: "invalid number of encryption providers, currently only 1 encryption provider is supported", + }, + { + testName: "two_encryption_providers", + expectedErr: errors.New("etcdEncryption[0].providers in invalid, only 1 encryption provider is currently supported"), + encryptionConfig: &[]v1alpha1.EtcdEncryption{ + { + Providers: []v1alpha1.EtcdEncryptionProvider{ + { + KMS: &v1alpha1.KMS{ + Name: "test_config1", + SocketListenAddress: "unix:///abc", + }, + }, + { + KMS: &v1alpha1.KMS{ + Name: "test_config2", + SocketListenAddress: "unix:///abc", + }, + }, + }, + Resources: resources, + }, + }, + }, + { + testName: "valid_config", + expectedErr: nil, + encryptionConfig: &[]v1alpha1.EtcdEncryption{ + { + Providers: []v1alpha1.EtcdEncryptionProvider{ + { + KMS: &v1alpha1.KMS{ + Name: "test_config", + SocketListenAddress: "unix:///abc", + }, + }, + }, + Resources: resources, + }, + }, }, } for _, tt := range tests { t.Run(tt.testName, func(t *testing.T) { + baseCluster := baseCluster() newCluster := baseCluster.DeepCopy() newCluster.Spec.EtcdEncryption = tt.encryptionConfig g := NewWithT(t) err := newCluster.ValidateUpdate(baseCluster) - t.Log(err.Error()) - g.Expect(err).To(MatchError(ContainSubstring(tt.expectedErr))) + if tt.expectedErr == nil { + g.Expect(err).ToNot(HaveOccurred()) + } else { + g.Expect(err).To(MatchError(ContainSubstring(tt.expectedErr.Error()))) + } }) } } diff --git a/pkg/api/v1alpha1/etcdencryption.go b/pkg/api/v1alpha1/etcdencryption.go new file mode 100644 index 000000000000..3fee4c7428f1 --- /dev/null +++ b/pkg/api/v1alpha1/etcdencryption.go @@ -0,0 +1,93 @@ +package v1alpha1 + +import ( + "net/url" + "time" + + "github.com/pkg/errors" + + "github.com/aws/eks-anywhere/pkg/utils/ptr" +) + +var ( + defaultKMSCacheSize = ptr.Int32(1000) + defaultKMSTimeout = time.Second * 3 +) + +// ValidateEtcdEncryptionConfig validates the etcd encryption configuration. +func ValidateEtcdEncryptionConfig(config *[]EtcdEncryption) error { + if config == nil { + return nil + } + + if len(*config) == 0 { + return errors.New("etcdEncryption cannot be empty") + } + + if len(*config) != 1 { + return errors.New("etcdEncryption config is invalid, only 1 encryption config is supported currently") + } + + for i, c := range *config { + if len(c.Providers) == 0 { + return errors.Errorf("etcdEncryption[%d].providers cannot be empty", i) + } + if len(c.Providers) != 1 { + return errors.Errorf("etcdEncryption[%d].providers in invalid, only 1 encryption provider is currently supported", i) + } + for j, p := range c.Providers { + if err := validateKMSConfig(p.KMS); err != nil { + return errors.Errorf("etcdEncryption[%d].providers[%d] is invalid: %v", i, j, err) + } + } + if len(c.Resources) == 0 { + return errors.Errorf("etcdEncryption[%d].resources cannot be empty", i) + } + } + + return nil +} + +func validateKMSConfig(kms *KMS) error { + if kms == nil { + return errors.New("kms cannot be nil") + } + if len(kms.Name) == 0 { + return errors.New("kms.name cannot be empty") + } + if len(kms.SocketListenAddress) == 0 { + return errors.New("kms.socketListenAddress cannot be empty") + } + u, err := url.Parse(kms.SocketListenAddress) + if err != nil { + return errors.Errorf("kms.socketListenAddress is malformed: %v", err) + } + if u.Scheme != "unix" { + return errors.Errorf("kms.socketListenAddress has unsupported scheme: %v", u.Scheme) + } + return nil +} + +func setEtcdEncryptionConfigDefaults(cluster *Cluster) error { + if cluster.Spec.EtcdEncryption == nil { + return nil + } + + for _, c := range *cluster.Spec.EtcdEncryption { + for _, p := range c.Providers { + setKMSConfigDefauts(p.KMS) + } + } + return nil +} + +func setKMSConfigDefauts(kms *KMS) { + if kms != nil { + if kms.CacheSize == nil { + kms.CacheSize = defaultKMSCacheSize + } + if kms.Timeout == nil { + kms.Timeout = &defaultKMSTimeout + } + } +} diff --git a/pkg/api/v1alpha1/etcdencryption_types.go b/pkg/api/v1alpha1/etcdencryption_types.go index f0d657f153db..c530f639275d 100644 --- a/pkg/api/v1alpha1/etcdencryption_types.go +++ b/pkg/api/v1alpha1/etcdencryption_types.go @@ -1,5 +1,9 @@ package v1alpha1 +import ( + "time" +) + // EtcdEncryption defines the configuration for ETCD encryption. type EtcdEncryption struct { Providers []EtcdEncryptionProvider `json:"providers"` @@ -16,6 +20,13 @@ type EtcdEncryptionProvider struct { // KMS defines the configuration for KMS Encryption provider. type KMS struct { + // CacheSize defines the maximum number of encrypted objects to be cached in memory. The default value is 1000. + // You can set this to a negative value to disable caching. + CacheSize *int32 `json:"cachesize,omitempty"` + // Name defines the name of KMS plugin to be used. + Name string `json:"name"` // SocketListenAddress defines a UNIX socket address that the KMS provider listens on. SocketListenAddress string `json:"socketListenAddress"` + // Timeout for kube-apiserver to wait for KMS plugin. Default is 3s. + Timeout *time.Duration `json:"timeout,omitempty"` } diff --git a/pkg/api/v1alpha1/etcdncryption.go b/pkg/api/v1alpha1/etcdncryption.go deleted file mode 100644 index 11b3f6dae6fc..000000000000 --- a/pkg/api/v1alpha1/etcdncryption.go +++ /dev/null @@ -1,15 +0,0 @@ -package v1alpha1 - -import "github.com/pkg/errors" - -func validateEtcdEncryptionConfig(config *[]EtcdEncryption) error { - if config == nil { - return nil - } - - if len(*config) != 1 { - return errors.New("invalid number of encryption providers, currently only 1 encryption provider is supported") - } - - return nil -} diff --git a/pkg/api/v1alpha1/zz_generated.deepcopy.go b/pkg/api/v1alpha1/zz_generated.deepcopy.go index 553c4d73e8aa..3ef8acfd9058 100644 --- a/pkg/api/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/api/v1alpha1/zz_generated.deepcopy.go @@ -26,6 +26,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "sigs.k8s.io/cluster-api/api/v1beta1" apiv1beta1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1beta1" + timex "time" ) // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. @@ -1082,7 +1083,7 @@ func (in *EtcdEncryptionProvider) DeepCopyInto(out *EtcdEncryptionProvider) { if in.KMS != nil { in, out := &in.KMS, &out.KMS *out = new(KMS) - **out = **in + (*in).DeepCopyInto(*out) } } @@ -1450,6 +1451,16 @@ func (in *ImageResource) DeepCopy() *ImageResource { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *KMS) DeepCopyInto(out *KMS) { *out = *in + if in.CacheSize != nil { + in, out := &in.CacheSize, &out.CacheSize + *out = new(int32) + **out = **in + } + if in.Timeout != nil { + in, out := &in.Timeout, &out.Timeout + *out = new(timex.Duration) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KMS.