diff --git a/cmd/kubeadm/app/apis/kubeadm/fuzzer/fuzzer.go b/cmd/kubeadm/app/apis/kubeadm/fuzzer/fuzzer.go index c804505fe00..b80ac470c08 100644 --- a/cmd/kubeadm/app/apis/kubeadm/fuzzer/fuzzer.go +++ b/cmd/kubeadm/app/apis/kubeadm/fuzzer/fuzzer.go @@ -94,7 +94,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 + obj.EncryptionAlgorithm = kubeadm.EncryptionAlgorithmRSA2048 obj.Proxy.Disabled = false } diff --git a/cmd/kubeadm/app/apis/kubeadm/types.go b/cmd/kubeadm/app/apis/kubeadm/types.go index 2e2b334c2f9..52f6b6c6cf0 100644 --- a/cmd/kubeadm/app/apis/kubeadm/types.go +++ b/cmd/kubeadm/app/apis/kubeadm/types.go @@ -147,7 +147,7 @@ type ClusterConfiguration struct { 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 elliptic curve). + // Can be one of "RSA-2048" (default), "RSA-3072", "RSA-4096" or "ECDSA-P256". EncryptionAlgorithm EncryptionAlgorithmType } @@ -433,9 +433,9 @@ func (cfg *ClusterConfiguration) EncryptionAlgorithmType() EncryptionAlgorithmTy // TODO: remove this function when the feature gate is removed. if enabled, ok := cfg.FeatureGates[features.PublicKeysECDSA]; ok { if enabled { - return EncryptionAlgorithmECDSA + return EncryptionAlgorithmECDSAP256 } - return EncryptionAlgorithmRSA + return EncryptionAlgorithmRSA2048 } return cfg.EncryptionAlgorithm } @@ -570,10 +570,14 @@ type EnvVar struct { type EncryptionAlgorithmType string const ( - // EncryptionAlgorithmECDSA defines the ECDSA encryption algorithm type. - EncryptionAlgorithmECDSA EncryptionAlgorithmType = "ECDSA" - // EncryptionAlgorithmRSA defines the RSA encryption algorithm type. - EncryptionAlgorithmRSA EncryptionAlgorithmType = "RSA" + // EncryptionAlgorithmECDSAP256 defines the ECDSA encryption algorithm type with curve P256. + EncryptionAlgorithmECDSAP256 EncryptionAlgorithmType = "ECDSA-P256" + // EncryptionAlgorithmRSA2048 defines the RSA encryption algorithm type with key size 2048 bits. + EncryptionAlgorithmRSA2048 EncryptionAlgorithmType = "RSA-2048" + // EncryptionAlgorithmRSA3072 defines the RSA encryption algorithm type with key size 3072 bits. + EncryptionAlgorithmRSA3072 EncryptionAlgorithmType = "RSA-3072" + // EncryptionAlgorithmRSA4096 defines the RSA encryption algorithm type with key size 4096 bits. + EncryptionAlgorithmRSA4096 EncryptionAlgorithmType = "RSA-4096" ) // Timeouts holds various timeouts that apply to kubeadm commands. diff --git a/cmd/kubeadm/app/apis/kubeadm/v1beta3/conversion.go b/cmd/kubeadm/app/apis/kubeadm/v1beta3/conversion.go index 6d1cd7d8f19..b5c655d6099 100644 --- a/cmd/kubeadm/app/apis/kubeadm/v1beta3/conversion.go +++ b/cmd/kubeadm/app/apis/kubeadm/v1beta3/conversion.go @@ -66,7 +66,7 @@ func Convert_kubeadm_ClusterConfiguration_To_v1beta3_ClusterConfiguration(in *ku 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 + out.EncryptionAlgorithm = kubeadm.EncryptionAlgorithmRSA2048 return autoConvert_v1beta3_ClusterConfiguration_To_kubeadm_ClusterConfiguration(in, out, s) } diff --git a/cmd/kubeadm/app/apis/kubeadm/v1beta4/defaults.go b/cmd/kubeadm/app/apis/kubeadm/v1beta4/defaults.go index 529aa8cf948..9a9f6ce3857 100644 --- a/cmd/kubeadm/app/apis/kubeadm/v1beta4/defaults.go +++ b/cmd/kubeadm/app/apis/kubeadm/v1beta4/defaults.go @@ -60,7 +60,7 @@ const ( DefaultImagePullPolicy = corev1.PullIfNotPresent // DefaultEncryptionAlgorithm is the default encryption algorithm. - DefaultEncryptionAlgorithm = EncryptionAlgorithmRSA + DefaultEncryptionAlgorithm = EncryptionAlgorithmRSA2048 ) func addDefaultingFuncs(scheme *runtime.Scheme) error { diff --git a/cmd/kubeadm/app/apis/kubeadm/v1beta4/doc.go b/cmd/kubeadm/app/apis/kubeadm/v1beta4/doc.go index 257dee63c36..f3cb6831260 100644 --- a/cmd/kubeadm/app/apis/kubeadm/v1beta4/doc.go +++ b/cmd/kubeadm/app/apis/kubeadm/v1beta4/doc.go @@ -33,8 +33,7 @@ limitations under the License. // 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 elliptic curve). +// used for this cluster's keys and certificates. Can be one of "RSA-2048" (default), "RSA-3072", "RSA-4096" or "ECDSA-P256". // - Add `ClusterConfiguration.DNS.Disabled` and `ClusterConfiguration.Proxy.Disabled` that can be used to disable // the CoreDNS and kube-proxy addons during cluster initialization. Skipping the related addons phases, // during cluster creation will set the same fields to `false`. diff --git a/cmd/kubeadm/app/apis/kubeadm/v1beta4/types.go b/cmd/kubeadm/app/apis/kubeadm/v1beta4/types.go index f5a74c993e2..62c76f3cc4d 100644 --- a/cmd/kubeadm/app/apis/kubeadm/v1beta4/types.go +++ b/cmd/kubeadm/app/apis/kubeadm/v1beta4/types.go @@ -149,7 +149,7 @@ type ClusterConfiguration struct { 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 elliptic curve). + // Can be one of "RSA-2048" (default), "RSA-3072", "RSA-4096" or "ECDSA-P256". // +optional EncryptionAlgorithm EncryptionAlgorithmType `json:"encryptionAlgorithm,omitempty"` } @@ -549,10 +549,14 @@ type EnvVar struct { type EncryptionAlgorithmType string const ( - // EncryptionAlgorithmECDSA defines the ECDSA encryption algorithm type. - EncryptionAlgorithmECDSA EncryptionAlgorithmType = "ECDSA" - // EncryptionAlgorithmRSA defines the RSA encryption algorithm type. - EncryptionAlgorithmRSA EncryptionAlgorithmType = "RSA" + // EncryptionAlgorithmECDSAP256 defines the ECDSA encryption algorithm type with curve P256. + EncryptionAlgorithmECDSAP256 EncryptionAlgorithmType = "ECDSA-P256" + // EncryptionAlgorithmRSA2048 defines the RSA encryption algorithm type with key size 2048 bits. + EncryptionAlgorithmRSA2048 EncryptionAlgorithmType = "RSA-2048" + // EncryptionAlgorithmRSA3072 defines the RSA encryption algorithm type with key size 3072 bits. + EncryptionAlgorithmRSA3072 EncryptionAlgorithmType = "RSA-3072" + // EncryptionAlgorithmRSA4096 defines the RSA encryption algorithm type with key size 4096 bits. + EncryptionAlgorithmRSA4096 EncryptionAlgorithmType = "RSA-4096" ) // Timeouts holds various timeouts that apply to kubeadm commands. diff --git a/cmd/kubeadm/app/apis/kubeadm/validation/validation.go b/cmd/kubeadm/app/apis/kubeadm/validation/validation.go index ac5871d3c2a..a6788f8805f 100644 --- a/cmd/kubeadm/app/apis/kubeadm/validation/validation.go +++ b/cmd/kubeadm/app/apis/kubeadm/validation/validation.go @@ -73,7 +73,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, ValidateEncryptionAlgorithm(c.EncryptionAlgorithm, field.NewPath("encryptionAlgorithm"))...) allErrs = append(allErrs, componentconfigs.Validate(c)...) return allErrs } @@ -341,11 +341,16 @@ func ValidateEtcd(e *kubeadm.Etcd, fldPath *field.Path) field.ErrorList { } // ValidateEncryptionAlgorithm validates the public key algorithm -func ValidateEncryptionAlgorithm(algo string, fldPath *field.Path) field.ErrorList { +func ValidateEncryptionAlgorithm(algo kubeadm.EncryptionAlgorithmType, 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) + knownAlgorithms := sets.New( + kubeadm.EncryptionAlgorithmECDSAP256, + kubeadm.EncryptionAlgorithmRSA2048, + kubeadm.EncryptionAlgorithmRSA3072, + kubeadm.EncryptionAlgorithmRSA4096, + ) + if !knownAlgorithms.Has(algo) { + msg := fmt.Sprintf("Invalid encryption algorithm %q. Must be one of %v", algo, sets.List(knownAlgorithms)) allErrs = append(allErrs, field.Invalid(fldPath, algo, msg)) } return allErrs diff --git a/cmd/kubeadm/app/apis/kubeadm/validation/validation_test.go b/cmd/kubeadm/app/apis/kubeadm/validation/validation_test.go index 31fe675a7a8..a6095ebb870 100644 --- a/cmd/kubeadm/app/apis/kubeadm/validation/validation_test.go +++ b/cmd/kubeadm/app/apis/kubeadm/validation/validation_test.go @@ -515,7 +515,7 @@ func TestValidateInitConfiguration(t *testing.T) { DNSDomain: "cluster.local", }, CertificatesDir: "/some/cert/dir", - EncryptionAlgorithm: kubeadmapi.EncryptionAlgorithmRSA, + EncryptionAlgorithm: kubeadmapi.EncryptionAlgorithmRSA2048, }, NodeRegistration: kubeadmapi.NodeRegistrationOptions{Name: nodename, CRISocket: criPath}, }, false}, @@ -531,7 +531,7 @@ func TestValidateInitConfiguration(t *testing.T) { DNSDomain: "cluster.local", }, CertificatesDir: "/some/cert/dir", - EncryptionAlgorithm: kubeadmapi.EncryptionAlgorithmRSA, + EncryptionAlgorithm: kubeadmapi.EncryptionAlgorithmRSA2048, }, NodeRegistration: kubeadmapi.NodeRegistrationOptions{Name: nodename, CRISocket: criPath}, }, false}, @@ -547,7 +547,7 @@ func TestValidateInitConfiguration(t *testing.T) { DNSDomain: "cluster.local", }, CertificatesDir: "/some/other/cert/dir", - EncryptionAlgorithm: kubeadmapi.EncryptionAlgorithmRSA, + EncryptionAlgorithm: kubeadmapi.EncryptionAlgorithmRSA2048, }, }, false}, {"valid InitConfiguration with incorrect IPv4 pod subnet", @@ -563,7 +563,7 @@ func TestValidateInitConfiguration(t *testing.T) { PodSubnet: "10.0.1.15", }, CertificatesDir: "/some/other/cert/dir", - EncryptionAlgorithm: kubeadmapi.EncryptionAlgorithmRSA, + EncryptionAlgorithm: kubeadmapi.EncryptionAlgorithmRSA2048, }, NodeRegistration: kubeadmapi.NodeRegistrationOptions{Name: nodename, CRISocket: criPath}, }, false}, @@ -586,7 +586,7 @@ func TestValidateInitConfiguration(t *testing.T) { PodSubnet: "10.0.1.15/16", }, CertificatesDir: "/some/other/cert/dir", - EncryptionAlgorithm: kubeadmapi.EncryptionAlgorithmRSA, + EncryptionAlgorithm: kubeadmapi.EncryptionAlgorithmRSA2048, }, NodeRegistration: kubeadmapi.NodeRegistrationOptions{Name: nodename, CRISocket: criPath}, }, true}, @@ -608,7 +608,7 @@ func TestValidateInitConfiguration(t *testing.T) { DNSDomain: "cluster.local", }, CertificatesDir: "/some/other/cert/dir", - EncryptionAlgorithm: kubeadmapi.EncryptionAlgorithmECDSA, + EncryptionAlgorithm: kubeadmapi.EncryptionAlgorithmECDSAP256, }, NodeRegistration: kubeadmapi.NodeRegistrationOptions{Name: nodename, CRISocket: criPath}, }, true}, @@ -1197,11 +1197,13 @@ func TestValidateEtcd(t *testing.T) { func TestValidateEncryptionAlgorithm(t *testing.T) { var tests = []struct { name string - algo string + algo kubeadmapi.EncryptionAlgorithmType expectedErrors bool }{ - {name: "valid RSA", algo: string(kubeadmapi.EncryptionAlgorithmRSA), expectedErrors: false}, - {name: "valid ECDSA", algo: string(kubeadmapi.EncryptionAlgorithmECDSA), expectedErrors: false}, + {name: "valid RSA-2048", algo: kubeadmapi.EncryptionAlgorithmRSA2048, expectedErrors: false}, + {name: "valid RSA-3072", algo: kubeadmapi.EncryptionAlgorithmRSA3072, expectedErrors: false}, + {name: "valid RSA-4096", algo: kubeadmapi.EncryptionAlgorithmRSA4096, expectedErrors: false}, + {name: "valid ECDSA-P256", algo: kubeadmapi.EncryptionAlgorithmECDSAP256, expectedErrors: false}, {name: "invalid algorithm", algo: "foo", expectedErrors: true}, {name: "empty algorithm returns an error", algo: "", expectedErrors: true}, } diff --git a/cmd/kubeadm/app/features/features.go b/cmd/kubeadm/app/features/features.go index eded870ab27..25f4f263c7d 100644 --- a/cmd/kubeadm/app/features/features.go +++ b/cmd/kubeadm/app/features/features.go @@ -43,8 +43,9 @@ const ( // InitFeatureGates are the default feature gates for the init command var InitFeatureGates = FeatureList{ 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.", + FeatureSpec: featuregate.FeatureSpec{Default: false, PreRelease: featuregate.Deprecated}, + DeprecationMessage: "The PublicKeysECDSA feature gate is deprecated and will be removed when v1beta3 is removed." + + " v1beta4 supports a new option 'ClusterConfiguration.EncryptionAlgorithm'.", }, RootlessControlPlane: {FeatureSpec: featuregate.FeatureSpec{Default: false, PreRelease: featuregate.Alpha}}, EtcdLearnerMode: {FeatureSpec: featuregate.FeatureSpec{Default: true, PreRelease: featuregate.Beta}}, diff --git a/cmd/kubeadm/app/phases/certs/certs_test.go b/cmd/kubeadm/app/phases/certs/certs_test.go index c74655edf23..04559041073 100644 --- a/cmd/kubeadm/app/phases/certs/certs_test.go +++ b/cmd/kubeadm/app/phases/certs/certs_test.go @@ -262,7 +262,7 @@ func TestCreateServiceAccountKeyAndPublicKeyFiles(t *testing.T) { } } - err := CreateServiceAccountKeyAndPublicKeyFiles(dir, kubeadmapi.EncryptionAlgorithmRSA) + err := CreateServiceAccountKeyAndPublicKeyFiles(dir, kubeadmapi.EncryptionAlgorithmRSA2048) 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/util/pkiutil/pki_helpers.go b/cmd/kubeadm/app/util/pkiutil/pki_helpers.go index b45592a92e2..564ac20ddf7 100644 --- a/cmd/kubeadm/app/util/pkiutil/pki_helpers.go +++ b/cmd/kubeadm/app/util/pkiutil/pki_helpers.go @@ -57,7 +57,6 @@ const ( CertificateBlockType = "CERTIFICATE" // RSAPrivateKeyBlockType is a possible value for pem.Block.Type. RSAPrivateKeyBlockType = "RSA PRIVATE KEY" - rsaKeySize = 2048 ) // CertConfig is a wrapper around certutil.Config extending it with EncryptionAlgorithm. @@ -608,12 +607,32 @@ func EncodePublicKeyPEM(key crypto.PublicKey) ([]byte, error) { // NewPrivateKey returns a new private key. var NewPrivateKey = GeneratePrivateKey +// rsaKeySizeFromAlgorithmType takes a known RSA algorithm defined in the kubeadm API +// an returns its key size. For unknown types it returns 0. For an empty type it returns +// the default size of 2048. +func rsaKeySizeFromAlgorithmType(keyType kubeadmapi.EncryptionAlgorithmType) int { + switch keyType { + case kubeadmapi.EncryptionAlgorithmRSA2048, "": + return 2048 + case kubeadmapi.EncryptionAlgorithmRSA3072: + return 3072 + case kubeadmapi.EncryptionAlgorithmRSA4096: + return 4096 + default: + return 0 + } +} + // GeneratePrivateKey is the default function for generating private keys. func GeneratePrivateKey(keyType kubeadmapi.EncryptionAlgorithmType) (crypto.Signer, error) { - if keyType == kubeadmapi.EncryptionAlgorithmECDSA { + if keyType == kubeadmapi.EncryptionAlgorithmECDSAP256 { return ecdsa.GenerateKey(elliptic.P256(), cryptorand.Reader) } + rsaKeySize := rsaKeySizeFromAlgorithmType(keyType) + if rsaKeySize == 0 { + return nil, errors.Errorf("cannot obtain key size from unknown RSA algorithm: %q", keyType) + } return rsa.GenerateKey(cryptorand.Reader, rsaKeySize) } diff --git a/cmd/kubeadm/app/util/pkiutil/pki_helpers_test.go b/cmd/kubeadm/app/util/pkiutil/pki_helpers_test.go index 4b17b336104..86b87a3155d 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", }, - EncryptionAlgorithm: kubeadmapi.EncryptionAlgorithmRSA, + EncryptionAlgorithm: kubeadmapi.EncryptionAlgorithmRSA2048, }) if err != nil { panic(fmt.Sprintf("Failed generating Root CA: %v", err)) @@ -141,7 +141,7 @@ func TestHasServerAuth(t *testing.T) { CommonName: "test", Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, }, - EncryptionAlgorithm: kubeadmapi.EncryptionAlgorithmECDSA, + EncryptionAlgorithm: kubeadmapi.EncryptionAlgorithmECDSAP256, }, expected: true, }, @@ -940,3 +940,24 @@ func TestVerifyCertChain(t *testing.T) { }) } } + +func TestRSAKeySizeFromAlgorithmType(t *testing.T) { + var tests = []struct { + algorithm kubeadmapi.EncryptionAlgorithmType + expectedSize int + }{ + {algorithm: "unknown", expectedSize: 0}, + {algorithm: "", expectedSize: 2048}, + {algorithm: kubeadmapi.EncryptionAlgorithmRSA2048, expectedSize: 2048}, + {algorithm: kubeadmapi.EncryptionAlgorithmRSA3072, expectedSize: 3072}, + {algorithm: kubeadmapi.EncryptionAlgorithmRSA4096, expectedSize: 4096}, + } + for _, rt := range tests { + t.Run(string(rt.algorithm), func(t *testing.T) { + size := rsaKeySizeFromAlgorithmType(rt.algorithm) + if size != rt.expectedSize { + t.Errorf("expected size: %d, got: %d", rt.expectedSize, size) + } + }) + } +} diff --git a/cmd/kubeadm/app/util/pkiutil/testing/testing.go b/cmd/kubeadm/app/util/pkiutil/testing/testing.go index 588aaacaef9..57319500dd6 100644 --- a/cmd/kubeadm/app/util/pkiutil/testing/testing.go +++ b/cmd/kubeadm/app/util/pkiutil/testing/testing.go @@ -108,7 +108,7 @@ func newPrivateKey(keyType kubeadmapi.EncryptionAlgorithmType) (crypto.Signer, e keyName := "" switch keyType { - case kubeadmapi.EncryptionAlgorithmECDSA: + case kubeadmapi.EncryptionAlgorithmECDSAP256: ecdsa++ keyName = fmt.Sprintf("%d.ecdsa", ecdsa) default: