From b5bffb6d97f9b6eaa54137da287c845e1b56e854 Mon Sep 17 00:00:00 2001 From: "Lubomir I. Ivanov" Date: Sun, 27 Aug 2023 16:55:40 +0300 Subject: [PATCH] kubeadm: add v1beta4.ClusterConfiguration.EncryptionAlgorithm Add v1beta4.ClusterConfiguration.EncryptionAlgorithm field (string) and allow the user to configure the cluster asymetric encryption algorithm to be either "RSA" (default, 2048 pkey size) or "ECDSA" (P-256). Add validation and fuzzing. Conversion from v1beta3 is not required because an empty field value is accepted and defaulted to RSA if needed. Leverage the existing configuration option (feature gate) PublicKeysECDSA but rename the backend fields, arguments, function names to be more generic - EncryptionAlgorithm instead of PublicKeyAlgorithm. That is because once the feature gate is enabled the algorithm configuration also applies to private keys. It also uses the kubeadm API type (string) instead of the x509.PublicKeyAlgorithm enum (int). Deprecate the PublicKeysECDSA feature gate with a message. It should be removed with the release of v1beta4 or maximum one release later (it is an alpha FG). --- cmd/kubeadm/app/apis/kubeadm/fuzzer/fuzzer.go | 1 + cmd/kubeadm/app/apis/kubeadm/types.go | 33 ++++++++++++---- .../app/apis/kubeadm/v1beta3/conversion.go | 17 +++++++++ .../v1beta3/zz_generated.conversion.go | 31 ++++++--------- .../app/apis/kubeadm/v1beta4/defaults.go | 7 ++++ cmd/kubeadm/app/apis/kubeadm/v1beta4/doc.go | 3 ++ cmd/kubeadm/app/apis/kubeadm/v1beta4/types.go | 15 ++++++++ .../v1beta4/zz_generated.conversion.go | 2 + .../app/apis/kubeadm/validation/validation.go | 12 ++++++ .../kubeadm/validation/validation_test.go | 38 ++++++++++++++++--- cmd/kubeadm/app/cmd/phases/init/certs.go | 2 +- cmd/kubeadm/app/features/features.go | 5 ++- cmd/kubeadm/app/phases/certs/certlist.go | 2 +- cmd/kubeadm/app/phases/certs/certs.go | 4 +- cmd/kubeadm/app/phases/certs/certs_test.go | 2 +- .../app/phases/certs/renewal/manager.go | 8 ++-- .../app/phases/kubeconfig/kubeconfig.go | 2 +- cmd/kubeadm/app/util/pkiutil/pki_helpers.go | 18 ++++----- .../app/util/pkiutil/pki_helpers_test.go | 6 +-- .../app/util/pkiutil/testing/testing.go | 6 +-- 20 files changed, 154 insertions(+), 60 deletions(-) diff --git a/cmd/kubeadm/app/apis/kubeadm/fuzzer/fuzzer.go b/cmd/kubeadm/app/apis/kubeadm/fuzzer/fuzzer.go index 38b7358d48a..60e1f44032d 100644 --- a/cmd/kubeadm/app/apis/kubeadm/fuzzer/fuzzer.go +++ b/cmd/kubeadm/app/apis/kubeadm/fuzzer/fuzzer.go @@ -91,6 +91,7 @@ func fuzzClusterConfiguration(obj *kubeadm.ClusterConfiguration, c fuzz.Continue obj.APIServer.ExtraEnvs = []kubeadm.EnvVar{} obj.Scheduler.ExtraEnvs = []kubeadm.EnvVar{} obj.Etcd.Local.ExtraEnvs = []kubeadm.EnvVar{} + obj.EncryptionAlgorithm = kubeadm.EncryptionAlgorithmRSA } func fuzzDNS(obj *kubeadm.DNS, c fuzz.Continue) { diff --git a/cmd/kubeadm/app/apis/kubeadm/types.go b/cmd/kubeadm/app/apis/kubeadm/types.go index 8b845239173..9a157b79f2c 100644 --- a/cmd/kubeadm/app/apis/kubeadm/types.go +++ b/cmd/kubeadm/app/apis/kubeadm/types.go @@ -17,8 +17,6 @@ limitations under the License. package kubeadm import ( - "crypto/x509" - v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" @@ -141,6 +139,10 @@ type ClusterConfiguration struct { // The cluster name ClusterName string + + // EncryptionAlgorithm holds the type of asymmetric encryption algorithm used for keys and certificates. + // Can be "RSA" (default algorithm, key size is 2048) or "ECDSA" (uses the P-256 eliptic curve). + EncryptionAlgorithm EncryptionAlgorithmType } // ControlPlaneComponent holds settings common to control plane component of the cluster @@ -403,13 +405,18 @@ func (cfg *ClusterConfiguration) GetControlPlaneImageRepository() string { return cfg.ImageRepository } -// PublicKeyAlgorithm returns the type of encryption keys used in the cluster. -func (cfg *ClusterConfiguration) PublicKeyAlgorithm() x509.PublicKeyAlgorithm { - if features.Enabled(cfg.FeatureGates, features.PublicKeysECDSA) { - return x509.ECDSA +// EncryptionAlgorithmType returns the type of encryption keys used in the cluster. +func (cfg *ClusterConfiguration) EncryptionAlgorithmType() EncryptionAlgorithmType { + // If the feature gate is set to true, or false respect it. + // If the feature gate is not set, use the EncryptionAlgorithm field (v1beta4). + // TODO: remove this function when the feature gate is removed. + if enabled, ok := cfg.FeatureGates[features.PublicKeysECDSA]; ok { + if enabled { + return EncryptionAlgorithmECDSA + } + return EncryptionAlgorithmRSA } - - return x509.RSA + return cfg.EncryptionAlgorithm } // HostPathMount contains elements describing volumes that are mounted from the @@ -518,3 +525,13 @@ type Arg struct { type EnvVar struct { v1.EnvVar } + +// EncryptionAlgorithmType can define an asymmetric encryption algorithm type. +type EncryptionAlgorithmType string + +const ( + // EncryptionAlgorithmECDSA defines the ECDSA encryption algorithm type. + EncryptionAlgorithmECDSA EncryptionAlgorithmType = "ECDSA" + // EncryptionAlgorithmRSA defines the RSA encryption algorithm type. + EncryptionAlgorithmRSA EncryptionAlgorithmType = "RSA" +) diff --git a/cmd/kubeadm/app/apis/kubeadm/v1beta3/conversion.go b/cmd/kubeadm/app/apis/kubeadm/v1beta3/conversion.go index 7114c938778..840ea16ab28 100644 --- a/cmd/kubeadm/app/apis/kubeadm/v1beta3/conversion.go +++ b/cmd/kubeadm/app/apis/kubeadm/v1beta3/conversion.go @@ -38,9 +38,26 @@ func Convert_v1beta3_InitConfiguration_To_kubeadm_InitConfiguration(in *InitConf return err } err = Convert_v1beta3_ClusterConfiguration_To_kubeadm_ClusterConfiguration(&ClusterConfiguration{}, &out.ClusterConfiguration, s) + // Required to pass fuzzer tests. This ClusterConfiguration is empty and is never defaulted. + // If we call Convert_v1beta3_ClusterConfiguration_To_kubeadm_ClusterConfiguration() it will receive + // a default value, thus here we need to reset it back to "". + out.EncryptionAlgorithm = "" return err } +// Convert_kubeadm_ClusterConfiguration_To_v1beta3_ClusterConfiguration is required due to missing EncryptionAlgorithm in v1beta3. +func Convert_kubeadm_ClusterConfiguration_To_v1beta3_ClusterConfiguration(in *kubeadm.ClusterConfiguration, out *ClusterConfiguration, s conversion.Scope) error { + return autoConvert_kubeadm_ClusterConfiguration_To_v1beta3_ClusterConfiguration(in, out, s) +} + +// Convert_v1beta3_ClusterConfiguration_To_kubeadm_ClusterConfiguration is required due to missing EncryptionAlgorithm in v1beta3. +func Convert_v1beta3_ClusterConfiguration_To_kubeadm_ClusterConfiguration(in *ClusterConfiguration, out *kubeadm.ClusterConfiguration, s conversion.Scope) error { + // Required to pass validation and fuzzer tests. The field is missing in v1beta3, thus we have to + // default it to a sane (default) value in the internal type. + out.EncryptionAlgorithm = kubeadm.EncryptionAlgorithmRSA + return autoConvert_v1beta3_ClusterConfiguration_To_kubeadm_ClusterConfiguration(in, out, s) +} + // Convert_v1beta3_ControlPlaneComponent_To_kubeadm_ControlPlaneComponent is required due to the missing ControlPlaneComponent.ExtraEnvs in v1beta3. func Convert_v1beta3_ControlPlaneComponent_To_kubeadm_ControlPlaneComponent(in *ControlPlaneComponent, out *kubeadm.ControlPlaneComponent, s conversion.Scope) error { out.ExtraEnvs = []kubeadm.EnvVar{} diff --git a/cmd/kubeadm/app/apis/kubeadm/v1beta3/zz_generated.conversion.go b/cmd/kubeadm/app/apis/kubeadm/v1beta3/zz_generated.conversion.go index 7eb0fdf209b..95abd68c4a2 100644 --- a/cmd/kubeadm/app/apis/kubeadm/v1beta3/zz_generated.conversion.go +++ b/cmd/kubeadm/app/apis/kubeadm/v1beta3/zz_generated.conversion.go @@ -69,16 +69,6 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*ClusterConfiguration)(nil), (*kubeadm.ClusterConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta3_ClusterConfiguration_To_kubeadm_ClusterConfiguration(a.(*ClusterConfiguration), b.(*kubeadm.ClusterConfiguration), scope) - }); err != nil { - return err - } - if err := s.AddGeneratedConversionFunc((*kubeadm.ClusterConfiguration)(nil), (*ClusterConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_kubeadm_ClusterConfiguration_To_v1beta3_ClusterConfiguration(a.(*kubeadm.ClusterConfiguration), b.(*ClusterConfiguration), scope) - }); err != nil { - return err - } if err := s.AddGeneratedConversionFunc((*DNS)(nil), (*kubeadm.DNS)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta3_DNS_To_kubeadm_DNS(a.(*DNS), b.(*kubeadm.DNS), scope) }); err != nil { @@ -184,6 +174,11 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddConversionFunc((*kubeadm.ClusterConfiguration)(nil), (*ClusterConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_kubeadm_ClusterConfiguration_To_v1beta3_ClusterConfiguration(a.(*kubeadm.ClusterConfiguration), b.(*ClusterConfiguration), scope) + }); err != nil { + return err + } if err := s.AddConversionFunc((*kubeadm.ControlPlaneComponent)(nil), (*ControlPlaneComponent)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_kubeadm_ControlPlaneComponent_To_v1beta3_ControlPlaneComponent(a.(*kubeadm.ControlPlaneComponent), b.(*ControlPlaneComponent), scope) }); err != nil { @@ -209,6 +204,11 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddConversionFunc((*ClusterConfiguration)(nil), (*kubeadm.ClusterConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta3_ClusterConfiguration_To_kubeadm_ClusterConfiguration(a.(*ClusterConfiguration), b.(*kubeadm.ClusterConfiguration), scope) + }); err != nil { + return err + } if err := s.AddConversionFunc((*ControlPlaneComponent)(nil), (*kubeadm.ControlPlaneComponent)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1beta3_ControlPlaneComponent_To_kubeadm_ControlPlaneComponent(a.(*ControlPlaneComponent), b.(*kubeadm.ControlPlaneComponent), scope) }); err != nil { @@ -336,11 +336,6 @@ func autoConvert_v1beta3_ClusterConfiguration_To_kubeadm_ClusterConfiguration(in return nil } -// Convert_v1beta3_ClusterConfiguration_To_kubeadm_ClusterConfiguration is an autogenerated conversion function. -func Convert_v1beta3_ClusterConfiguration_To_kubeadm_ClusterConfiguration(in *ClusterConfiguration, out *kubeadm.ClusterConfiguration, s conversion.Scope) error { - return autoConvert_v1beta3_ClusterConfiguration_To_kubeadm_ClusterConfiguration(in, out, s) -} - func autoConvert_kubeadm_ClusterConfiguration_To_v1beta3_ClusterConfiguration(in *kubeadm.ClusterConfiguration, out *ClusterConfiguration, s conversion.Scope) error { // INFO: in.ComponentConfigs opted out of conversion generation if err := Convert_kubeadm_Etcd_To_v1beta3_Etcd(&in.Etcd, &out.Etcd, s); err != nil { @@ -369,14 +364,10 @@ func autoConvert_kubeadm_ClusterConfiguration_To_v1beta3_ClusterConfiguration(in // INFO: in.CIImageRepository opted out of conversion generation out.FeatureGates = *(*map[string]bool)(unsafe.Pointer(&in.FeatureGates)) out.ClusterName = in.ClusterName + // WARNING: in.EncryptionAlgorithm requires manual conversion: does not exist in peer-type return nil } -// Convert_kubeadm_ClusterConfiguration_To_v1beta3_ClusterConfiguration is an autogenerated conversion function. -func Convert_kubeadm_ClusterConfiguration_To_v1beta3_ClusterConfiguration(in *kubeadm.ClusterConfiguration, out *ClusterConfiguration, s conversion.Scope) error { - return autoConvert_kubeadm_ClusterConfiguration_To_v1beta3_ClusterConfiguration(in, out, s) -} - func autoConvert_v1beta3_ControlPlaneComponent_To_kubeadm_ControlPlaneComponent(in *ControlPlaneComponent, out *kubeadm.ControlPlaneComponent, s conversion.Scope) error { // WARNING: in.ExtraArgs requires manual conversion: inconvertible types (map[string]string vs []k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm.Arg) out.ExtraVolumes = *(*[]kubeadm.HostPathMount)(unsafe.Pointer(&in.ExtraVolumes)) diff --git a/cmd/kubeadm/app/apis/kubeadm/v1beta4/defaults.go b/cmd/kubeadm/app/apis/kubeadm/v1beta4/defaults.go index 53433863cdf..69484f526d1 100644 --- a/cmd/kubeadm/app/apis/kubeadm/v1beta4/defaults.go +++ b/cmd/kubeadm/app/apis/kubeadm/v1beta4/defaults.go @@ -60,6 +60,9 @@ const ( // DefaultImagePullPolicy is the default image pull policy in kubeadm DefaultImagePullPolicy = corev1.PullIfNotPresent + + // DefaultEncryptionAlgorithm is the default encryption algorithm. + DefaultEncryptionAlgorithm = EncryptionAlgorithmRSA ) func addDefaultingFuncs(scheme *runtime.Scheme) error { @@ -99,6 +102,10 @@ func SetDefaults_ClusterConfiguration(obj *ClusterConfiguration) { obj.ClusterName = DefaultClusterName } + if obj.EncryptionAlgorithm == "" { + obj.EncryptionAlgorithm = DefaultEncryptionAlgorithm + } + SetDefaults_Etcd(obj) SetDefaults_APIServer(&obj.APIServer) } diff --git a/cmd/kubeadm/app/apis/kubeadm/v1beta4/doc.go b/cmd/kubeadm/app/apis/kubeadm/v1beta4/doc.go index 511d946e72c..602a72e57c5 100644 --- a/cmd/kubeadm/app/apis/kubeadm/v1beta4/doc.go +++ b/cmd/kubeadm/app/apis/kubeadm/v1beta4/doc.go @@ -32,6 +32,9 @@ limitations under the License. // - Replace the existing string/string extra argument maps with structured extra arguments that support duplicates. // The change applies to `ClusterConfiguration` - `APIServer.ExtraArgs, `ControllerManager.ExtraArgs`, // `Scheduler.ExtraArgs`, `Etcd.Local.ExtraArgs`. Also to `NodeRegistrationOptions.KubeletExtraArgs`. +// - Add `ClusterConfiguration.EncryptionAlgorithm` that can be used to set the asymmetric encryption algorithm +// used for this cluster's keys and certificates. Can be "RSA" (default algorithm, key size is 2048) or +// "ECDSA" (uses the P-256 eliptic curve). // // Migration from old kubeadm config versions // diff --git a/cmd/kubeadm/app/apis/kubeadm/v1beta4/types.go b/cmd/kubeadm/app/apis/kubeadm/v1beta4/types.go index 8008d3b54d6..269912c012b 100644 --- a/cmd/kubeadm/app/apis/kubeadm/v1beta4/types.go +++ b/cmd/kubeadm/app/apis/kubeadm/v1beta4/types.go @@ -140,6 +140,11 @@ type ClusterConfiguration struct { // The cluster name // +optional ClusterName string `json:"clusterName,omitempty"` + + // EncryptionAlgorithm holds the type of asymmetric encryption algorithm used for keys and certificates. + // Can be "RSA" (default algorithm, key size is 2048) or "ECDSA" (uses the P-256 eliptic curve). + // +optional + EncryptionAlgorithm EncryptionAlgorithmType `json:"encryptionAlgorithm,omitempty"` } // ControlPlaneComponent holds settings common to control plane component of the cluster @@ -513,3 +518,13 @@ type Arg struct { type EnvVar struct { corev1.EnvVar `json:",inline"` } + +// EncryptionAlgorithmType can define an asymmetric encryption algorithm type. +type EncryptionAlgorithmType string + +const ( + // EncryptionAlgorithmECDSA defines the ECDSA encryption algorithm type. + EncryptionAlgorithmECDSA EncryptionAlgorithmType = "ECDSA" + // EncryptionAlgorithmRSA defines the RSA encryption algorithm type. + EncryptionAlgorithmRSA EncryptionAlgorithmType = "RSA" +) diff --git a/cmd/kubeadm/app/apis/kubeadm/v1beta4/zz_generated.conversion.go b/cmd/kubeadm/app/apis/kubeadm/v1beta4/zz_generated.conversion.go index 67bebb9bd95..ed01f88d9f6 100644 --- a/cmd/kubeadm/app/apis/kubeadm/v1beta4/zz_generated.conversion.go +++ b/cmd/kubeadm/app/apis/kubeadm/v1beta4/zz_generated.conversion.go @@ -385,6 +385,7 @@ func autoConvert_v1beta4_ClusterConfiguration_To_kubeadm_ClusterConfiguration(in out.ImageRepository = in.ImageRepository out.FeatureGates = *(*map[string]bool)(unsafe.Pointer(&in.FeatureGates)) out.ClusterName = in.ClusterName + out.EncryptionAlgorithm = kubeadm.EncryptionAlgorithmType(in.EncryptionAlgorithm) return nil } @@ -421,6 +422,7 @@ func autoConvert_kubeadm_ClusterConfiguration_To_v1beta4_ClusterConfiguration(in // INFO: in.CIImageRepository opted out of conversion generation out.FeatureGates = *(*map[string]bool)(unsafe.Pointer(&in.FeatureGates)) out.ClusterName = in.ClusterName + out.EncryptionAlgorithm = EncryptionAlgorithmType(in.EncryptionAlgorithm) return nil } diff --git a/cmd/kubeadm/app/apis/kubeadm/validation/validation.go b/cmd/kubeadm/app/apis/kubeadm/validation/validation.go index 23c0590d7c3..a8e1c625f83 100644 --- a/cmd/kubeadm/app/apis/kubeadm/validation/validation.go +++ b/cmd/kubeadm/app/apis/kubeadm/validation/validation.go @@ -72,6 +72,7 @@ func ValidateClusterConfiguration(c *kubeadm.ClusterConfiguration) field.ErrorLi allErrs = append(allErrs, ValidateHostPort(c.ControlPlaneEndpoint, field.NewPath("controlPlaneEndpoint"))...) allErrs = append(allErrs, ValidateImageRepository(c.ImageRepository, field.NewPath("imageRepository"))...) allErrs = append(allErrs, ValidateEtcd(&c.Etcd, field.NewPath("etcd"))...) + allErrs = append(allErrs, ValidateEncryptionAlgorithm(string(c.EncryptionAlgorithm), field.NewPath("encryptionAlgorithm"))...) allErrs = append(allErrs, componentconfigs.Validate(c)...) return allErrs } @@ -337,6 +338,17 @@ func ValidateEtcd(e *kubeadm.Etcd, fldPath *field.Path) field.ErrorList { return allErrs } +// ValidateEncryptionAlgorithm validates the public key algorithm +func ValidateEncryptionAlgorithm(algo string, fldPath *field.Path) field.ErrorList { + allErrs := field.ErrorList{} + if algo != string(kubeadm.EncryptionAlgorithmRSA) && algo != string(kubeadm.EncryptionAlgorithmECDSA) { + msg := fmt.Sprintf("Invalid encryption algorithm. Must be %q or %q", + kubeadm.EncryptionAlgorithmRSA, kubeadm.EncryptionAlgorithmECDSA) + allErrs = append(allErrs, field.Invalid(fldPath, algo, msg)) + } + return allErrs +} + // ValidateCertSANs validates alternative names func ValidateCertSANs(altnames []string, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} diff --git a/cmd/kubeadm/app/apis/kubeadm/validation/validation_test.go b/cmd/kubeadm/app/apis/kubeadm/validation/validation_test.go index c6ddf38e72b..32ffe5a2320 100644 --- a/cmd/kubeadm/app/apis/kubeadm/validation/validation_test.go +++ b/cmd/kubeadm/app/apis/kubeadm/validation/validation_test.go @@ -513,7 +513,8 @@ func TestValidateInitConfiguration(t *testing.T) { ServiceSubnet: "10.96.0.1/12", DNSDomain: "cluster.local", }, - CertificatesDir: "/some/cert/dir", + CertificatesDir: "/some/cert/dir", + EncryptionAlgorithm: kubeadmapi.EncryptionAlgorithmRSA, }, NodeRegistration: kubeadmapi.NodeRegistrationOptions{Name: nodename, CRISocket: criPath}, }, false}, @@ -528,7 +529,8 @@ func TestValidateInitConfiguration(t *testing.T) { ServiceSubnet: "2001:db8::1/98", DNSDomain: "cluster.local", }, - CertificatesDir: "/some/cert/dir", + CertificatesDir: "/some/cert/dir", + EncryptionAlgorithm: kubeadmapi.EncryptionAlgorithmRSA, }, NodeRegistration: kubeadmapi.NodeRegistrationOptions{Name: nodename, CRISocket: criPath}, }, false}, @@ -543,7 +545,8 @@ func TestValidateInitConfiguration(t *testing.T) { ServiceSubnet: "10.96.0.1/12", DNSDomain: "cluster.local", }, - CertificatesDir: "/some/other/cert/dir", + CertificatesDir: "/some/other/cert/dir", + EncryptionAlgorithm: kubeadmapi.EncryptionAlgorithmRSA, }, }, false}, {"valid InitConfiguration with incorrect IPv4 pod subnet", @@ -558,7 +561,8 @@ func TestValidateInitConfiguration(t *testing.T) { DNSDomain: "cluster.local", PodSubnet: "10.0.1.15", }, - CertificatesDir: "/some/other/cert/dir", + CertificatesDir: "/some/other/cert/dir", + EncryptionAlgorithm: kubeadmapi.EncryptionAlgorithmRSA, }, NodeRegistration: kubeadmapi.NodeRegistrationOptions{Name: nodename, CRISocket: criPath}, }, false}, @@ -580,7 +584,8 @@ func TestValidateInitConfiguration(t *testing.T) { DNSDomain: "cluster.local", PodSubnet: "10.0.1.15/16", }, - CertificatesDir: "/some/other/cert/dir", + CertificatesDir: "/some/other/cert/dir", + EncryptionAlgorithm: kubeadmapi.EncryptionAlgorithmRSA, }, NodeRegistration: kubeadmapi.NodeRegistrationOptions{Name: nodename, CRISocket: criPath}, }, true}, @@ -601,7 +606,8 @@ func TestValidateInitConfiguration(t *testing.T) { ServiceSubnet: "2001:db8::1/112", DNSDomain: "cluster.local", }, - CertificatesDir: "/some/other/cert/dir", + CertificatesDir: "/some/other/cert/dir", + EncryptionAlgorithm: kubeadmapi.EncryptionAlgorithmECDSA, }, NodeRegistration: kubeadmapi.NodeRegistrationOptions{Name: nodename, CRISocket: criPath}, }, true}, @@ -1187,6 +1193,26 @@ func TestValidateEtcd(t *testing.T) { } } +func TestValidateEncryptionAlgorithm(t *testing.T) { + var tests = []struct { + name string + algo string + expectedErrors bool + }{ + {name: "valid RSA", algo: string(kubeadmapi.EncryptionAlgorithmRSA), expectedErrors: false}, + {name: "valid ECDSA", algo: string(kubeadmapi.EncryptionAlgorithmECDSA), expectedErrors: false}, + {name: "invalid algorithm", algo: "foo", expectedErrors: true}, + {name: "empty algorithm returns an error", algo: "", expectedErrors: true}, + } + for _, tc := range tests { + actual := ValidateEncryptionAlgorithm(tc.algo, field.NewPath("encryptionAlgorithm")) + actualErrors := len(actual) > 0 + if actualErrors != tc.expectedErrors { + t.Errorf("error: validate public key algorithm: %q\n\texpected: %t\n\t actual: %t", tc.algo, tc.expectedErrors, actualErrors) + } + } +} + func TestGetClusterNodeMask(t *testing.T) { tests := []struct { name string diff --git a/cmd/kubeadm/app/cmd/phases/init/certs.go b/cmd/kubeadm/app/cmd/phases/init/certs.go index 5d2ef1cc100..b499819dcf9 100644 --- a/cmd/kubeadm/app/cmd/phases/init/certs.go +++ b/cmd/kubeadm/app/cmd/phases/init/certs.go @@ -188,7 +188,7 @@ func runCertsSa(c workflow.RunData) error { } // create the new service account key (or use existing) - return certsphase.CreateServiceAccountKeyAndPublicKeyFiles(data.CertificateWriteDir(), data.Cfg().ClusterConfiguration.PublicKeyAlgorithm()) + return certsphase.CreateServiceAccountKeyAndPublicKeyFiles(data.CertificateWriteDir(), data.Cfg().ClusterConfiguration.EncryptionAlgorithmType()) } func runCerts(c workflow.RunData) error { diff --git a/cmd/kubeadm/app/features/features.go b/cmd/kubeadm/app/features/features.go index 5c007a708d4..5aa63424774 100644 --- a/cmd/kubeadm/app/features/features.go +++ b/cmd/kubeadm/app/features/features.go @@ -42,7 +42,10 @@ const ( // InitFeatureGates are the default feature gates for the init command var InitFeatureGates = FeatureList{ - PublicKeysECDSA: {FeatureSpec: featuregate.FeatureSpec{Default: false, PreRelease: featuregate.Alpha}}, + PublicKeysECDSA: { + FeatureSpec: featuregate.FeatureSpec{Default: false, PreRelease: featuregate.Deprecated}, + DeprecationMessage: "The PublicKeysECDSA feature gate is deprecated and will be removed after the feature 'ClusterConfiguration.EncryptionAlgorithm' is added.", + }, RootlessControlPlane: {FeatureSpec: featuregate.FeatureSpec{Default: false, PreRelease: featuregate.Alpha}}, EtcdLearnerMode: {FeatureSpec: featuregate.FeatureSpec{Default: true, PreRelease: featuregate.Beta}}, UpgradeAddonsBeforeControlPlane: { diff --git a/cmd/kubeadm/app/phases/certs/certlist.go b/cmd/kubeadm/app/phases/certs/certlist.go index 177d8224123..8c5d04665e3 100644 --- a/cmd/kubeadm/app/phases/certs/certlist.go +++ b/cmd/kubeadm/app/phases/certs/certlist.go @@ -60,7 +60,7 @@ func (k *KubeadmCert) GetConfig(ic *kubeadmapi.InitConfiguration) (*pkiutil.Cert } } - k.config.PublicKeyAlgorithm = ic.ClusterConfiguration.PublicKeyAlgorithm() + k.config.EncryptionAlgorithm = ic.ClusterConfiguration.EncryptionAlgorithmType() return &k.config, nil } diff --git a/cmd/kubeadm/app/phases/certs/certs.go b/cmd/kubeadm/app/phases/certs/certs.go index 7c823960fe5..ea4d34528c0 100644 --- a/cmd/kubeadm/app/phases/certs/certs.go +++ b/cmd/kubeadm/app/phases/certs/certs.go @@ -69,12 +69,12 @@ func CreatePKIAssets(cfg *kubeadmapi.InitConfiguration) error { fmt.Printf("[certs] Valid certificates and keys now exist in %q\n", cfg.CertificatesDir) // Service accounts are not x509 certs, so handled separately - return CreateServiceAccountKeyAndPublicKeyFiles(cfg.CertificatesDir, cfg.ClusterConfiguration.PublicKeyAlgorithm()) + return CreateServiceAccountKeyAndPublicKeyFiles(cfg.CertificatesDir, cfg.ClusterConfiguration.EncryptionAlgorithmType()) } // CreateServiceAccountKeyAndPublicKeyFiles creates new public/private key files for signing service account users. // If the sa public/private key files already exist in the target folder, they are used only if evaluated equals; otherwise an error is returned. -func CreateServiceAccountKeyAndPublicKeyFiles(certsDir string, keyType x509.PublicKeyAlgorithm) error { +func CreateServiceAccountKeyAndPublicKeyFiles(certsDir string, keyType kubeadmapi.EncryptionAlgorithmType) error { klog.V(1).Infoln("creating new public/private key files for signing service account users") _, err := keyutil.PrivateKeyFromFile(filepath.Join(certsDir, kubeadmconstants.ServiceAccountPrivateKeyName)) if err == nil { diff --git a/cmd/kubeadm/app/phases/certs/certs_test.go b/cmd/kubeadm/app/phases/certs/certs_test.go index a399551432b..755ff124ac8 100644 --- a/cmd/kubeadm/app/phases/certs/certs_test.go +++ b/cmd/kubeadm/app/phases/certs/certs_test.go @@ -347,7 +347,7 @@ func TestCreateServiceAccountKeyAndPublicKeyFiles(t *testing.T) { } } - err := CreateServiceAccountKeyAndPublicKeyFiles(dir, x509.RSA) + err := CreateServiceAccountKeyAndPublicKeyFiles(dir, kubeadmapi.EncryptionAlgorithmRSA) if (err != nil) != tt.expectedErr { t.Fatalf("expected error: %v, got: %v, error: %v", tt.expectedErr, err != nil, err) } else if tt.expectedErr { diff --git a/cmd/kubeadm/app/phases/certs/renewal/manager.go b/cmd/kubeadm/app/phases/certs/renewal/manager.go index 764bcb01a80..d7c420c45fd 100644 --- a/cmd/kubeadm/app/phases/certs/renewal/manager.go +++ b/cmd/kubeadm/app/phases/certs/renewal/manager.go @@ -227,8 +227,8 @@ func (rm *Manager) RenewUsingLocalCA(name string) (bool, error) { // extract the certificate config cfg := &pkiutil.CertConfig{ - Config: certToConfig(cert), - PublicKeyAlgorithm: rm.cfg.PublicKeyAlgorithm(), + Config: certToConfig(cert), + EncryptionAlgorithm: rm.cfg.EncryptionAlgorithmType(), } // reads the CA @@ -270,8 +270,8 @@ func (rm *Manager) CreateRenewCSR(name, outdir string) error { // extracts the certificate config cfg := &pkiutil.CertConfig{ - Config: certToConfig(cert), - PublicKeyAlgorithm: rm.cfg.PublicKeyAlgorithm(), + Config: certToConfig(cert), + EncryptionAlgorithm: rm.cfg.EncryptionAlgorithmType(), } // generates the CSR request and save it diff --git a/cmd/kubeadm/app/phases/kubeconfig/kubeconfig.go b/cmd/kubeadm/app/phases/kubeconfig/kubeconfig.go index eeac15bb958..af25d7cac3f 100644 --- a/cmd/kubeadm/app/phases/kubeconfig/kubeconfig.go +++ b/cmd/kubeadm/app/phases/kubeconfig/kubeconfig.go @@ -482,7 +482,7 @@ func createKubeConfigAndCSR(kubeConfigDir string, kubeadmConfig *kubeadmapi.Init clientCertConfig := newClientCertConfigFromKubeConfigSpec(spec, nil) - clientKey, err := pkiutil.NewPrivateKey(clientCertConfig.PublicKeyAlgorithm) + clientKey, err := pkiutil.NewPrivateKey(clientCertConfig.EncryptionAlgorithm) if err != nil { return err } diff --git a/cmd/kubeadm/app/util/pkiutil/pki_helpers.go b/cmd/kubeadm/app/util/pkiutil/pki_helpers.go index dc78d1b3f6b..cba7a41c7b9 100644 --- a/cmd/kubeadm/app/util/pkiutil/pki_helpers.go +++ b/cmd/kubeadm/app/util/pkiutil/pki_helpers.go @@ -60,16 +60,16 @@ const ( rsaKeySize = 2048 ) -// CertConfig is a wrapper around certutil.Config extending it with PublicKeyAlgorithm. +// CertConfig is a wrapper around certutil.Config extending it with EncryptionAlgorithm. type CertConfig struct { certutil.Config - NotAfter *time.Time - PublicKeyAlgorithm x509.PublicKeyAlgorithm + NotAfter *time.Time + EncryptionAlgorithm kubeadmapi.EncryptionAlgorithmType } // NewCertificateAuthority creates new certificate and private key for the certificate authority func NewCertificateAuthority(config *CertConfig) (*x509.Certificate, crypto.Signer, error) { - key, err := NewPrivateKey(config.PublicKeyAlgorithm) + key, err := NewPrivateKey(config.EncryptionAlgorithm) if err != nil { return nil, nil, errors.Wrap(err, "unable to create private key while generating CA certificate") } @@ -86,7 +86,7 @@ func NewCertificateAuthority(config *CertConfig) (*x509.Certificate, crypto.Sign // NewIntermediateCertificateAuthority creates new certificate and private key for an intermediate certificate authority func NewIntermediateCertificateAuthority(parentCert *x509.Certificate, parentKey crypto.Signer, config *CertConfig) (*x509.Certificate, crypto.Signer, error) { - key, err := NewPrivateKey(config.PublicKeyAlgorithm) + key, err := NewPrivateKey(config.EncryptionAlgorithm) if err != nil { return nil, nil, errors.Wrap(err, "unable to create private key while generating intermediate CA certificate") } @@ -105,7 +105,7 @@ func NewCertAndKey(caCert *x509.Certificate, caKey crypto.Signer, config *CertCo return nil, nil, errors.New("must specify at least one ExtKeyUsage") } - key, err := NewPrivateKey(config.PublicKeyAlgorithm) + key, err := NewPrivateKey(config.EncryptionAlgorithm) if err != nil { return nil, nil, errors.Wrap(err, "unable to create private key") } @@ -120,7 +120,7 @@ func NewCertAndKey(caCert *x509.Certificate, caKey crypto.Signer, config *CertCo // NewCSRAndKey generates a new key and CSR and that could be signed to create the given certificate func NewCSRAndKey(config *CertConfig) (*x509.CertificateRequest, crypto.Signer, error) { - key, err := NewPrivateKey(config.PublicKeyAlgorithm) + key, err := NewPrivateKey(config.EncryptionAlgorithm) if err != nil { return nil, nil, errors.Wrap(err, "unable to create private key") } @@ -623,8 +623,8 @@ func EncodePublicKeyPEM(key crypto.PublicKey) ([]byte, error) { // NewPrivateKey returns a new private key. var NewPrivateKey = GeneratePrivateKey -func GeneratePrivateKey(keyType x509.PublicKeyAlgorithm) (crypto.Signer, error) { - if keyType == x509.ECDSA { +func GeneratePrivateKey(keyType kubeadmapi.EncryptionAlgorithmType) (crypto.Signer, error) { + if keyType == kubeadmapi.EncryptionAlgorithmECDSA { return ecdsa.GenerateKey(elliptic.P256(), cryptorand.Reader) } diff --git a/cmd/kubeadm/app/util/pkiutil/pki_helpers_test.go b/cmd/kubeadm/app/util/pkiutil/pki_helpers_test.go index 9a4590814be..4b17b336104 100644 --- a/cmd/kubeadm/app/util/pkiutil/pki_helpers_test.go +++ b/cmd/kubeadm/app/util/pkiutil/pki_helpers_test.go @@ -52,7 +52,7 @@ func TestMain(m *testing.M) { Config: certutil.Config{ CommonName: "Root CA 1", }, - PublicKeyAlgorithm: x509.RSA, + EncryptionAlgorithm: kubeadmapi.EncryptionAlgorithmRSA, }) if err != nil { panic(fmt.Sprintf("Failed generating Root CA: %v", err)) @@ -112,7 +112,7 @@ func TestHasServerAuth(t *testing.T) { // Override NewPrivateKey to reuse the same key for all certs // since this test is only checking cert.ExtKeyUsage privateKeyFunc := NewPrivateKey - NewPrivateKey = func(x509.PublicKeyAlgorithm) (crypto.Signer, error) { + NewPrivateKey = func(kubeadmapi.EncryptionAlgorithmType) (crypto.Signer, error) { return rootCAKey, nil } defer func() { @@ -141,7 +141,7 @@ func TestHasServerAuth(t *testing.T) { CommonName: "test", Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, }, - PublicKeyAlgorithm: x509.ECDSA, + EncryptionAlgorithm: kubeadmapi.EncryptionAlgorithmECDSA, }, expected: true, }, diff --git a/cmd/kubeadm/app/util/pkiutil/testing/testing.go b/cmd/kubeadm/app/util/pkiutil/testing/testing.go index 6ddd9434323..588aaacaef9 100644 --- a/cmd/kubeadm/app/util/pkiutil/testing/testing.go +++ b/cmd/kubeadm/app/util/pkiutil/testing/testing.go @@ -18,7 +18,6 @@ package testing import ( "crypto" - "crypto/x509" "fmt" "os" "path/filepath" @@ -29,6 +28,7 @@ import ( "sync" "testing" + kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" "k8s.io/kubernetes/cmd/kubeadm/app/util/pkiutil" ) @@ -75,7 +75,7 @@ func install() (cleanup func()) { } } -func newPrivateKey(keyType x509.PublicKeyAlgorithm) (crypto.Signer, error) { +func newPrivateKey(keyType kubeadmapi.EncryptionAlgorithmType) (crypto.Signer, error) { lock.Lock() defer lock.Unlock() @@ -108,7 +108,7 @@ func newPrivateKey(keyType x509.PublicKeyAlgorithm) (crypto.Signer, error) { keyName := "" switch keyType { - case x509.ECDSA: + case kubeadmapi.EncryptionAlgorithmECDSA: ecdsa++ keyName = fmt.Sprintf("%d.ecdsa", ecdsa) default: