diff --git a/api/api-rules/violation_exceptions.list b/api/api-rules/violation_exceptions.list index 599e55546cb..67f06a7eead 100644 --- a/api/api-rules/violation_exceptions.list +++ b/api/api-rules/violation_exceptions.list @@ -471,9 +471,15 @@ API rule violation: names_match,k8s.io/apimachinery/pkg/util/intstr,IntOrString, API rule violation: names_match,k8s.io/apimachinery/pkg/util/intstr,IntOrString,Type API rule violation: names_match,k8s.io/kube-controller-manager/config/v1alpha1,AttachDetachControllerConfiguration,DisableAttachDetachReconcilerSync API rule violation: names_match,k8s.io/kube-controller-manager/config/v1alpha1,AttachDetachControllerConfiguration,ReconcilerSyncLoopPeriod +API rule violation: names_match,k8s.io/kube-controller-manager/config/v1alpha1,CSRSigningConfiguration,CertFile +API rule violation: names_match,k8s.io/kube-controller-manager/config/v1alpha1,CSRSigningConfiguration,KeyFile API rule violation: names_match,k8s.io/kube-controller-manager/config/v1alpha1,CSRSigningControllerConfiguration,ClusterSigningCertFile API rule violation: names_match,k8s.io/kube-controller-manager/config/v1alpha1,CSRSigningControllerConfiguration,ClusterSigningDuration API rule violation: names_match,k8s.io/kube-controller-manager/config/v1alpha1,CSRSigningControllerConfiguration,ClusterSigningKeyFile +API rule violation: names_match,k8s.io/kube-controller-manager/config/v1alpha1,CSRSigningControllerConfiguration,KubeAPIServerClientSignerConfiguration +API rule violation: names_match,k8s.io/kube-controller-manager/config/v1alpha1,CSRSigningControllerConfiguration,KubeletClientSignerConfiguration +API rule violation: names_match,k8s.io/kube-controller-manager/config/v1alpha1,CSRSigningControllerConfiguration,KubeletServingSignerConfiguration +API rule violation: names_match,k8s.io/kube-controller-manager/config/v1alpha1,CSRSigningControllerConfiguration,LegacyUnknownSignerConfiguration API rule violation: names_match,k8s.io/kube-controller-manager/config/v1alpha1,CloudProviderConfiguration,CloudConfigFile API rule violation: names_match,k8s.io/kube-controller-manager/config/v1alpha1,CloudProviderConfiguration,Name API rule violation: names_match,k8s.io/kube-controller-manager/config/v1alpha1,DaemonSetControllerConfiguration,ConcurrentDaemonSetSyncs diff --git a/cmd/kube-controller-manager/app/BUILD b/cmd/kube-controller-manager/app/BUILD index 14962b7bda0..7348dbb0b74 100644 --- a/cmd/kube-controller-manager/app/BUILD +++ b/cmd/kube-controller-manager/app/BUILD @@ -158,9 +158,13 @@ go_library( go_test( name = "go_default_test", - srcs = ["core_test.go"], + srcs = [ + "certificates_test.go", + "core_test.go", + ], embed = [":go_default_library"], deps = [ + "//pkg/controller/certificates/signer/config:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//staging/src/k8s.io/client-go/discovery:go_default_library", "//staging/src/k8s.io/client-go/discovery/fake:go_default_library", diff --git a/cmd/kube-controller-manager/app/certificates.go b/cmd/kube-controller-manager/app/certificates.go index be35f6a2444..238b7df2a66 100644 --- a/cmd/kube-controller-manager/app/certificates.go +++ b/cmd/kube-controller-manager/app/certificates.go @@ -22,14 +22,11 @@ package app import ( "fmt" - "os" - "net/http" "k8s.io/apimachinery/pkg/runtime/schema" utilfeature "k8s.io/apiserver/pkg/util/feature" "k8s.io/klog/v2" - kubeoptions "k8s.io/kubernetes/cmd/kube-controller-manager/app/options" "k8s.io/kubernetes/pkg/controller/certificates/approver" "k8s.io/kubernetes/pkg/controller/certificates/cleaner" "k8s.io/kubernetes/pkg/controller/certificates/rootcacertpublisher" @@ -44,87 +41,129 @@ func startCSRSigningController(ctx ControllerContext) (http.Handler, bool, error klog.Warningf("Resource %s is not available now", gvr.String()) return nil, false, nil } - if ctx.ComponentConfig.CSRSigningController.ClusterSigningCertFile == "" || ctx.ComponentConfig.CSRSigningController.ClusterSigningKeyFile == "" { + missingSingleSigningFile := ctx.ComponentConfig.CSRSigningController.ClusterSigningCertFile == "" || ctx.ComponentConfig.CSRSigningController.ClusterSigningKeyFile == "" + if missingSingleSigningFile && !anySpecificFilesSet(ctx.ComponentConfig.CSRSigningController) { klog.V(2).Info("skipping CSR signer controller because no csr cert/key was specified") return nil, false, nil } - - // Deprecation warning for old defaults. - // - // * If the signing cert and key are the default paths but the files - // exist, warn that the paths need to be specified explicitly in a - // later release and the defaults will be removed. We don't expect this - // to be the case. - // - // * If the signing cert and key are default paths but the files don't exist, - // bail out of startController without logging. - var keyFileExists, keyUsesDefault, certFileExists, certUsesDefault bool - - _, err := os.Stat(ctx.ComponentConfig.CSRSigningController.ClusterSigningCertFile) - certFileExists = !os.IsNotExist(err) - - certUsesDefault = (ctx.ComponentConfig.CSRSigningController.ClusterSigningCertFile == kubeoptions.DefaultClusterSigningCertFile) - - _, err = os.Stat(ctx.ComponentConfig.CSRSigningController.ClusterSigningKeyFile) - keyFileExists = !os.IsNotExist(err) - - keyUsesDefault = (ctx.ComponentConfig.CSRSigningController.ClusterSigningKeyFile == kubeoptions.DefaultClusterSigningKeyFile) - - switch { - case (keyFileExists && keyUsesDefault) || (certFileExists && certUsesDefault): - klog.Warningf("You might be using flag defaulting for --cluster-signing-cert-file and" + - " --cluster-signing-key-file. These defaults are deprecated and will be removed" + - " in a subsequent release. Please pass these options explicitly.") - case (!keyFileExists && keyUsesDefault) && (!certFileExists && certUsesDefault): - // This is what we expect right now if people aren't - // setting up the signing controller. This isn't - // actually a problem since the signer is not a - // required controller. - klog.V(2).Info("skipping CSR signer controller because no csr cert/key was specified and the default files are missing") - return nil, false, nil - default: - // Note that '!filesExist && !usesDefaults' is obviously - // operator error. We don't handle this case here and instead - // allow it to be handled by NewCSR... below. + if !missingSingleSigningFile && anySpecificFilesSet(ctx.ComponentConfig.CSRSigningController) { + return nil, false, fmt.Errorf("cannot specify default and per controller certs at the same time") } c := ctx.ClientBuilder.ClientOrDie("certificate-controller") csrInformer := ctx.InformerFactory.Certificates().V1().CertificateSigningRequests() certTTL := ctx.ComponentConfig.CSRSigningController.ClusterSigningDuration.Duration - caFile, caKeyFile := getKubeletServingSignerFiles(ctx.ComponentConfig.CSRSigningController) - // TODO get different signer cert and key files for each signer when we add flags. - - kubeletServingSigner, err := signer.NewKubeletServingCSRSigningController(c, csrInformer, caFile, caKeyFile, certTTL) - if err != nil { - return nil, false, fmt.Errorf("failed to start kubernetes.io/kubelet-serving certificate controller: %v", err) + if kubeletServingSignerCertFile, kubeletServingSignerKeyFile := getKubeletServingSignerFiles(ctx.ComponentConfig.CSRSigningController); len(kubeletServingSignerCertFile) > 0 || len(kubeletServingSignerKeyFile) > 0 { + kubeletServingSigner, err := signer.NewKubeletServingCSRSigningController(c, csrInformer, kubeletServingSignerCertFile, kubeletServingSignerKeyFile, certTTL) + if err != nil { + return nil, false, fmt.Errorf("failed to start kubernetes.io/kubelet-serving certificate controller: %v", err) + } + go kubeletServingSigner.Run(1, ctx.Stop) + } else { + klog.V(2).Infof("skipping CSR signer controller %q because specific files were specified for other signers and not this one.", "kubernetes.io/kubelet-serving") } - go kubeletServingSigner.Run(1, ctx.Stop) - kubeletClientSigner, err := signer.NewKubeletClientCSRSigningController(c, csrInformer, caFile, caKeyFile, certTTL) - if err != nil { - return nil, false, fmt.Errorf("failed to start kubernetes.io/kube-apiserver-client-kubelet certificate controller: %v", err) + if kubeletClientSignerCertFile, kubeletClientSignerKeyFile := getKubeletClientSignerFiles(ctx.ComponentConfig.CSRSigningController); len(kubeletClientSignerCertFile) > 0 || len(kubeletClientSignerKeyFile) > 0 { + kubeletClientSigner, err := signer.NewKubeletClientCSRSigningController(c, csrInformer, kubeletClientSignerCertFile, kubeletClientSignerKeyFile, certTTL) + if err != nil { + return nil, false, fmt.Errorf("failed to start kubernetes.io/kube-apiserver-client-kubelet certificate controller: %v", err) + } + go kubeletClientSigner.Run(1, ctx.Stop) + } else { + klog.V(2).Infof("skipping CSR signer controller %q because specific files were specified for other signers and not this one.", "kubernetes.io/kube-apiserver-client-kubelet") } - go kubeletClientSigner.Run(1, ctx.Stop) - kubeAPIServerClientSigner, err := signer.NewKubeAPIServerClientCSRSigningController(c, csrInformer, caFile, caKeyFile, certTTL) - if err != nil { - return nil, false, fmt.Errorf("failed to start kubernetes.io/kube-apiserver-client certificate controller: %v", err) + if kubeAPIServerSignerCertFile, kubeAPIServerSignerKeyFile := getKubeAPIServerClientSignerFiles(ctx.ComponentConfig.CSRSigningController); len(kubeAPIServerSignerCertFile) > 0 || len(kubeAPIServerSignerKeyFile) > 0 { + kubeAPIServerClientSigner, err := signer.NewKubeAPIServerClientCSRSigningController(c, csrInformer, kubeAPIServerSignerCertFile, kubeAPIServerSignerKeyFile, certTTL) + if err != nil { + return nil, false, fmt.Errorf("failed to start kubernetes.io/kube-apiserver-client certificate controller: %v", err) + } + go kubeAPIServerClientSigner.Run(1, ctx.Stop) + } else { + klog.V(2).Infof("skipping CSR signer controller %q because specific files were specified for other signers and not this one.", "kubernetes.io/kube-apiserver-client") } - go kubeAPIServerClientSigner.Run(1, ctx.Stop) - legacyUnknownSigner, err := signer.NewLegacyUnknownCSRSigningController(c, csrInformer, caFile, caKeyFile, certTTL) - if err != nil { - return nil, false, fmt.Errorf("failed to start kubernetes.io/legacy-unknown certificate controller: %v", err) + if legacyUnknownSignerCertFile, legacyUnknownSignerKeyFile := getLegacyUnknownSignerFiles(ctx.ComponentConfig.CSRSigningController); len(legacyUnknownSignerCertFile) > 0 || len(legacyUnknownSignerKeyFile) > 0 { + legacyUnknownSigner, err := signer.NewLegacyUnknownCSRSigningController(c, csrInformer, legacyUnknownSignerCertFile, legacyUnknownSignerKeyFile, certTTL) + if err != nil { + return nil, false, fmt.Errorf("failed to start kubernetes.io/legacy-unknown certificate controller: %v", err) + } + go legacyUnknownSigner.Run(1, ctx.Stop) + } else { + klog.V(2).Infof("skipping CSR signer controller %q because specific files were specified for other signers and not this one.", "kubernetes.io/legacy-unknown") } - go legacyUnknownSigner.Run(1, ctx.Stop) return nil, true, nil } -// getKubeletServingSignerFiles returns the cert and key for signing. -// TODO we will extended this for each signer so that it prefers the specific flag (to be added) and falls back to the single flag +func areKubeletServingSignerFilesSpecified(config csrsigningconfig.CSRSigningControllerConfiguration) bool { + if len(config.KubeletServingSignerConfiguration.CertFile) > 0 || len(config.KubeletServingSignerConfiguration.KeyFile) > 0 { + // if only one is specified, it will error later during construction + return true + } + return false +} +func areKubeletClientSignerFilesSpecified(config csrsigningconfig.CSRSigningControllerConfiguration) bool { + if len(config.KubeletClientSignerConfiguration.CertFile) > 0 || len(config.KubeletClientSignerConfiguration.KeyFile) > 0 { + // if only one is specified, it will error later during construction + return true + } + return false +} + +func areKubeAPIServerClientSignerFilesSpecified(config csrsigningconfig.CSRSigningControllerConfiguration) bool { + if len(config.KubeAPIServerClientSignerConfiguration.CertFile) > 0 || len(config.KubeAPIServerClientSignerConfiguration.KeyFile) > 0 { + // if only one is specified, it will error later during construction + return true + } + return false +} + +func areLegacyUnknownSignerFilesSpecified(config csrsigningconfig.CSRSigningControllerConfiguration) bool { + if len(config.LegacyUnknownSignerConfiguration.CertFile) > 0 || len(config.LegacyUnknownSignerConfiguration.KeyFile) > 0 { + // if only one is specified, it will error later during construction + return true + } + return false +} + +func anySpecificFilesSet(config csrsigningconfig.CSRSigningControllerConfiguration) bool { + return areKubeletServingSignerFilesSpecified(config) || + areKubeletClientSignerFilesSpecified(config) || + areKubeAPIServerClientSignerFilesSpecified(config) || + areLegacyUnknownSignerFilesSpecified(config) +} + func getKubeletServingSignerFiles(config csrsigningconfig.CSRSigningControllerConfiguration) (string, string) { + // if any cert/key is set for specific CSR signing loops, then the --cluster-signing-{cert,key}-file are not used for any CSR signing loop. + if anySpecificFilesSet(config) { + return config.KubeletServingSignerConfiguration.CertFile, config.KubeletServingSignerConfiguration.KeyFile + } + return config.ClusterSigningCertFile, config.ClusterSigningKeyFile +} + +func getKubeletClientSignerFiles(config csrsigningconfig.CSRSigningControllerConfiguration) (string, string) { + // if any cert/key is set for specific CSR signing loops, then the --cluster-signing-{cert,key}-file are not used for any CSR signing loop. + if anySpecificFilesSet(config) { + return config.KubeletClientSignerConfiguration.CertFile, config.KubeletClientSignerConfiguration.KeyFile + } + return config.ClusterSigningCertFile, config.ClusterSigningKeyFile +} + +func getKubeAPIServerClientSignerFiles(config csrsigningconfig.CSRSigningControllerConfiguration) (string, string) { + // if any cert/key is set for specific CSR signing loops, then the --cluster-signing-{cert,key}-file are not used for any CSR signing loop. + if anySpecificFilesSet(config) { + return config.KubeAPIServerClientSignerConfiguration.CertFile, config.KubeAPIServerClientSignerConfiguration.KeyFile + } + return config.ClusterSigningCertFile, config.ClusterSigningKeyFile +} + +func getLegacyUnknownSignerFiles(config csrsigningconfig.CSRSigningControllerConfiguration) (string, string) { + // if any cert/key is set for specific CSR signing loops, then the --cluster-signing-{cert,key}-file are not used for any CSR signing loop. + if anySpecificFilesSet(config) { + return config.LegacyUnknownSignerConfiguration.CertFile, config.LegacyUnknownSignerConfiguration.KeyFile + } return config.ClusterSigningCertFile, config.ClusterSigningKeyFile } diff --git a/cmd/kube-controller-manager/app/certificates_test.go b/cmd/kube-controller-manager/app/certificates_test.go new file mode 100644 index 00000000000..7e724598f3d --- /dev/null +++ b/cmd/kube-controller-manager/app/certificates_test.go @@ -0,0 +1,309 @@ +/* +Copyright 2020 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package app + +import ( + "testing" + "time" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + csrsigningconfig "k8s.io/kubernetes/pkg/controller/certificates/signer/config" +) + +func TestCertSpecified(t *testing.T) { + allConfig := csrsigningconfig.CSRSigningControllerConfiguration{ + ClusterSigningCertFile: "/cluster-signing-cert", + ClusterSigningKeyFile: "/cluster-signing-key", + ClusterSigningDuration: metav1.Duration{Duration: 10 * time.Hour}, + KubeletServingSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{ + CertFile: "/cluster-signing-kubelet-serving/cert-file", + KeyFile: "/cluster-signing-kubelet-serving/key-file", + }, + KubeletClientSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{ + CertFile: "/cluster-signing-kubelet-client/cert-file", + KeyFile: "/cluster-signing-kubelet-client/key-file", + }, + KubeAPIServerClientSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{ + CertFile: "/cluster-signing-kube-apiserver-client/cert-file", + KeyFile: "/cluster-signing-kube-apiserver-client/key-file", + }, + LegacyUnknownSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{ + CertFile: "/cluster-signing-legacy-unknown/cert-file", + KeyFile: "/cluster-signing-legacy-unknown/key-file", + }, + } + defaultOnly := csrsigningconfig.CSRSigningControllerConfiguration{ + ClusterSigningCertFile: "/cluster-signing-cert", + ClusterSigningKeyFile: "/cluster-signing-key", + ClusterSigningDuration: metav1.Duration{Duration: 10 * time.Hour}, + } + specifiedOnly := csrsigningconfig.CSRSigningControllerConfiguration{ + KubeletServingSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{ + CertFile: "/cluster-signing-kubelet-serving/cert-file", + KeyFile: "/cluster-signing-kubelet-serving/key-file", + }, + KubeletClientSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{ + CertFile: "/cluster-signing-kubelet-client/cert-file", + KeyFile: "/cluster-signing-kubelet-client/key-file", + }, + KubeAPIServerClientSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{ + CertFile: "/cluster-signing-kube-apiserver-client/cert-file", + KeyFile: "/cluster-signing-kube-apiserver-client/key-file", + }, + LegacyUnknownSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{ + CertFile: "/cluster-signing-legacy-unknown/cert-file", + KeyFile: "/cluster-signing-legacy-unknown/key-file", + }, + } + halfASpecified := csrsigningconfig.CSRSigningControllerConfiguration{ + ClusterSigningCertFile: "/cluster-signing-cert", + ClusterSigningKeyFile: "/cluster-signing-key", + ClusterSigningDuration: metav1.Duration{Duration: 10 * time.Hour}, + KubeletServingSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{ + CertFile: "/cluster-signing-kubelet-serving/cert-file", + KeyFile: "/cluster-signing-kubelet-serving/key-file", + }, + KubeletClientSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{ + CertFile: "/cluster-signing-kubelet-client/cert-file", + KeyFile: "/cluster-signing-kubelet-client/key-file", + }, + } + halfBSpecified := csrsigningconfig.CSRSigningControllerConfiguration{ + ClusterSigningCertFile: "/cluster-signing-cert", + ClusterSigningKeyFile: "/cluster-signing-key", + ClusterSigningDuration: metav1.Duration{Duration: 10 * time.Hour}, + KubeAPIServerClientSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{ + CertFile: "/cluster-signing-kube-apiserver-client/cert-file", + KeyFile: "/cluster-signing-kube-apiserver-client/key-file", + }, + LegacyUnknownSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{ + CertFile: "/cluster-signing-legacy-unknown/cert-file", + KeyFile: "/cluster-signing-legacy-unknown/key-file", + }, + } + + tests := []struct { + name string + config csrsigningconfig.CSRSigningControllerConfiguration + specifiedFn func(config csrsigningconfig.CSRSigningControllerConfiguration) bool + expectedSpecified bool + filesFn func(config csrsigningconfig.CSRSigningControllerConfiguration) (string, string) + expectedCert string + expectedKey string + }{ + { + name: "allConfig-KubeletServingSignerFilesSpecified", + config: allConfig, + specifiedFn: areKubeletServingSignerFilesSpecified, + expectedSpecified: true, + filesFn: getKubeletServingSignerFiles, + expectedCert: "/cluster-signing-kubelet-serving/cert-file", + expectedKey: "/cluster-signing-kubelet-serving/key-file", + }, + { + name: "defaultOnly-KubeletServingSignerFilesSpecified", + config: defaultOnly, + specifiedFn: areKubeletServingSignerFilesSpecified, + expectedSpecified: false, + filesFn: getKubeletServingSignerFiles, + expectedCert: "/cluster-signing-cert", + expectedKey: "/cluster-signing-key", + }, + { + name: "specifiedOnly-KubeletServingSignerFilesSpecified", + config: specifiedOnly, + specifiedFn: areKubeletServingSignerFilesSpecified, + expectedSpecified: true, + filesFn: getKubeletServingSignerFiles, + expectedCert: "/cluster-signing-kubelet-serving/cert-file", + expectedKey: "/cluster-signing-kubelet-serving/key-file", + }, + { + name: "halfASpecified-KubeletServingSignerFilesSpecified", + config: halfASpecified, + specifiedFn: areKubeletServingSignerFilesSpecified, + expectedSpecified: true, + filesFn: getKubeletServingSignerFiles, + expectedCert: "/cluster-signing-kubelet-serving/cert-file", + expectedKey: "/cluster-signing-kubelet-serving/key-file", + }, + { + name: "halfBSpecified-KubeletServingSignerFilesSpecified", + config: halfBSpecified, + specifiedFn: areKubeletServingSignerFilesSpecified, + expectedSpecified: false, + filesFn: getKubeletServingSignerFiles, + expectedCert: "", + expectedKey: "", + }, + + { + name: "allConfig-KubeletClientSignerFiles", + config: allConfig, + specifiedFn: areKubeletClientSignerFilesSpecified, + expectedSpecified: true, + filesFn: getKubeletClientSignerFiles, + expectedCert: "/cluster-signing-kubelet-client/cert-file", + expectedKey: "/cluster-signing-kubelet-client/key-file", + }, + { + name: "defaultOnly-KubeletClientSignerFiles", + config: defaultOnly, + specifiedFn: areKubeletClientSignerFilesSpecified, + expectedSpecified: false, + filesFn: getKubeletClientSignerFiles, + expectedCert: "/cluster-signing-cert", + expectedKey: "/cluster-signing-key", + }, + { + name: "specifiedOnly-KubeletClientSignerFiles", + config: specifiedOnly, + specifiedFn: areKubeletClientSignerFilesSpecified, + expectedSpecified: true, + filesFn: getKubeletClientSignerFiles, + expectedCert: "/cluster-signing-kubelet-client/cert-file", + expectedKey: "/cluster-signing-kubelet-client/key-file", + }, + { + name: "halfASpecified-KubeletClientSignerFiles", + config: halfASpecified, + specifiedFn: areKubeletClientSignerFilesSpecified, + expectedSpecified: true, + filesFn: getKubeletClientSignerFiles, + expectedCert: "/cluster-signing-kubelet-client/cert-file", + expectedKey: "/cluster-signing-kubelet-client/key-file", + }, + { + name: "halfBSpecified-KubeletClientSignerFiles", + config: halfBSpecified, + specifiedFn: areKubeletClientSignerFilesSpecified, + expectedSpecified: false, + filesFn: getKubeletClientSignerFiles, + expectedCert: "", + expectedKey: "", + }, + + { + name: "allConfig-KubeletClientSignerFiles", + config: allConfig, + specifiedFn: areKubeAPIServerClientSignerFilesSpecified, + expectedSpecified: true, + filesFn: getKubeAPIServerClientSignerFiles, + expectedCert: "/cluster-signing-kube-apiserver-client/cert-file", + expectedKey: "/cluster-signing-kube-apiserver-client/key-file", + }, + { + name: "defaultOnly-KubeletClientSignerFiles", + config: defaultOnly, + specifiedFn: areKubeAPIServerClientSignerFilesSpecified, + expectedSpecified: false, + filesFn: getKubeAPIServerClientSignerFiles, + expectedCert: "/cluster-signing-cert", + expectedKey: "/cluster-signing-key", + }, + { + name: "specifiedOnly-KubeletClientSignerFiles", + config: specifiedOnly, + specifiedFn: areKubeAPIServerClientSignerFilesSpecified, + expectedSpecified: true, + filesFn: getKubeAPIServerClientSignerFiles, + expectedCert: "/cluster-signing-kube-apiserver-client/cert-file", + expectedKey: "/cluster-signing-kube-apiserver-client/key-file", + }, + { + name: "halfASpecified-KubeletClientSignerFiles", + config: halfASpecified, + specifiedFn: areKubeAPIServerClientSignerFilesSpecified, + expectedSpecified: false, + filesFn: getKubeAPIServerClientSignerFiles, + expectedCert: "", + expectedKey: "", + }, + { + name: "halfBSpecified-KubeletClientSignerFiles", + config: halfBSpecified, + specifiedFn: areKubeAPIServerClientSignerFilesSpecified, + expectedSpecified: true, + filesFn: getKubeAPIServerClientSignerFiles, + expectedCert: "/cluster-signing-kube-apiserver-client/cert-file", + expectedKey: "/cluster-signing-kube-apiserver-client/key-file", + }, + + { + name: "allConfig-LegacyUnknownSignerFiles", + config: allConfig, + specifiedFn: areLegacyUnknownSignerFilesSpecified, + expectedSpecified: true, + filesFn: getLegacyUnknownSignerFiles, + expectedCert: "/cluster-signing-legacy-unknown/cert-file", + expectedKey: "/cluster-signing-legacy-unknown/key-file", + }, + { + name: "defaultOnly-LegacyUnknownSignerFiles", + config: defaultOnly, + specifiedFn: areLegacyUnknownSignerFilesSpecified, + expectedSpecified: false, + filesFn: getLegacyUnknownSignerFiles, + expectedCert: "/cluster-signing-cert", + expectedKey: "/cluster-signing-key", + }, + { + name: "specifiedOnly-LegacyUnknownSignerFiles", + config: specifiedOnly, + specifiedFn: areLegacyUnknownSignerFilesSpecified, + expectedSpecified: true, + filesFn: getLegacyUnknownSignerFiles, + expectedCert: "/cluster-signing-legacy-unknown/cert-file", + expectedKey: "/cluster-signing-legacy-unknown/key-file", + }, + { + name: "halfASpecified-LegacyUnknownSignerFiles", + config: halfASpecified, + specifiedFn: areLegacyUnknownSignerFilesSpecified, + expectedSpecified: false, + filesFn: getLegacyUnknownSignerFiles, + expectedCert: "", + expectedKey: "", + }, + { + name: "halfBSpecified-LegacyUnknownSignerFiles", + config: halfBSpecified, + specifiedFn: areLegacyUnknownSignerFilesSpecified, + expectedSpecified: true, + filesFn: getLegacyUnknownSignerFiles, + expectedCert: "/cluster-signing-legacy-unknown/cert-file", + expectedKey: "/cluster-signing-legacy-unknown/key-file", + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + actualSpecified := test.specifiedFn(test.config) + if actualSpecified != test.expectedSpecified { + t.Error(actualSpecified) + } + + actualCert, actualKey := test.filesFn(test.config) + if actualCert != test.expectedCert { + t.Error(actualCert) + } + if actualKey != test.expectedKey { + t.Error(actualKey) + } + }) + } +} diff --git a/cmd/kube-controller-manager/app/options/csrsigningcontroller.go b/cmd/kube-controller-manager/app/options/csrsigningcontroller.go index f9477449cc7..ebb49886803 100644 --- a/cmd/kube-controller-manager/app/options/csrsigningcontroller.go +++ b/cmd/kube-controller-manager/app/options/csrsigningcontroller.go @@ -17,21 +17,13 @@ limitations under the License. package options import ( + "fmt" + "github.com/spf13/pflag" csrsigningconfig "k8s.io/kubernetes/pkg/controller/certificates/signer/config" ) -const ( - // These defaults are deprecated and exported so that we can warn if - // they are being used. - - // DefaultClusterSigningCertFile is deprecated. Do not use. - DefaultClusterSigningCertFile = "/etc/kubernetes/ca/ca.pem" - // DefaultClusterSigningKeyFile is deprecated. Do not use. - DefaultClusterSigningKeyFile = "/etc/kubernetes/ca/ca.key" -) - // CSRSigningControllerOptions holds the CSRSigningController options. type CSRSigningControllerOptions struct { *csrsigningconfig.CSRSigningControllerConfiguration @@ -43,8 +35,16 @@ func (o *CSRSigningControllerOptions) AddFlags(fs *pflag.FlagSet) { return } - fs.StringVar(&o.ClusterSigningCertFile, "cluster-signing-cert-file", o.ClusterSigningCertFile, "Filename containing a PEM-encoded X509 CA certificate used to issue cluster-scoped certificates") - fs.StringVar(&o.ClusterSigningKeyFile, "cluster-signing-key-file", o.ClusterSigningKeyFile, "Filename containing a PEM-encoded RSA or ECDSA private key used to sign cluster-scoped certificates") + fs.StringVar(&o.ClusterSigningCertFile, "cluster-signing-cert-file", o.ClusterSigningCertFile, "Filename containing a PEM-encoded X509 CA certificate used to issue cluster-scoped certificates. If specified, no more specific --cluster-signing-* flag may be specified.") + fs.StringVar(&o.ClusterSigningKeyFile, "cluster-signing-key-file", o.ClusterSigningKeyFile, "Filename containing a PEM-encoded RSA or ECDSA private key used to sign cluster-scoped certificates. If specified, no more specific --cluster-signing-* flag may be specified.") + fs.StringVar(&o.KubeletServingSignerConfiguration.CertFile, "cluster-signing-kubelet-serving-cert-file", o.KubeletServingSignerConfiguration.CertFile, "Filename containing a PEM-encoded X509 CA certificate used to issue certificates for the kubernetes.io/kubelet-serving signer. If specified, --cluster-signing-{cert,key}-file must not be set.") + fs.StringVar(&o.KubeletServingSignerConfiguration.KeyFile, "cluster-signing-kubelet-serving-key-file", o.KubeletServingSignerConfiguration.KeyFile, "Filename containing a PEM-encoded RSA or ECDSA private key used to sign certificates for the kubernetes.io/kubelet-serving signer. If specified, --cluster-signing-{cert,key}-file must not be set.") + fs.StringVar(&o.KubeletClientSignerConfiguration.CertFile, "cluster-signing-kubelet-client-cert-file", o.KubeletClientSignerConfiguration.CertFile, "Filename containing a PEM-encoded X509 CA certificate used to issue certificates for the kubernetes.io/kube-apiserver-client-kubelet signer. If specified, --cluster-signing-{cert,key}-file must not be set.") + fs.StringVar(&o.KubeletClientSignerConfiguration.KeyFile, "cluster-signing-kubelet-client-key-file", o.KubeletClientSignerConfiguration.KeyFile, "Filename containing a PEM-encoded RSA or ECDSA private key used to sign certificates for the kubernetes.io/kube-apiserver-client-kubelet signer. If specified, --cluster-signing-{cert,key}-file must not be set.") + fs.StringVar(&o.KubeAPIServerClientSignerConfiguration.CertFile, "cluster-signing-kube-apiserver-client-cert-file", o.KubeAPIServerClientSignerConfiguration.CertFile, "Filename containing a PEM-encoded X509 CA certificate used to issue certificates for the kubernetes.io/kube-apiserver-client signer. If specified, --cluster-signing-{cert,key}-file must not be set.") + fs.StringVar(&o.KubeAPIServerClientSignerConfiguration.KeyFile, "cluster-signing-kube-apiserver-client-key-file", o.KubeAPIServerClientSignerConfiguration.KeyFile, "Filename containing a PEM-encoded RSA or ECDSA private key used to sign certificates for the kubernetes.io/kube-apiserver-client signer. If specified, --cluster-signing-{cert,key}-file must not be set.") + fs.StringVar(&o.LegacyUnknownSignerConfiguration.CertFile, "cluster-signing-legacy-unknown-cert-file", o.LegacyUnknownSignerConfiguration.CertFile, "Filename containing a PEM-encoded X509 CA certificate used to issue certificates for the kubernetes.io/legacy-unknown signer. If specified, --cluster-signing-{cert,key}-file must not be set.") + fs.StringVar(&o.LegacyUnknownSignerConfiguration.KeyFile, "cluster-signing-legacy-unknown-key-file", o.LegacyUnknownSignerConfiguration.KeyFile, "Filename containing a PEM-encoded RSA or ECDSA private key used to sign certificates for the kubernetes.io/legacy-unknown signer. If specified, --cluster-signing-{cert,key}-file must not be set.") fs.DurationVar(&o.ClusterSigningDuration.Duration, "cluster-signing-duration", o.ClusterSigningDuration.Duration, "The length of duration signed certificates will be given.") fs.DurationVar(&o.ClusterSigningDuration.Duration, "experimental-cluster-signing-duration", o.ClusterSigningDuration.Duration, "The length of duration signed certificates will be given.") fs.MarkDeprecated("experimental-cluster-signing-duration", "use --cluster-signing-duration") @@ -58,6 +58,10 @@ func (o *CSRSigningControllerOptions) ApplyTo(cfg *csrsigningconfig.CSRSigningCo cfg.ClusterSigningCertFile = o.ClusterSigningCertFile cfg.ClusterSigningKeyFile = o.ClusterSigningKeyFile + cfg.KubeletServingSignerConfiguration = o.KubeletServingSignerConfiguration + cfg.KubeletClientSignerConfiguration = o.KubeletClientSignerConfiguration + cfg.KubeAPIServerClientSignerConfiguration = o.KubeAPIServerClientSignerConfiguration + cfg.LegacyUnknownSignerConfiguration = o.LegacyUnknownSignerConfiguration cfg.ClusterSigningDuration = o.ClusterSigningDuration return nil @@ -70,5 +74,43 @@ func (o *CSRSigningControllerOptions) Validate() []error { } errs := []error{} + if err := csrSigningFilesValid(o.KubeletServingSignerConfiguration); err != nil { + errs = append(errs, fmt.Errorf("%q: %v", "cluster-signing-kubelet-serving", err)) + } + if err := csrSigningFilesValid(o.KubeletClientSignerConfiguration); err != nil { + errs = append(errs, fmt.Errorf("%q: %v", "cluster-signing-kube-apiserver-client", err)) + } + if err := csrSigningFilesValid(o.KubeAPIServerClientSignerConfiguration); err != nil { + errs = append(errs, fmt.Errorf("%q: %v", "cluster-signing-kube-apiserver", err)) + } + if err := csrSigningFilesValid(o.LegacyUnknownSignerConfiguration); err != nil { + errs = append(errs, fmt.Errorf("%q: %v", "cluster-signing-legacy-unknown", err)) + } + + singleSigningFile := len(o.ClusterSigningCertFile) > 0 || len(o.ClusterSigningKeyFile) > 0 + anySpecificFilesSet := len(o.KubeletServingSignerConfiguration.CertFile) > 0 || len(o.KubeletServingSignerConfiguration.KeyFile) > 0 || + len(o.KubeletClientSignerConfiguration.CertFile) > 0 || len(o.KubeletClientSignerConfiguration.KeyFile) > 0 || + len(o.KubeAPIServerClientSignerConfiguration.CertFile) > 0 || len(o.KubeAPIServerClientSignerConfiguration.KeyFile) > 0 || + len(o.LegacyUnknownSignerConfiguration.CertFile) > 0 || len(o.LegacyUnknownSignerConfiguration.KeyFile) > 0 + if singleSigningFile && anySpecificFilesSet { + errs = append(errs, fmt.Errorf("cannot specify --cluster-signing-{cert,key}-file and other --cluster-signing-*-file flags at the same time")) + } + return errs } + +// both must be specified or both must be empty +func csrSigningFilesValid(config csrsigningconfig.CSRSigningConfiguration) error { + switch { + case (len(config.CertFile) == 0) && (len(config.KeyFile) == 0): + return nil + case (len(config.CertFile) != 0) && (len(config.KeyFile) != 0): + return nil + case (len(config.CertFile) == 0) && (len(config.KeyFile) != 0): + return fmt.Errorf("cannot specify key without cert") + case (len(config.CertFile) != 0) && (len(config.KeyFile) == 0): + return fmt.Errorf("cannot specify cert without key") + } + + return fmt.Errorf("math broke") +} diff --git a/cmd/kube-controller-manager/app/options/options_test.go b/cmd/kube-controller-manager/app/options/options_test.go index cb9ede1d373..ce821254ddc 100644 --- a/cmd/kube-controller-manager/app/options/options_test.go +++ b/cmd/kube-controller-manager/app/options/options_test.go @@ -68,6 +68,14 @@ var args = []string{ "--cluster-name=k8s", "--cluster-signing-cert-file=/cluster-signing-cert", "--cluster-signing-key-file=/cluster-signing-key", + "--cluster-signing-kubelet-serving-cert-file=/cluster-signing-kubelet-serving/cert-file", + "--cluster-signing-kubelet-serving-key-file=/cluster-signing-kubelet-serving/key-file", + "--cluster-signing-kubelet-client-cert-file=/cluster-signing-kubelet-client/cert-file", + "--cluster-signing-kubelet-client-key-file=/cluster-signing-kubelet-client/key-file", + "--cluster-signing-kube-apiserver-client-cert-file=/cluster-signing-kube-apiserver-client/cert-file", + "--cluster-signing-kube-apiserver-client-key-file=/cluster-signing-kube-apiserver-client/key-file", + "--cluster-signing-legacy-unknown-cert-file=/cluster-signing-legacy-unknown/cert-file", + "--cluster-signing-legacy-unknown-key-file=/cluster-signing-legacy-unknown/key-file", "--concurrent-deployment-syncs=10", "--concurrent-statefulset-syncs=15", "--concurrent-endpoint-syncs=10", @@ -216,6 +224,22 @@ func TestAddFlags(t *testing.T) { ClusterSigningCertFile: "/cluster-signing-cert", ClusterSigningKeyFile: "/cluster-signing-key", ClusterSigningDuration: metav1.Duration{Duration: 10 * time.Hour}, + KubeletServingSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{ + CertFile: "/cluster-signing-kubelet-serving/cert-file", + KeyFile: "/cluster-signing-kubelet-serving/key-file", + }, + KubeletClientSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{ + CertFile: "/cluster-signing-kubelet-client/cert-file", + KeyFile: "/cluster-signing-kubelet-client/key-file", + }, + KubeAPIServerClientSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{ + CertFile: "/cluster-signing-kube-apiserver-client/cert-file", + KeyFile: "/cluster-signing-kube-apiserver-client/key-file", + }, + LegacyUnknownSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{ + CertFile: "/cluster-signing-legacy-unknown/cert-file", + KeyFile: "/cluster-signing-legacy-unknown/key-file", + }, }, }, DaemonSetController: &DaemonSetControllerOptions{ @@ -461,6 +485,22 @@ func TestApplyTo(t *testing.T) { ClusterSigningCertFile: "/cluster-signing-cert", ClusterSigningKeyFile: "/cluster-signing-key", ClusterSigningDuration: metav1.Duration{Duration: 10 * time.Hour}, + KubeletServingSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{ + CertFile: "/cluster-signing-kubelet-serving/cert-file", + KeyFile: "/cluster-signing-kubelet-serving/key-file", + }, + KubeletClientSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{ + CertFile: "/cluster-signing-kubelet-client/cert-file", + KeyFile: "/cluster-signing-kubelet-client/key-file", + }, + KubeAPIServerClientSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{ + CertFile: "/cluster-signing-kube-apiserver-client/cert-file", + KeyFile: "/cluster-signing-kube-apiserver-client/key-file", + }, + LegacyUnknownSignerConfiguration: csrsigningconfig.CSRSigningConfiguration{ + CertFile: "/cluster-signing-legacy-unknown/cert-file", + KeyFile: "/cluster-signing-legacy-unknown/key-file", + }, }, DaemonSetController: daemonconfig.DaemonSetControllerConfiguration{ ConcurrentDaemonSetSyncs: 2, diff --git a/pkg/controller/certificates/signer/config/types.go b/pkg/controller/certificates/signer/config/types.go index 0452b0f2d5f..edf60b17dca 100644 --- a/pkg/controller/certificates/signer/config/types.go +++ b/pkg/controller/certificates/signer/config/types.go @@ -28,7 +28,27 @@ type CSRSigningControllerConfiguration struct { // clusterSigningCertFile is the filename containing a PEM-encoded // RSA or ECDSA private key used to issue cluster-scoped certificates ClusterSigningKeyFile string + + // kubeletServingSignerConfiguration holds the certificate and key used to issue certificates for the kubernetes.io/kubelet-serving signer + KubeletServingSignerConfiguration CSRSigningConfiguration + // kubeletClientSignerConfiguration holds the certificate and key used to issue certificates for the kubernetes.io/kube-apiserver-client-kubelet + KubeletClientSignerConfiguration CSRSigningConfiguration + // kubeAPIServerClientSignerConfiguration holds the certificate and key used to issue certificates for the kubernetes.io/kube-apiserver-client + KubeAPIServerClientSignerConfiguration CSRSigningConfiguration + // legacyUnknownSignerConfiguration holds the certificate and key used to issue certificates for the kubernetes.io/legacy-unknown + LegacyUnknownSignerConfiguration CSRSigningConfiguration + // clusterSigningDuration is the length of duration signed certificates // will be given. ClusterSigningDuration metav1.Duration } + +// CSRSigningConfiguration holds information about a particular CSR signer +type CSRSigningConfiguration struct { + // certFile is the filename containing a PEM-encoded + // X509 CA certificate used to issue certificates + CertFile string + // keyFile is the filename containing a PEM-encoded + // RSA or ECDSA private key used to issue certificates + KeyFile string +} diff --git a/pkg/controller/certificates/signer/config/v1alpha1/defaults.go b/pkg/controller/certificates/signer/config/v1alpha1/defaults.go index 4f91b884322..65244798fc1 100644 --- a/pkg/controller/certificates/signer/config/v1alpha1/defaults.go +++ b/pkg/controller/certificates/signer/config/v1alpha1/defaults.go @@ -34,12 +34,6 @@ import ( // run it in your wrapper struct of this type in its `SetDefaults_` method. func RecommendedDefaultCSRSigningControllerConfiguration(obj *kubectrlmgrconfigv1alpha1.CSRSigningControllerConfiguration) { zero := metav1.Duration{} - if obj.ClusterSigningCertFile == "" { - obj.ClusterSigningCertFile = "/etc/kubernetes/ca/ca.pem" - } - if obj.ClusterSigningKeyFile == "" { - obj.ClusterSigningKeyFile = "/etc/kubernetes/ca/ca.key" - } if obj.ClusterSigningDuration == zero { obj.ClusterSigningDuration = metav1.Duration{Duration: 365 * 24 * time.Hour} } diff --git a/staging/src/k8s.io/kube-controller-manager/config/v1alpha1/types.go b/staging/src/k8s.io/kube-controller-manager/config/v1alpha1/types.go index f99980bec8e..a04afacbd25 100644 --- a/staging/src/k8s.io/kube-controller-manager/config/v1alpha1/types.go +++ b/staging/src/k8s.io/kube-controller-manager/config/v1alpha1/types.go @@ -243,11 +243,31 @@ type CSRSigningControllerConfiguration struct { // clusterSigningCertFile is the filename containing a PEM-encoded // RSA or ECDSA private key used to issue cluster-scoped certificates ClusterSigningKeyFile string + + // kubeletServingSignerConfiguration holds the certificate and key used to issue certificates for the kubernetes.io/kubelet-serving signer + KubeletServingSignerConfiguration CSRSigningConfiguration + // kubeletClientSignerConfiguration holds the certificate and key used to issue certificates for the kubernetes.io/kube-apiserver-client-kubelet + KubeletClientSignerConfiguration CSRSigningConfiguration + // kubeAPIServerClientSignerConfiguration holds the certificate and key used to issue certificates for the kubernetes.io/kube-apiserver-client + KubeAPIServerClientSignerConfiguration CSRSigningConfiguration + // legacyUnknownSignerConfiguration holds the certificate and key used to issue certificates for the kubernetes.io/legacy-unknown + LegacyUnknownSignerConfiguration CSRSigningConfiguration + // clusterSigningDuration is the length of duration signed certificates // will be given. ClusterSigningDuration metav1.Duration } +// CSRSigningConfiguration holds information about a particular CSR signer +type CSRSigningConfiguration struct { + // certFile is the filename containing a PEM-encoded + // X509 CA certificate used to issue certificates + CertFile string + // keyFile is the filename containing a PEM-encoded + // RSA or ECDSA private key used to issue certificates + KeyFile string +} + // DaemonSetControllerConfiguration contains elements describing DaemonSetController. type DaemonSetControllerConfiguration struct { // concurrentDaemonSetSyncs is the number of daemonset objects that are