From 44638a10e8a374c5eb36c65b44c96ef635bad438 Mon Sep 17 00:00:00 2001 From: "Lubomir I. Ivanov" Date: Tue, 14 Apr 2020 21:57:37 +0300 Subject: [PATCH] kubeadm: remove usage of the "certificates" API for cert renewal The flag "--use-api" for "alpha certs renew" was deprecated in 1.18. Remove the flag and related logic that executes certificate renewal using "api/certificates/v1beta1". kubeadm continues to be able to create CSR files and renew using the local CA on disk. --- cmd/kubeadm/app/cmd/alpha/certs.go | 39 ++--- cmd/kubeadm/app/cmd/alpha/certs_test.go | 1 - cmd/kubeadm/app/phases/certs/renewal/BUILD | 13 -- .../app/phases/certs/renewal/apirenewer.go | 134 ------------------ .../phases/certs/renewal/apirenewer_test.go | 125 ---------------- .../app/phases/certs/renewal/manager.go | 38 ----- 6 files changed, 9 insertions(+), 341 deletions(-) delete mode 100644 cmd/kubeadm/app/phases/certs/renewal/apirenewer.go delete mode 100644 cmd/kubeadm/app/phases/certs/renewal/apirenewer_test.go diff --git a/cmd/kubeadm/app/cmd/alpha/certs.go b/cmd/kubeadm/app/cmd/alpha/certs.go index c78b613ebfb..399529cfe99 100644 --- a/cmd/kubeadm/app/cmd/alpha/certs.go +++ b/cmd/kubeadm/app/cmd/alpha/certs.go @@ -121,7 +121,6 @@ type renewFlags struct { cfgPath string kubeconfigPath string cfg kubeadmapiv1beta2.ClusterConfiguration - useAPI bool csrOnly bool csrPath string } @@ -208,12 +207,6 @@ func addRenewFlags(cmd *cobra.Command, flags *renewFlags) { options.AddKubeConfigFlag(cmd.Flags(), &flags.kubeconfigPath) options.AddCSRFlag(cmd.Flags(), &flags.csrOnly) options.AddCSRDirFlag(cmd.Flags(), &flags.csrPath) - // TODO: remove the flag and related logic once legacy signers are removed, - // potentially with the release of certificates.k8s.io/v1: - // https://github.com/kubernetes/kubeadm/issues/2047 - cmd.Flags().BoolVar(&flags.useAPI, "use-api", flags.useAPI, "Use the Kubernetes certificate API to renew certificates") - cmd.Flags().MarkDeprecated("use-api", "certificate renewal from kubeadm using the Kubernetes API "+ - "is deprecated and will be removed when 'certificates.k8s.io/v1' releases.") } func renewCert(flags *renewFlags, kdir string, internalcfg *kubeadmapi.InitConfiguration, handler *renewal.CertificateRenewHandler) error { @@ -239,29 +232,15 @@ func renewCert(flags *renewFlags, kdir string, internalcfg *kubeadmapi.InitConfi // otherwise, the renewal operation has to actually renew a certificate - // renew the certificate using the requested renew method - if flags.useAPI { - // renew using K8s certificate API - kubeConfigPath := cmdutil.GetKubeConfigPath(flags.kubeconfigPath) - client, err := kubeconfigutil.ClientSetFromFile(kubeConfigPath) - if err != nil { - return err - } - - if err := rm.RenewUsingCSRAPI(handler.Name, client); err != nil { - return err - } - } else { - // renew using local certificate authorities. - // this operation can't complete in case the certificate key is not provided (external CA) - renewed, err := rm.RenewUsingLocalCA(handler.Name) - if err != nil { - return err - } - if !renewed { - fmt.Printf("Detected external %s, %s can't be renewed\n", handler.CABaseName, handler.LongName) - return nil - } + // renew using local certificate authorities. + // this operation can't complete in case the certificate key is not provided (external CA) + renewed, err := rm.RenewUsingLocalCA(handler.Name) + if err != nil { + return err + } + if !renewed { + fmt.Printf("Detected external %s, %s can't be renewed\n", handler.CABaseName, handler.LongName) + return nil } fmt.Printf("%s renewed\n", handler.LongName) return nil diff --git a/cmd/kubeadm/app/cmd/alpha/certs_test.go b/cmd/kubeadm/app/cmd/alpha/certs_test.go index 4bf964b48d9..67dc460bbec 100644 --- a/cmd/kubeadm/app/cmd/alpha/certs_test.go +++ b/cmd/kubeadm/app/cmd/alpha/certs_test.go @@ -39,7 +39,6 @@ func TestCommandsGenerated(t *testing.T) { expectedFlags := []string{ "cert-dir", "config", - "use-api", } expectedCommands := []string{ diff --git a/cmd/kubeadm/app/phases/certs/renewal/BUILD b/cmd/kubeadm/app/phases/certs/renewal/BUILD index a9f9e37d908..f5c2e79d646 100644 --- a/cmd/kubeadm/app/phases/certs/renewal/BUILD +++ b/cmd/kubeadm/app/phases/certs/renewal/BUILD @@ -3,7 +3,6 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") go_library( name = "go_default_library", srcs = [ - "apirenewer.go", "expiration.go", "filerenewer.go", "manager.go", @@ -16,14 +15,9 @@ go_library( "//cmd/kubeadm/app/constants:go_default_library", "//cmd/kubeadm/app/phases/certs:go_default_library", "//cmd/kubeadm/app/util/pkiutil:go_default_library", - "//staging/src/k8s.io/api/certificates/v1beta1:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", - "//staging/src/k8s.io/client-go/kubernetes:go_default_library", - "//staging/src/k8s.io/client-go/kubernetes/typed/certificates/v1beta1:go_default_library", "//staging/src/k8s.io/client-go/tools/clientcmd:go_default_library", "//staging/src/k8s.io/client-go/tools/clientcmd/api:go_default_library", "//staging/src/k8s.io/client-go/util/cert:go_default_library", - "//staging/src/k8s.io/client-go/util/certificate/csr:go_default_library", "//staging/src/k8s.io/client-go/util/keyutil:go_default_library", "//vendor/github.com/pkg/errors:go_default_library", ], @@ -32,7 +26,6 @@ go_library( go_test( name = "go_default_test", srcs = [ - "apirenewer_test.go", "expiration_test.go", "filerenewer_test.go", "manager_test.go", @@ -46,12 +39,6 @@ go_test( "//cmd/kubeadm/app/util/kubeconfig:go_default_library", "//cmd/kubeadm/app/util/pkiutil:go_default_library", "//cmd/kubeadm/test:go_default_library", - "//staging/src/k8s.io/api/certificates/v1beta1:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/watch:go_default_library", - "//staging/src/k8s.io/client-go/kubernetes/typed/certificates/v1beta1/fake:go_default_library", - "//staging/src/k8s.io/client-go/testing:go_default_library", "//staging/src/k8s.io/client-go/tools/clientcmd:go_default_library", "//staging/src/k8s.io/client-go/util/cert:go_default_library", "//staging/src/k8s.io/client-go/util/keyutil:go_default_library", diff --git a/cmd/kubeadm/app/phases/certs/renewal/apirenewer.go b/cmd/kubeadm/app/phases/certs/renewal/apirenewer.go deleted file mode 100644 index f1188c8634b..00000000000 --- a/cmd/kubeadm/app/phases/certs/renewal/apirenewer.go +++ /dev/null @@ -1,134 +0,0 @@ -/* -Copyright 2018 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 renewal - -import ( - "context" - "crypto" - "crypto/x509" - "crypto/x509/pkix" - "fmt" - "time" - - "github.com/pkg/errors" - - certsapi "k8s.io/api/certificates/v1beta1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - clientset "k8s.io/client-go/kubernetes" - certstype "k8s.io/client-go/kubernetes/typed/certificates/v1beta1" - certutil "k8s.io/client-go/util/cert" - csrutil "k8s.io/client-go/util/certificate/csr" - pkiutil "k8s.io/kubernetes/cmd/kubeadm/app/util/pkiutil" -) - -const certAPIPrefixName = "kubeadm-cert" - -var watchTimeout = 5 * time.Minute - -// APIRenewer define a certificate renewer implementation that uses the K8s certificate API -type APIRenewer struct { - client certstype.CertificatesV1beta1Interface -} - -// NewAPIRenewer a new certificate renewer implementation that uses the K8s certificate API -func NewAPIRenewer(client clientset.Interface) *APIRenewer { - return &APIRenewer{ - client: client.CertificatesV1beta1(), - } -} - -// Renew a certificate using the K8s certificate API -func (r *APIRenewer) Renew(cfg *pkiutil.CertConfig) (*x509.Certificate, crypto.Signer, error) { - reqTmp := &x509.CertificateRequest{ - Subject: pkix.Name{ - CommonName: cfg.CommonName, - Organization: cfg.Organization, - }, - DNSNames: cfg.AltNames.DNSNames, - IPAddresses: cfg.AltNames.IPs, - } - - key, err := pkiutil.NewPrivateKey(cfg.PublicKeyAlgorithm) - if err != nil { - return nil, nil, errors.Wrap(err, "couldn't create new private key") - } - - csr, err := certutil.MakeCSRFromTemplate(key, reqTmp) - if err != nil { - return nil, nil, errors.Wrap(err, "couldn't create certificate signing request") - } - - usages := make([]certsapi.KeyUsage, len(cfg.Usages)) - for i, usage := range cfg.Usages { - certsAPIUsage, ok := usageMap[usage] - if !ok { - return nil, nil, errors.Errorf("unknown key usage: %v", usage) - } - usages[i] = certsAPIUsage - } - - k8sCSR := &certsapi.CertificateSigningRequest{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: fmt.Sprintf("%s-%s-", certAPIPrefixName, cfg.CommonName), - }, - Spec: certsapi.CertificateSigningRequestSpec{ - Request: csr, - Usages: usages, - }, - } - - req, err := r.client.CertificateSigningRequests().Create(context.TODO(), k8sCSR, metav1.CreateOptions{}) - if err != nil { - return nil, nil, errors.Wrap(err, "couldn't create certificate signing request") - } - - fmt.Printf("[certs] Certificate request %q created\n", req.Name) - - ctx, cancel := context.WithTimeout(context.Background(), watchTimeout) - defer cancel() - - certData, err := csrutil.WaitForCertificate(ctx, r.client.CertificateSigningRequests(), req) - if err != nil { - return nil, nil, errors.Wrap(err, "certificate failed to appear") - } - - cert, err := certutil.ParseCertsPEM(certData) - if err != nil { - return nil, nil, errors.Wrap(err, "couldn't parse issued certificate") - } - - if len(cert) != 1 { - return nil, nil, errors.Errorf("certificate request %q has %d certificates, wanted exactly 1", req.Name, len(cert)) - } - - return cert[0], key, nil -} - -var usageMap = map[x509.ExtKeyUsage]certsapi.KeyUsage{ - x509.ExtKeyUsageAny: certsapi.UsageAny, - x509.ExtKeyUsageServerAuth: certsapi.UsageServerAuth, - x509.ExtKeyUsageClientAuth: certsapi.UsageClientAuth, - x509.ExtKeyUsageCodeSigning: certsapi.UsageCodeSigning, - x509.ExtKeyUsageEmailProtection: certsapi.UsageEmailProtection, - x509.ExtKeyUsageIPSECEndSystem: certsapi.UsageIPsecEndSystem, - x509.ExtKeyUsageIPSECTunnel: certsapi.UsageIPsecTunnel, - x509.ExtKeyUsageIPSECUser: certsapi.UsageIPsecUser, - x509.ExtKeyUsageTimeStamping: certsapi.UsageTimestamping, - x509.ExtKeyUsageOCSPSigning: certsapi.UsageOCSPSigning, - x509.ExtKeyUsageMicrosoftServerGatedCrypto: certsapi.UsageMicrosoftSGC, - x509.ExtKeyUsageNetscapeServerGatedCrypto: certsapi.UsageNetscapeSGC, -} diff --git a/cmd/kubeadm/app/phases/certs/renewal/apirenewer_test.go b/cmd/kubeadm/app/phases/certs/renewal/apirenewer_test.go deleted file mode 100644 index 2720057a441..00000000000 --- a/cmd/kubeadm/app/phases/certs/renewal/apirenewer_test.go +++ /dev/null @@ -1,125 +0,0 @@ -/* -Copyright 2019 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 renewal - -import ( - "crypto" - "crypto/x509" - "testing" - "time" - - certsapi "k8s.io/api/certificates/v1beta1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/watch" - fakecerts "k8s.io/client-go/kubernetes/typed/certificates/v1beta1/fake" - k8stesting "k8s.io/client-go/testing" - certutil "k8s.io/client-go/util/cert" - pkiutil "k8s.io/kubernetes/cmd/kubeadm/app/util/pkiutil" -) - -func TestAPIRenewer(t *testing.T) { - caCertCfg := &pkiutil.CertConfig{ - Config: certutil.Config{CommonName: "kubernetes"}, - } - caCert, caKey, err := pkiutil.NewCertificateAuthority(caCertCfg) - if err != nil { - t.Fatalf("couldn't create CA: %v", err) - } - - client := &fakecerts.FakeCertificatesV1beta1{ - Fake: &k8stesting.Fake{}, - } - certReq := getCertReq(t, caCert, caKey) - certReqNoCert := certReq.DeepCopy() - certReqNoCert.Status.Certificate = nil - client.AddReactor("get", "certificatesigningrequests", defaultReactionFunc(certReq)) - watcher := watch.NewFakeWithChanSize(3, false) - watcher.Add(certReqNoCert) - watcher.Modify(certReqNoCert) - watcher.Modify(certReq) - client.AddWatchReactor("certificatesigningrequests", k8stesting.DefaultWatchReactor(watcher, nil)) - - // override the timeout so tests are faster - watchTimeout = time.Second - - certCfg := &pkiutil.CertConfig{ - Config: certutil.Config{ - CommonName: "test-certs", - AltNames: certutil.AltNames{ - DNSNames: []string{"test-domain.space"}, - }, - Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, - }, - } - - renewer := &APIRenewer{ - client: client, - } - - cert, _, err := renewer.Renew(certCfg) - if err != nil { - t.Fatalf("unexpected error renewing cert: %v", err) - } - - pool := x509.NewCertPool() - pool.AddCert(caCert) - - _, err = cert.Verify(x509.VerifyOptions{ - DNSName: "test-domain.space", - Roots: pool, - KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, - }) - if err != nil { - t.Errorf("couldn't verify new cert: %v", err) - } -} - -func defaultReactionFunc(obj runtime.Object) k8stesting.ReactionFunc { - return func(act k8stesting.Action) (bool, runtime.Object, error) { - return true, obj, nil - } -} - -func getCertReq(t *testing.T, caCert *x509.Certificate, caKey crypto.Signer) *certsapi.CertificateSigningRequest { - cert, _, err := pkiutil.NewCertAndKey(caCert, caKey, &pkiutil.CertConfig{ - Config: certutil.Config{ - CommonName: "testcert", - AltNames: certutil.AltNames{ - DNSNames: []string{"test-domain.space"}, - }, - Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, - }, - }) - if err != nil { - t.Fatalf("couldn't generate cert: %v", err) - } - - return &certsapi.CertificateSigningRequest{ - ObjectMeta: metav1.ObjectMeta{ - Name: "testcert", - }, - Status: certsapi.CertificateSigningRequestStatus{ - Conditions: []certsapi.CertificateSigningRequestCondition{ - { - Type: certsapi.CertificateApproved, - }, - }, - Certificate: pkiutil.EncodeCertPEM(cert), - }, - } -} diff --git a/cmd/kubeadm/app/phases/certs/renewal/manager.go b/cmd/kubeadm/app/phases/certs/renewal/manager.go index c7245e50ed4..0b3f93a31f9 100644 --- a/cmd/kubeadm/app/phases/certs/renewal/manager.go +++ b/cmd/kubeadm/app/phases/certs/renewal/manager.go @@ -21,7 +21,6 @@ import ( "sort" "github.com/pkg/errors" - clientset "k8s.io/client-go/kubernetes" certutil "k8s.io/client-go/util/cert" kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants" @@ -250,43 +249,6 @@ func (rm *Manager) RenewUsingLocalCA(name string) (bool, error) { return true, nil } -// RenewUsingCSRAPI executes certificate renewal uses the K8s certificate API. -// For PKI certificates, use the name defined in the certsphase package, while for certificates -// embedded in the kubeConfig files, use the kubeConfig file name defined in the kubeadm constants package. -// If you use the CertificateRenewHandler returned by Certificates func, handler.Name already contains the right value. -func (rm *Manager) RenewUsingCSRAPI(name string, client clientset.Interface) error { - handler, ok := rm.certificates[name] - if !ok { - return errors.Errorf("%s is not a valid certificate for this cluster", name) - } - - // reads the current certificate - cert, err := handler.readwriter.Read() - if err != nil { - return err - } - - // extract the certificate config - cfg := &pkiutil.CertConfig{ - Config: certToConfig(cert), - PublicKeyAlgorithm: rm.cfg.PublicKeyAlgorithm(), - } - - // create a new certificate with the same config - newCert, newKey, err := NewAPIRenewer(client).Renew(cfg) - if err != nil { - return errors.Wrapf(err, "failed to renew certificate %s", name) - } - - // writes the new certificate to disk - err = handler.readwriter.Write(newCert, newKey) - if err != nil { - return err - } - - return nil -} - // CreateRenewCSR generates CSR request for certificate renewal. // For PKI certificates, use the name defined in the certsphase package, while for certificates // embedded in the kubeConfig files, use the kubeConfig file name defined in the kubeadm constants package.