mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-24 12:15:52 +00:00
Merge pull request #68749 from liztio/renew-etcd-certs
Renew certificates as part of upgrade rather than recreating them
This commit is contained in:
commit
e7eb26919b
@ -25,6 +25,7 @@ go_library(
|
||||
"//cmd/kubeadm/app/phases/bootstraptoken/clusterinfo:go_default_library",
|
||||
"//cmd/kubeadm/app/phases/bootstraptoken/node:go_default_library",
|
||||
"//cmd/kubeadm/app/phases/certs:go_default_library",
|
||||
"//cmd/kubeadm/app/phases/certs/renewal:go_default_library",
|
||||
"//cmd/kubeadm/app/phases/controlplane:go_default_library",
|
||||
"//cmd/kubeadm/app/phases/etcd:go_default_library",
|
||||
"//cmd/kubeadm/app/phases/kubelet:go_default_library",
|
||||
@ -79,12 +80,14 @@ go_test(
|
||||
"//cmd/kubeadm/app/apis/kubeadm/validation:go_default_library",
|
||||
"//cmd/kubeadm/app/constants:go_default_library",
|
||||
"//cmd/kubeadm/app/phases/certs:go_default_library",
|
||||
"//cmd/kubeadm/app/phases/certs/pkiutil:go_default_library",
|
||||
"//cmd/kubeadm/app/phases/controlplane:go_default_library",
|
||||
"//cmd/kubeadm/app/phases/etcd:go_default_library",
|
||||
"//cmd/kubeadm/app/util/apiclient:go_default_library",
|
||||
"//cmd/kubeadm/app/util/config:go_default_library",
|
||||
"//cmd/kubeadm/app/util/etcd:go_default_library",
|
||||
"//cmd/kubeadm/test:go_default_library",
|
||||
"//cmd/kubeadm/test/certs:go_default_library",
|
||||
"//pkg/util/version:go_default_library",
|
||||
"//staging/src/k8s.io/api/apps/v1:go_default_library",
|
||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||
|
@ -25,6 +25,7 @@ import (
|
||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||
certsphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/certs"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/phases/certs/renewal"
|
||||
controlplanephase "k8s.io/kubernetes/cmd/kubeadm/app/phases/controlplane"
|
||||
etcdphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/etcd"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/util"
|
||||
@ -185,31 +186,8 @@ func upgradeComponent(component string, waiter apiclient.Waiter, pathMgr StaticP
|
||||
}
|
||||
}
|
||||
|
||||
if cfg.Etcd.Local != nil {
|
||||
// ensure etcd certs are generated for etcd and kube-apiserver
|
||||
if component == constants.Etcd || component == constants.KubeAPIServer {
|
||||
caCert, caKey, err := certsphase.KubeadmCertEtcdCA.CreateAsCA(cfg)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to upgrade the %s CA certificate and key: %v", constants.Etcd, err)
|
||||
}
|
||||
|
||||
if component == constants.Etcd {
|
||||
if err := certsphase.KubeadmCertEtcdServer.CreateFromCA(cfg, caCert, caKey); err != nil {
|
||||
return fmt.Errorf("failed to upgrade the %s certificate and key: %v", constants.Etcd, err)
|
||||
}
|
||||
if err := certsphase.KubeadmCertEtcdPeer.CreateFromCA(cfg, caCert, caKey); err != nil {
|
||||
return fmt.Errorf("failed to upgrade the %s peer certificate and key: %v", constants.Etcd, err)
|
||||
}
|
||||
if err := certsphase.KubeadmCertEtcdHealthcheck.CreateFromCA(cfg, caCert, caKey); err != nil {
|
||||
return fmt.Errorf("failed to upgrade the %s healthcheck certificate and key: %v", constants.Etcd, err)
|
||||
}
|
||||
}
|
||||
if component == constants.KubeAPIServer {
|
||||
if err := certsphase.KubeadmCertEtcdAPIClient.CreateFromCA(cfg, caCert, caKey); err != nil {
|
||||
return fmt.Errorf("failed to upgrade the %s %s-client certificate and key: %v", constants.KubeAPIServer, constants.Etcd, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := renewCerts(cfg, component); err != nil {
|
||||
return fmt.Errorf("failed to renew certificates for component %q: %v", component, err)
|
||||
}
|
||||
|
||||
// The old manifest is here; in the /etc/kubernetes/manifests/
|
||||
@ -524,3 +502,35 @@ func rollbackEtcdData(cfg *kubeadmapi.InitConfiguration, pathMgr StaticPodPathMa
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func renewCerts(cfg *kubeadmapi.InitConfiguration, component string) error {
|
||||
if cfg.Etcd.Local != nil {
|
||||
// ensure etcd certs are loaded for etcd and kube-apiserver
|
||||
if component == constants.Etcd || component == constants.KubeAPIServer {
|
||||
caCert, caKey, err := certsphase.LoadCertificateAuthority(cfg.CertificatesDir, certsphase.KubeadmCertEtcdCA.BaseName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to upgrade the %s CA certificate and key: %v", constants.Etcd, err)
|
||||
}
|
||||
renewer := renewal.NewFileRenewal(caCert, caKey)
|
||||
|
||||
if component == constants.Etcd {
|
||||
for _, cert := range []*certsphase.KubeadmCert{
|
||||
&certsphase.KubeadmCertEtcdServer,
|
||||
&certsphase.KubeadmCertEtcdPeer,
|
||||
&certsphase.KubeadmCertEtcdHealthcheck,
|
||||
} {
|
||||
if err := renewal.RenewExistingCert(cfg.CertificatesDir, cert.BaseName, renewer); err != nil {
|
||||
return fmt.Errorf("failed to renew %s certificate and key: %v", cert.Name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
if component == constants.KubeAPIServer {
|
||||
cert := certsphase.KubeadmCertEtcdAPIClient
|
||||
if err := renewal.RenewExistingCert(cfg.CertificatesDir, cert.BaseName, renewer); err != nil {
|
||||
return fmt.Errorf("failed to renew %s certificate and key: %v", cert.Name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ import (
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@ -32,11 +33,14 @@ import (
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||
certsphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/certs"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/phases/certs/pkiutil"
|
||||
controlplanephase "k8s.io/kubernetes/cmd/kubeadm/app/phases/controlplane"
|
||||
etcdphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/etcd"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient"
|
||||
configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config"
|
||||
etcdutil "k8s.io/kubernetes/cmd/kubeadm/app/util/etcd"
|
||||
testutil "k8s.io/kubernetes/cmd/kubeadm/test"
|
||||
certstestutil "k8s.io/kubernetes/cmd/kubeadm/test/certs"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -456,6 +460,22 @@ func TestStaticPodControlPlane(t *testing.T) {
|
||||
t.Fatalf("couldn't create config: %v", err)
|
||||
}
|
||||
|
||||
// create the kubeadm etcd certs
|
||||
caCert, caKey, err := certsphase.KubeadmCertEtcdCA.CreateAsCA(newcfg)
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't create new CA certificate: %v", err)
|
||||
}
|
||||
for _, cert := range []*certsphase.KubeadmCert{
|
||||
&certsphase.KubeadmCertEtcdServer,
|
||||
&certsphase.KubeadmCertEtcdPeer,
|
||||
&certsphase.KubeadmCertEtcdHealthcheck,
|
||||
&certsphase.KubeadmCertEtcdAPIClient,
|
||||
} {
|
||||
if err := cert.CreateFromCA(newcfg, caCert, caKey); err != nil {
|
||||
t.Fatalf("couldn't create certificate %s: %v", cert.Name, err)
|
||||
}
|
||||
}
|
||||
|
||||
actualErr := StaticPodControlPlane(
|
||||
waiter,
|
||||
pathMgr,
|
||||
@ -606,3 +626,113 @@ func TestCleanupDirs(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRenewCerts(t *testing.T) {
|
||||
caCert, caKey := certstestutil.SetupCertificateAuthorithy(t)
|
||||
t.Run("all certs exist, should be rotated", func(t *testing.T) {
|
||||
})
|
||||
tests := []struct {
|
||||
name string
|
||||
component string
|
||||
skipCreateCA bool
|
||||
shouldErrorOnRenew bool
|
||||
certsShouldExist []*certsphase.KubeadmCert
|
||||
}{
|
||||
{
|
||||
name: "all certs exist, should be rotated",
|
||||
component: constants.Etcd,
|
||||
certsShouldExist: []*certsphase.KubeadmCert{
|
||||
&certsphase.KubeadmCertEtcdServer,
|
||||
&certsphase.KubeadmCertEtcdPeer,
|
||||
&certsphase.KubeadmCertEtcdHealthcheck,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "just renew API cert",
|
||||
component: constants.KubeAPIServer,
|
||||
certsShouldExist: []*certsphase.KubeadmCert{
|
||||
&certsphase.KubeadmCertEtcdAPIClient,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ignores other compnonents",
|
||||
skipCreateCA: true,
|
||||
component: constants.KubeScheduler,
|
||||
},
|
||||
{
|
||||
name: "missing a cert to renew",
|
||||
component: constants.Etcd,
|
||||
shouldErrorOnRenew: true,
|
||||
certsShouldExist: []*certsphase.KubeadmCert{
|
||||
&certsphase.KubeadmCertEtcdServer,
|
||||
&certsphase.KubeadmCertEtcdPeer,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no CA, cannot continue",
|
||||
component: constants.Etcd,
|
||||
skipCreateCA: true,
|
||||
shouldErrorOnRenew: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
// Setup up basic requities
|
||||
tmpDir := testutil.SetupTempDir(t)
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
cfg := testutil.GetDefaultInternalConfig(t)
|
||||
cfg.CertificatesDir = tmpDir
|
||||
|
||||
if !test.skipCreateCA {
|
||||
if err := pkiutil.WriteCertAndKey(tmpDir, constants.EtcdCACertAndKeyBaseName, caCert, caKey); err != nil {
|
||||
t.Fatalf("couldn't write out CA: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Create expected certs
|
||||
for _, kubeCert := range test.certsShouldExist {
|
||||
if err := kubeCert.CreateFromCA(cfg, caCert, caKey); err != nil {
|
||||
t.Fatalf("couldn't renew certificate %q: %v", kubeCert.Name, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Load expected certs to check if serial numbers changes
|
||||
certMaps := make(map[*certsphase.KubeadmCert]big.Int)
|
||||
for _, kubeCert := range test.certsShouldExist {
|
||||
cert, err := pkiutil.TryLoadCertFromDisk(tmpDir, kubeCert.BaseName)
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't load certificate %q: %v", kubeCert.Name, err)
|
||||
}
|
||||
certMaps[kubeCert] = *cert.SerialNumber
|
||||
}
|
||||
|
||||
// Renew everything
|
||||
err := renewCerts(cfg, test.component)
|
||||
if test.shouldErrorOnRenew {
|
||||
if err == nil {
|
||||
t.Fatal("expected renewal error, got nothing")
|
||||
}
|
||||
// expected error, got error
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't renew certificates: %v", err)
|
||||
}
|
||||
|
||||
// See if the certificate serial numbers change
|
||||
for kubeCert, cert := range certMaps {
|
||||
newCert, err := pkiutil.TryLoadCertFromDisk(tmpDir, kubeCert.BaseName)
|
||||
if err != nil {
|
||||
t.Errorf("couldn't load new certificate %q: %v", kubeCert.Name, err)
|
||||
continue
|
||||
}
|
||||
if cert.Cmp(newCert.SerialNumber) == 0 {
|
||||
t.Errorf("certifitate %v was not reissued", kubeCert.Name)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user