diff --git a/cmd/kubeadm/app/cmd/phases/certs.go b/cmd/kubeadm/app/cmd/phases/certs.go index d8220c5d358..7a9ffd74da9 100644 --- a/cmd/kubeadm/app/cmd/phases/certs.go +++ b/cmd/kubeadm/app/cmd/phases/certs.go @@ -98,6 +98,13 @@ var ( If both files already exist, kubeadm skips the generation step and existing files will be used. `+cmdutil.AlphaDisclaimer), kubeadmconstants.EtcdPeerCertName, kubeadmconstants.EtcdPeerKeyName) + etcdHealthcheckClientCertLongDesc = fmt.Sprintf(normalizer.LongDesc(` + Generates the client certificate for liveness probes to healthcheck etcd and the respective key, + and saves them into %s and %s files. + + If both files already exist, kubeadm skips the generation step and existing files will be used. + `+cmdutil.AlphaDisclaimer), kubeadmconstants.EtcdHealthcheckClientCertName, kubeadmconstants.EtcdHealthcheckClientKeyName) + apiServerEtcdServerCertLongDesc = fmt.Sprintf(normalizer.LongDesc(` Generates the client certificate for the API server to connect to etcd securely and the respective key, and saves them into %s and %s files. @@ -206,6 +213,12 @@ func getCertsSubCommands(defaultKubernetesVersion string) []*cobra.Command { long: etcdPeerCertLongDesc, cmdFunc: certsphase.CreateEtcdPeerCertAndKeyFiles, }, + { + use: "etcd-healthcheck-client", + short: "Generates a client certificate for liveness probes to healthcheck etcd", + long: etcdHealthcheckClientCertLongDesc, + cmdFunc: certsphase.CreateEtcdHealthcheckClientCertAndKeyFiles, + }, { use: "apiserver-etcd-client", short: "Generates a client certificate for the API server to connect to etcd securely", diff --git a/cmd/kubeadm/app/cmd/phases/certs_test.go b/cmd/kubeadm/app/cmd/phases/certs_test.go index 415e5d13890..31b9b49ce49 100644 --- a/cmd/kubeadm/app/cmd/phases/certs_test.go +++ b/cmd/kubeadm/app/cmd/phases/certs_test.go @@ -73,6 +73,21 @@ func TestCertsSubCommandsHasFlags(t *testing.T) { { command: "apiserver-kubelet-client", }, + { + command: "etcd-ca", + }, + { + command: "etcd-server", + }, + { + command: "etcd-peer", + }, + { + command: "etcd-healthcheck-client", + }, + { + command: "apiserver-etcd-client", + }, { command: "sa", }, @@ -113,6 +128,16 @@ func TestSubCmdCertsCreateFilesWithFlags(t *testing.T) { subCmds: []string{"ca", "apiserver", "apiserver-kubelet-client"}, expectedFiles: []string{kubeadmconstants.CACertName, kubeadmconstants.CAKeyName, kubeadmconstants.APIServerCertName, kubeadmconstants.APIServerKeyName, kubeadmconstants.APIServerKubeletClientCertName, kubeadmconstants.APIServerKubeletClientKeyName}, }, + { + subCmds: []string{"etcd-ca", "etcd-server", "etcd-peer", "etcd-healthcheck-client", "apiserver-etcd-client"}, + expectedFiles: []string{ + kubeadmconstants.EtcdCACertName, kubeadmconstants.EtcdCAKeyName, + kubeadmconstants.EtcdServerCertName, kubeadmconstants.EtcdServerKeyName, + kubeadmconstants.EtcdPeerCertName, kubeadmconstants.EtcdPeerKeyName, + kubeadmconstants.EtcdHealthcheckClientCertName, kubeadmconstants.EtcdHealthcheckClientKeyName, + kubeadmconstants.APIServerEtcdClientCertName, kubeadmconstants.APIServerEtcdClientKeyName, + }, + }, { subCmds: []string{"sa"}, expectedFiles: []string{kubeadmconstants.ServiceAccountPrivateKeyName, kubeadmconstants.ServiceAccountPublicKeyName}, @@ -204,6 +229,16 @@ func TestSubCmdCertsCreateFilesWithConfigFile(t *testing.T) { subCmds: []string{"ca", "apiserver", "apiserver-kubelet-client"}, expectedFiles: []string{kubeadmconstants.CACertName, kubeadmconstants.CAKeyName, kubeadmconstants.APIServerCertName, kubeadmconstants.APIServerKeyName, kubeadmconstants.APIServerKubeletClientCertName, kubeadmconstants.APIServerKubeletClientKeyName}, }, + { + subCmds: []string{"etcd-ca", "etcd-server", "etcd-peer", "etcd-healthcheck-client", "apiserver-etcd-client"}, + expectedFiles: []string{ + kubeadmconstants.EtcdCACertName, kubeadmconstants.EtcdCAKeyName, + kubeadmconstants.EtcdServerCertName, kubeadmconstants.EtcdServerKeyName, + kubeadmconstants.EtcdPeerCertName, kubeadmconstants.EtcdPeerKeyName, + kubeadmconstants.EtcdHealthcheckClientCertName, kubeadmconstants.EtcdHealthcheckClientKeyName, + kubeadmconstants.APIServerEtcdClientCertName, kubeadmconstants.APIServerEtcdClientKeyName, + }, + }, { subCmds: []string{"front-proxy-ca", "front-proxy-client"}, expectedFiles: []string{kubeadmconstants.FrontProxyCACertName, kubeadmconstants.FrontProxyCAKeyName, kubeadmconstants.FrontProxyClientCertName, kubeadmconstants.FrontProxyClientKeyName}, diff --git a/cmd/kubeadm/app/constants/constants.go b/cmd/kubeadm/app/constants/constants.go index f0acb1e25d2..66a782a86e8 100644 --- a/cmd/kubeadm/app/constants/constants.go +++ b/cmd/kubeadm/app/constants/constants.go @@ -90,13 +90,22 @@ const ( // EtcdPeerCertCommonName defines etcd's peer certificate common name (CN) EtcdPeerCertCommonName = "kube-etcd-peer" - // APIServerEtcdClientCertAndKeyBaseName defines etcd client certificate and key base name + // EtcdHealthcheckClientCertAndKeyBaseName defines etcd's healthcheck client certificate and key base name + EtcdHealthcheckClientCertAndKeyBaseName = "etcd/healthcheck-client" + // EtcdHealthcheckClientCertName defines etcd's healthcheck client certificate name + EtcdHealthcheckClientCertName = "etcd/healthcheck-client.crt" + // EtcdHealthcheckClientKeyName defines etcd's healthcheck client key name + EtcdHealthcheckClientKeyName = "etcd/healthcheck-client.key" + // EtcdHealthcheckClientCertCommonName defines etcd's healthcheck client certificate common name (CN) + EtcdHealthcheckClientCertCommonName = "kube-etcd-healthcheck-client" + + // APIServerEtcdClientCertAndKeyBaseName defines apiserver's etcd client certificate and key base name APIServerEtcdClientCertAndKeyBaseName = "apiserver-etcd-client" - // APIServerEtcdClientCertName defines etcd client certificate name + // APIServerEtcdClientCertName defines apiserver's etcd client certificate name APIServerEtcdClientCertName = "apiserver-etcd-client.crt" - // APIServerEtcdClientKeyName defines etcd client key name + // APIServerEtcdClientKeyName defines apiserver's etcd client key name APIServerEtcdClientKeyName = "apiserver-etcd-client.key" - // APIServerEtcdClientCertCommonName defines etcd client certificate common name (CN) + // APIServerEtcdClientCertCommonName defines apiserver's etcd client certificate common name (CN) APIServerEtcdClientCertCommonName = "kube-apiserver-etcd-client" // ServiceAccountKeyBaseName defines SA key base name diff --git a/cmd/kubeadm/app/phases/certs/certs.go b/cmd/kubeadm/app/phases/certs/certs.go index 2d9a1026853..f45d3247ce5 100644 --- a/cmd/kubeadm/app/phases/certs/certs.go +++ b/cmd/kubeadm/app/phases/certs/certs.go @@ -40,6 +40,7 @@ func CreatePKIAssets(cfg *kubeadmapi.MasterConfiguration) error { CreateEtcdCACertAndKeyFiles, CreateEtcdServerCertAndKeyFiles, CreateEtcdPeerCertAndKeyFiles, + CreateEtcdHealthcheckClientCertAndKeyFiles, CreateAPIServerEtcdClientCertAndKeyFiles, CreateServiceAccountKeyAndPublicKeyFiles, CreateFrontProxyCACertAndKeyFiles, @@ -190,6 +191,30 @@ func CreateEtcdPeerCertAndKeyFiles(cfg *kubeadmapi.MasterConfiguration) error { ) } +// CreateEtcdHealthcheckClientCertAndKeyFiles create a new client certificate for liveness probes to healthcheck etcd +// If the etcd-healthcheck-client certificate and key file already exist in the target folder, they are used only if evaluated equal; otherwise an error is returned. +// It assumes the etcd CA certificate and key file exist in the CertificatesDir +func CreateEtcdHealthcheckClientCertAndKeyFiles(cfg *kubeadmapi.MasterConfiguration) error { + + etcdCACert, etcdCAKey, err := loadCertificateAuthority(cfg.CertificatesDir, kubeadmconstants.EtcdCACertAndKeyBaseName) + if err != nil { + return err + } + + etcdHealthcheckClientCert, etcdHealthcheckClientKey, err := NewEtcdHealthcheckClientCertAndKey(etcdCACert, etcdCAKey) + if err != nil { + return err + } + + return writeCertificateFilesIfNotExist( + cfg.CertificatesDir, + kubeadmconstants.EtcdHealthcheckClientCertAndKeyBaseName, + etcdCACert, + etcdHealthcheckClientCert, + etcdHealthcheckClientKey, + ) +} + // CreateAPIServerEtcdClientCertAndKeyFiles create a new client certificate for the apiserver calling etcd // If the apiserver-etcd-client certificate and key file already exist in the target folder, they are used only if evaluated equal; otherwise an error is returned. // It assumes the etcd CA certificate and key file exist in the CertificatesDir @@ -375,6 +400,22 @@ func NewEtcdPeerCertAndKey(cfg *kubeadmapi.MasterConfiguration, caCert *x509.Cer return etcdPeerCert, etcdPeerKey, nil } +// NewEtcdHealthcheckClientCertAndKey generate certificate for liveness probes to healthcheck etcd, signed by the given CA. +func NewEtcdHealthcheckClientCertAndKey(caCert *x509.Certificate, caKey *rsa.PrivateKey) (*x509.Certificate, *rsa.PrivateKey, error) { + + config := certutil.Config{ + CommonName: kubeadmconstants.EtcdHealthcheckClientCertCommonName, + Organization: []string{kubeadmconstants.MastersGroup}, + Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, + } + etcdHealcheckClientCert, etcdHealcheckClientKey, err := pkiutil.NewCertAndKey(caCert, caKey, config) + if err != nil { + return nil, nil, fmt.Errorf("failure while creating etcd healthcheck client key and certificate: %v", err) + } + + return etcdHealcheckClientCert, etcdHealcheckClientKey, nil +} + // NewAPIServerEtcdClientCertAndKey generate certificate for the apiservers to connect to etcd securely, signed by the given CA. func NewAPIServerEtcdClientCertAndKey(caCert *x509.Certificate, caKey *rsa.PrivateKey) (*x509.Certificate, *rsa.PrivateKey, error) { diff --git a/cmd/kubeadm/app/phases/certs/certs_test.go b/cmd/kubeadm/app/phases/certs/certs_test.go index e35759e6c38..65982f135d0 100644 --- a/cmd/kubeadm/app/phases/certs/certs_test.go +++ b/cmd/kubeadm/app/phases/certs/certs_test.go @@ -382,6 +382,22 @@ func TestNewEtcdPeerCertAndKey(t *testing.T) { } } +func TestNewEtcdHealthcheckClientCertAndKey(t *testing.T) { + caCert, caKey, err := NewCACertAndKey() + if err != nil { + t.Fatalf("failed creation of ca cert and key: %v", err) + } + + etcdHealthcheckClientCert, _, err := NewEtcdHealthcheckClientCertAndKey(caCert, caKey) + if err != nil { + t.Fatalf("failed creation of cert and key: %v", err) + } + + certstestutil.AssertCertificateIsSignedByCa(t, etcdHealthcheckClientCert, caCert) + certstestutil.AssertCertificateHasClientAuthUsage(t, etcdHealthcheckClientCert) + certstestutil.AssertCertificateHasOrganizations(t, etcdHealthcheckClientCert, kubeadmconstants.MastersGroup) +} + func TestNewAPIServerEtcdClientCertAndKey(t *testing.T) { caCert, caKey, err := NewCACertAndKey() if err != nil { @@ -595,6 +611,7 @@ func TestCreateCertificateFilesMethods(t *testing.T) { kubeadmconstants.EtcdCACertName, kubeadmconstants.EtcdCAKeyName, kubeadmconstants.EtcdServerCertName, kubeadmconstants.EtcdServerKeyName, kubeadmconstants.EtcdPeerCertName, kubeadmconstants.EtcdPeerKeyName, + kubeadmconstants.EtcdHealthcheckClientCertName, kubeadmconstants.EtcdHealthcheckClientKeyName, kubeadmconstants.APIServerEtcdClientCertName, kubeadmconstants.APIServerEtcdClientKeyName, kubeadmconstants.ServiceAccountPrivateKeyName, kubeadmconstants.ServiceAccountPublicKeyName, kubeadmconstants.FrontProxyCACertName, kubeadmconstants.FrontProxyCAKeyName, @@ -629,6 +646,11 @@ func TestCreateCertificateFilesMethods(t *testing.T) { createFunc: CreateEtcdPeerCertAndKeyFiles, expectedFiles: []string{kubeadmconstants.EtcdPeerCertName, kubeadmconstants.EtcdPeerKeyName}, }, + { + setupFunc: CreateEtcdCACertAndKeyFiles, + createFunc: CreateEtcdHealthcheckClientCertAndKeyFiles, + expectedFiles: []string{kubeadmconstants.EtcdHealthcheckClientCertName, kubeadmconstants.EtcdHealthcheckClientKeyName}, + }, { setupFunc: CreateEtcdCACertAndKeyFiles, createFunc: CreateAPIServerEtcdClientCertAndKeyFiles, diff --git a/cmd/kubeadm/app/phases/certs/doc.go b/cmd/kubeadm/app/phases/certs/doc.go index ffad90b0737..0a60f992dcc 100644 --- a/cmd/kubeadm/app/phases/certs/doc.go +++ b/cmd/kubeadm/app/phases/certs/doc.go @@ -46,6 +46,8 @@ package certs - etcd/server.key - etcd/peer.crt - etcd/peer.key + - etcd/healthcheck-client.crt + - etcd/healthcheck-client.key - sa.pub - sa.key - front-proxy-ca.crt diff --git a/cmd/kubeadm/app/phases/upgrade/staticpods.go b/cmd/kubeadm/app/phases/upgrade/staticpods.go index 47718c751eb..5d45fdb5606 100644 --- a/cmd/kubeadm/app/phases/upgrade/staticpods.go +++ b/cmd/kubeadm/app/phases/upgrade/staticpods.go @@ -148,6 +148,9 @@ func upgradeComponent(component string, waiter apiclient.Waiter, pathMgr StaticP if err := certsphase.CreateEtcdPeerCertAndKeyFiles(cfg); err != nil { return fmt.Errorf("failed to upgrade the %s peer certificate and key: %v", constants.Etcd, err) } + if err := certsphase.CreateEtcdHealthcheckClientCertAndKeyFiles(cfg); 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.CreateAPIServerEtcdClientCertAndKeyFiles(cfg); err != nil { diff --git a/cmd/kubeadm/app/phases/upgrade/staticpods_test.go b/cmd/kubeadm/app/phases/upgrade/staticpods_test.go index cea9c171d0e..e8e8f3ffafc 100644 --- a/cmd/kubeadm/app/phases/upgrade/staticpods_test.go +++ b/cmd/kubeadm/app/phases/upgrade/staticpods_test.go @@ -327,6 +327,7 @@ func TestStaticPodControlPlane(t *testing.T) { // certsphase.CreateEtcdCACertAndKeyFiles, // certsphase.CreateEtcdServerCertAndKeyFiles, // certsphase.CreateEtcdPeerCertAndKeyFiles, + // certsphase.CreateEtcdHealthcheckClientCertAndKeyFiles, // certsphase.CreateAPIServerEtcdClientCertAndKeyFiles, certsphase.CreateServiceAccountKeyAndPublicKeyFiles, certsphase.CreateFrontProxyCACertAndKeyFiles, diff --git a/docs/.generated_docs b/docs/.generated_docs index e8d3ed9bac9..c0198d7757b 100644 --- a/docs/.generated_docs +++ b/docs/.generated_docs @@ -25,6 +25,7 @@ docs/admin/kubeadm_alpha_phase_certs_apiserver-kubelet-client.md docs/admin/kubeadm_alpha_phase_certs_apiserver.md docs/admin/kubeadm_alpha_phase_certs_ca.md docs/admin/kubeadm_alpha_phase_certs_etcd-ca.md +docs/admin/kubeadm_alpha_phase_certs_etcd-healthcheck-client.md docs/admin/kubeadm_alpha_phase_certs_etcd-peer.md docs/admin/kubeadm_alpha_phase_certs_etcd-server.md docs/admin/kubeadm_alpha_phase_certs_front-proxy-ca.md @@ -92,6 +93,7 @@ docs/man/man1/kubeadm-alpha-phase-certs-apiserver-kubelet-client.1 docs/man/man1/kubeadm-alpha-phase-certs-apiserver.1 docs/man/man1/kubeadm-alpha-phase-certs-ca.1 docs/man/man1/kubeadm-alpha-phase-certs-etcd-ca.1 +docs/man/man1/kubeadm-alpha-phase-certs-etcd-healthcheck-client.1 docs/man/man1/kubeadm-alpha-phase-certs-etcd-peer.1 docs/man/man1/kubeadm-alpha-phase-certs-etcd-server.1 docs/man/man1/kubeadm-alpha-phase-certs-front-proxy-ca.1 diff --git a/docs/admin/kubeadm_alpha_phase_certs_etcd-healthcheck-client.md b/docs/admin/kubeadm_alpha_phase_certs_etcd-healthcheck-client.md new file mode 100644 index 00000000000..b6fd7a0f989 --- /dev/null +++ b/docs/admin/kubeadm_alpha_phase_certs_etcd-healthcheck-client.md @@ -0,0 +1,3 @@ +This file is autogenerated, but we've stopped checking such files into the +repository to reduce the need for rebases. Please run hack/generate-docs.sh to +populate this file. diff --git a/docs/man/man1/kubeadm-alpha-phase-certs-etcd-healthcheck-client.1 b/docs/man/man1/kubeadm-alpha-phase-certs-etcd-healthcheck-client.1 new file mode 100644 index 00000000000..b6fd7a0f989 --- /dev/null +++ b/docs/man/man1/kubeadm-alpha-phase-certs-etcd-healthcheck-client.1 @@ -0,0 +1,3 @@ +This file is autogenerated, but we've stopped checking such files into the +repository to reduce the need for rebases. Please run hack/generate-docs.sh to +populate this file.