From 66095128902b9b6164c2062157044ecbd5b2d228 Mon Sep 17 00:00:00 2001 From: xiangpengzhao Date: Sun, 19 Nov 2017 00:41:58 +0800 Subject: [PATCH] Regenerate API server serving certificates when upgrading. --- cmd/kubeadm/app/cmd/upgrade/apply.go | 2 +- cmd/kubeadm/app/phases/upgrade/postupgrade.go | 19 +++- .../app/phases/upgrade/postupgrade_v18_19.go | 95 +++++++++++++++++++ 3 files changed, 114 insertions(+), 2 deletions(-) create mode 100644 cmd/kubeadm/app/phases/upgrade/postupgrade_v18_19.go diff --git a/cmd/kubeadm/app/cmd/upgrade/apply.go b/cmd/kubeadm/app/cmd/upgrade/apply.go index b36ddf23c10..4065c7e2e28 100644 --- a/cmd/kubeadm/app/cmd/upgrade/apply.go +++ b/cmd/kubeadm/app/cmd/upgrade/apply.go @@ -162,7 +162,7 @@ func RunApply(flags *applyFlags) error { } // Upgrade RBAC rules and addons. - if err := upgrade.PerformPostUpgradeTasks(upgradeVars.client, internalcfg); err != nil { + if err := upgrade.PerformPostUpgradeTasks(upgradeVars.client, internalcfg, flags.newK8sVersion); err != nil { return fmt.Errorf("[upgrade/postupgrade] FATAL post-upgrade error: %v", err) } diff --git a/cmd/kubeadm/app/phases/upgrade/postupgrade.go b/cmd/kubeadm/app/phases/upgrade/postupgrade.go index 3452413c38b..8791a7242d8 100644 --- a/cmd/kubeadm/app/phases/upgrade/postupgrade.go +++ b/cmd/kubeadm/app/phases/upgrade/postupgrade.go @@ -17,6 +17,8 @@ limitations under the License. package upgrade import ( + "fmt" + "k8s.io/apimachinery/pkg/util/errors" clientset "k8s.io/client-go/kubernetes" kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" @@ -24,12 +26,14 @@ import ( "k8s.io/kubernetes/cmd/kubeadm/app/phases/addons/proxy" "k8s.io/kubernetes/cmd/kubeadm/app/phases/bootstraptoken/clusterinfo" nodebootstraptoken "k8s.io/kubernetes/cmd/kubeadm/app/phases/bootstraptoken/node" + certsphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/certs" "k8s.io/kubernetes/cmd/kubeadm/app/phases/uploadconfig" + "k8s.io/kubernetes/pkg/util/version" ) // PerformPostUpgradeTasks runs nearly the same functions as 'kubeadm init' would do // Note that the markmaster phase is left out, not needed, and no token is created as that doesn't belong to the upgrade -func PerformPostUpgradeTasks(client clientset.Interface, cfg *kubeadmapi.MasterConfiguration) error { +func PerformPostUpgradeTasks(client clientset.Interface, cfg *kubeadmapi.MasterConfiguration, newK8sVer *version.Version) error { errs := []error{} // Upload currently used configuration to the cluster @@ -64,6 +68,19 @@ func PerformPostUpgradeTasks(client clientset.Interface, cfg *kubeadmapi.MasterC errs = append(errs, err) } + // Don't fail the upgrade phase if failing to determine to backup kube-apiserver cert and key. + if shouldBackup, err := shouldBackupAPIServerCertAndKey(newK8sVer); err != nil { + fmt.Printf("[postupgrade] WARNING: failed to determine to backup kube-apiserver cert and key: %v", err) + } else if shouldBackup { + // Don't fail the upgrade phase if failing to backup kube-apiserver cert and key. + if err := backupAPIServerCertAndKey(); err != nil { + fmt.Printf("[postupgrade] WARNING: failed to backup kube-apiserver cert and key: %v", err) + } + if err := certsphase.CreateAPIServerCertAndKeyFiles(cfg); err != nil { + errs = append(errs, err) + } + } + // Upgrade kube-dns and kube-proxy if err := dns.EnsureDNSAddon(cfg, client); err != nil { errs = append(errs, err) diff --git a/cmd/kubeadm/app/phases/upgrade/postupgrade_v18_19.go b/cmd/kubeadm/app/phases/upgrade/postupgrade_v18_19.go new file mode 100644 index 00000000000..ccedb491cec --- /dev/null +++ b/cmd/kubeadm/app/phases/upgrade/postupgrade_v18_19.go @@ -0,0 +1,95 @@ +/* +Copyright 2017 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 upgrade + +import ( + "crypto/x509" + "encoding/pem" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "time" + + "k8s.io/apimachinery/pkg/util/errors" + kubeadmapiext "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1" + "k8s.io/kubernetes/cmd/kubeadm/app/constants" + "k8s.io/kubernetes/pkg/util/version" +) + +var v190 = version.MustParseSemantic("v1.9.0") + +// backupAPIServerCertAndKey backups the old cert and key of kube-apiserver to a specified directory. +func backupAPIServerCertAndKey() error { + subDir := "expired" + filesToMove := map[string]string{ + filepath.Join(kubeadmapiext.DefaultCertificatesDir, constants.APIServerCertName): filepath.Join(kubeadmapiext.DefaultCertificatesDir, subDir, constants.APIServerCertName), + filepath.Join(kubeadmapiext.DefaultCertificatesDir, constants.APIServerKeyName): filepath.Join(kubeadmapiext.DefaultCertificatesDir, subDir, constants.APIServerKeyName), + } + + return moveFiles(filesToMove) +} + +// moveFiles moves files from one directory to another. +func moveFiles(files map[string]string) error { + filesToRecover := map[string]string{} + for from, to := range files { + if err := os.Rename(from, to); err != nil { + return rollbackFiles(filesToRecover, err) + } + filesToRecover[to] = from + } + return nil +} + +// rollbackFiles moves the files back to the original directory. +func rollbackFiles(files map[string]string, originalErr error) error { + errs := []error{originalErr} + for from, to := range files { + if err := os.Rename(from, to); err != nil { + errs = append(errs, err) + } + } + return fmt.Errorf("couldn't roll back kube-apiserver cert and key! Got errors: %v", errors.NewAggregate(errs)) +} + +// shouldBackupAPIServerCertAndKey check if the new k8s version is at least 1.9.0 +// and kube-apiserver will be expired in 60 days. +func shouldBackupAPIServerCertAndKey(newK8sVer *version.Version) (bool, error) { + if !newK8sVer.AtLeast(v190) { + return false, nil + + } + + data, err := ioutil.ReadFile(filepath.Join(kubeadmapiext.DefaultCertificatesDir, constants.APIServerCertName)) + if err != nil { + return false, fmt.Errorf("failed to read kube-apiserver certificate from disk: %v", err) + } + block, _ := pem.Decode(data) + if block == nil { + return false, fmt.Errorf("expected the kube-apiserver certificate to be PEM encoded") + } + certs, err := x509.ParseCertificates(block.Bytes) + if err != nil { + return false, fmt.Errorf("unable to parse certificate data: %v", err) + } + if certs[0].NotAfter.Sub(time.Now()) < 60*24*time.Hour { + return true, nil + } + + return false, nil +}