Skip to content

Commit e1770e6

Browse files
authored
Merge pull request kubernetes#77780 from fabriziopandini/refactor-renewal-package
Kubeadm: Refactor renewal package
2 parents b7bc5dc + 1c89787 commit e1770e6

19 files changed

+1270
-889
lines changed

cmd/kubeadm/app/cmd/alpha/BUILD

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ go_library(
1212
importpath = "k8s.io/kubernetes/cmd/kubeadm/app/cmd/alpha",
1313
visibility = ["//visibility:public"],
1414
deps = [
15+
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
1516
"//cmd/kubeadm/app/apis/kubeadm/scheme:go_default_library",
1617
"//cmd/kubeadm/app/apis/kubeadm/v1beta2:go_default_library",
1718
"//cmd/kubeadm/app/apis/kubeadm/validation:go_default_library",
@@ -20,7 +21,6 @@ go_library(
2021
"//cmd/kubeadm/app/cmd/util:go_default_library",
2122
"//cmd/kubeadm/app/constants:go_default_library",
2223
"//cmd/kubeadm/app/features:go_default_library",
23-
"//cmd/kubeadm/app/phases/certs:go_default_library",
2424
"//cmd/kubeadm/app/phases/certs/renewal:go_default_library",
2525
"//cmd/kubeadm/app/phases/kubeconfig:go_default_library",
2626
"//cmd/kubeadm/app/phases/kubelet:go_default_library",

cmd/kubeadm/app/cmd/alpha/certs.go

Lines changed: 61 additions & 148 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,16 @@ package alpha
1919
import (
2020
"fmt"
2121

22+
"github.com/pkg/errors"
2223
"github.com/spf13/cobra"
24+
25+
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
2326
kubeadmscheme "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/scheme"
2427
kubeadmapiv1beta2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta2"
2528
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/options"
2629
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
2730
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
2831
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
29-
certsphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/certs"
3032
"k8s.io/kubernetes/cmd/kubeadm/app/phases/certs/renewal"
3133
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
3234
configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config"
@@ -36,14 +38,16 @@ import (
3638

3739
var (
3840
genericCertRenewLongDesc = normalizer.LongDesc(`
39-
Renew the %[1]s, and save them into %[2]s.cert and %[2]s.key files.
41+
Renew the %s.
4042
41-
Extra attributes such as SANs will be based on the existing certificates, there is no need to resupply them.
42-
`)
43-
genericCertRenewEmbeddedLongDesc = normalizer.LongDesc(`
44-
Renew the certificate embedded in the kubeconfig file %s.
43+
Renewals run unconditionally, regardless of certificate expiration date; extra attributes such as SANs will
44+
be based on the existing file/certificates, there is no need to resupply them.
4545
46-
Kubeconfig attributes and certificate extra attributes such as SANs will be based on the existing kubeconfig/certificates, there is no need to resupply them.
46+
Renewal by default tries to use the certificate authority in the local PKI managed by kubeadm; as alternative
47+
it is possible to use K8s certificate API for certificate renewal, or as a last option, to generate a CSR request.
48+
49+
After renewal, in order to make changes effective, is is required to restart control-plane components and
50+
eventually re-distribute the renewed certificate in case the file is used elsewhere.
4751
`)
4852

4953
allLongDesc = normalizer.LongDesc(`
@@ -78,17 +82,17 @@ func newCmdCertsRenewal() *cobra.Command {
7882
return cmd
7983
}
8084

81-
type renewConfig struct {
85+
type renewFlags struct {
8286
cfgPath string
8387
kubeconfigPath string
8488
cfg kubeadmapiv1beta2.InitConfiguration
8589
useAPI bool
86-
useCSR bool
90+
csrOnly bool
8791
csrPath string
8892
}
8993

9094
func getRenewSubCommands(kdir string) []*cobra.Command {
91-
cfg := &renewConfig{
95+
flags := &renewFlags{
9296
cfg: kubeadmapiv1beta2.InitConfiguration{
9397
ClusterConfiguration: kubeadmapiv1beta2.ClusterConfiguration{
9498
// Setting kubernetes version to a default value in order to allow a not necessary internet lookup
@@ -97,45 +101,28 @@ func getRenewSubCommands(kdir string) []*cobra.Command {
97101
},
98102
}
99103
// Default values for the cobra help text
100-
kubeadmscheme.Scheme.Default(&cfg.cfg)
104+
kubeadmscheme.Scheme.Default(&flags.cfg)
101105

102-
certTree, err := certsphase.GetDefaultCertList().AsMap().CertTree()
106+
// Get a renewal manager for a generic Cluster configuration, that is used only for getting
107+
// the list of certificates for building subcommands
108+
rm, err := renewal.NewManager(&kubeadmapi.ClusterConfiguration{}, "")
103109
kubeadmutil.CheckErr(err)
104110

105111
cmdList := []*cobra.Command{}
106112
funcList := []func(){}
107113

108-
for caCert, certs := range certTree {
109-
// Don't offer to renew CAs; would cause serious consequences
110-
for _, cert := range certs {
111-
// get the cobra.Command skeleton for this command
112-
cmd := generateCertRenewalCommand(cert, cfg)
113-
// get the implementation of renewing this certificate
114-
renewalFunc := func(cert *certsphase.KubeadmCert, caCert *certsphase.KubeadmCert) func() {
115-
return func() { renewCert(cert, caCert, cfg) }
116-
}(cert, caCert)
117-
// install the implementation into the command
118-
cmd.Run = func(*cobra.Command, []string) { renewalFunc() }
119-
cmdList = append(cmdList, cmd)
120-
// Collect renewal functions for `renew all`
121-
funcList = append(funcList, renewalFunc)
122-
}
123-
}
124-
125-
kubeconfigs := []string{
126-
kubeadmconstants.AdminKubeConfigFileName,
127-
kubeadmconstants.ControllerManagerKubeConfigFileName,
128-
kubeadmconstants.SchedulerKubeConfigFileName,
129-
//NB. we are escluding KubeletKubeConfig from renewal because management of this certificate is delegated to kubelet
130-
}
131-
132-
for _, k := range kubeconfigs {
114+
for _, handler := range rm.Certificates() {
133115
// get the cobra.Command skeleton for this command
134-
cmd := generateEmbeddedCertRenewalCommand(k, cfg)
116+
cmd := &cobra.Command{
117+
Use: handler.Name,
118+
Short: fmt.Sprintf("Renew the %s", handler.LongName),
119+
Long: fmt.Sprintf(genericCertRenewLongDesc, handler.LongName),
120+
}
121+
addFlags(cmd, flags)
135122
// get the implementation of renewing this certificate
136-
renewalFunc := func(kdir, k string) func() {
137-
return func() { renewEmbeddedCert(kdir, k, cfg) }
138-
}(kdir, k)
123+
renewalFunc := func(handler *renewal.CertificateRenewHandler) func() {
124+
return func() { renewCert(flags, kdir, handler) }
125+
}(handler)
139126
// install the implementation into the command
140127
cmd.Run = func(*cobra.Command, []string) { renewalFunc() }
141128
cmdList = append(cmdList, cmd)
@@ -153,134 +140,60 @@ func getRenewSubCommands(kdir string) []*cobra.Command {
153140
}
154141
},
155142
}
156-
addFlags(allCmd, cfg)
143+
addFlags(allCmd, flags)
157144

158145
cmdList = append(cmdList, allCmd)
159146
return cmdList
160147
}
161148

162-
func addFlags(cmd *cobra.Command, cfg *renewConfig) {
163-
options.AddConfigFlag(cmd.Flags(), &cfg.cfgPath)
164-
options.AddCertificateDirFlag(cmd.Flags(), &cfg.cfg.CertificatesDir)
165-
options.AddKubeConfigFlag(cmd.Flags(), &cfg.kubeconfigPath)
166-
options.AddCSRFlag(cmd.Flags(), &cfg.useCSR)
167-
options.AddCSRDirFlag(cmd.Flags(), &cfg.csrPath)
168-
cmd.Flags().BoolVar(&cfg.useAPI, "use-api", cfg.useAPI, "Use the Kubernetes certificate API to renew certificates")
149+
func addFlags(cmd *cobra.Command, flags *renewFlags) {
150+
options.AddConfigFlag(cmd.Flags(), &flags.cfgPath)
151+
options.AddCertificateDirFlag(cmd.Flags(), &flags.cfg.CertificatesDir)
152+
options.AddKubeConfigFlag(cmd.Flags(), &flags.kubeconfigPath)
153+
options.AddCSRFlag(cmd.Flags(), &flags.csrOnly)
154+
options.AddCSRDirFlag(cmd.Flags(), &flags.csrPath)
155+
cmd.Flags().BoolVar(&flags.useAPI, "use-api", flags.useAPI, "Use the Kubernetes certificate API to renew certificates")
169156
}
170157

171-
func renewCert(cert *certsphase.KubeadmCert, caCert *certsphase.KubeadmCert, cfg *renewConfig) {
172-
internalcfg, err := configutil.LoadOrDefaultInitConfiguration(cfg.cfgPath, &cfg.cfg)
158+
func renewCert(flags *renewFlags, kdir string, handler *renewal.CertificateRenewHandler) {
159+
internalcfg, err := configutil.LoadOrDefaultInitConfiguration(flags.cfgPath, &flags.cfg)
173160
kubeadmutil.CheckErr(err)
174161

175-
// if the renewal operation is set to generate only CSR request
176-
if cfg.useCSR {
177-
// trigger CSR generation in the csrPath, or if this one is missing, in the CertificateDir
178-
path := cfg.csrPath
179-
if path == "" {
180-
path = cfg.cfg.CertificatesDir
181-
}
182-
err := certsphase.CreateCSR(cert, internalcfg, path)
183-
kubeadmutil.CheckErr(err)
184-
return
185-
}
186-
187-
// otherwise, the renewal operation has to actually renew a certificate
188-
189-
var externalCA bool
190-
switch caCert.BaseName {
191-
case kubeadmconstants.CACertAndKeyBaseName:
192-
// Check if an external CA is provided by the user (when the CA Cert is present but the CA Key is not)
193-
externalCA, _ = certsphase.UsingExternalCA(&internalcfg.ClusterConfiguration)
194-
case kubeadmconstants.FrontProxyCACertAndKeyBaseName:
195-
// Check if an external Front-Proxy CA is provided by the user (when the Front-Proxy CA Cert is present but the Front-Proxy CA Key is not)
196-
externalCA, _ = certsphase.UsingExternalFrontProxyCA(&internalcfg.ClusterConfiguration)
197-
default:
198-
externalCA = false
199-
}
200-
201-
if !externalCA {
202-
renewer, err := getRenewer(cfg, caCert.BaseName)
203-
kubeadmutil.CheckErr(err)
204-
205-
err = renewal.RenewExistingCert(internalcfg.CertificatesDir, cert.BaseName, renewer)
206-
kubeadmutil.CheckErr(err)
207-
208-
fmt.Printf("Certificate %s renewed\n", cert.Name)
209-
return
210-
}
211-
212-
fmt.Printf("Detected external %s, certificate %s can't be renewed\n", cert.CAName, cert.Name)
213-
}
214-
215-
func renewEmbeddedCert(kdir, k string, cfg *renewConfig) {
216-
internalcfg, err := configutil.LoadOrDefaultInitConfiguration(cfg.cfgPath, &cfg.cfg)
162+
// Get a renewal manager for the given cluster configuration
163+
rm, err := renewal.NewManager(&internalcfg.ClusterConfiguration, kdir)
217164
kubeadmutil.CheckErr(err)
218165

219-
// if the renewal operation is set to generate only CSR request
220-
if cfg.useCSR {
221-
// trigger CSR generation in the csrPath, or if this one is missing, in the CertificateDir
222-
path := cfg.csrPath
223-
if path == "" {
224-
path = cfg.cfg.CertificatesDir
166+
// if the renewal operation is set to generate CSR request only
167+
if flags.csrOnly {
168+
// checks a path for storing CSR request is given
169+
if flags.csrPath == "" {
170+
kubeadmutil.CheckErr(errors.New("please provide a path where CSR request should be stored"))
225171
}
226-
err := certsphase.CreateCSR(nil, internalcfg, path)
172+
err := rm.CreateRenewCSR(handler.Name, flags.csrPath)
227173
kubeadmutil.CheckErr(err)
228174
return
229175
}
230176

231177
// otherwise, the renewal operation has to actually renew a certificate
232178

233-
// Check if an external CA is provided by the user (when the CA Cert is present but the CA Key is not)
234-
externalCA, _ := certsphase.UsingExternalCA(&internalcfg.ClusterConfiguration)
235-
236-
if !externalCA {
237-
renewer, err := getRenewer(cfg, certsphase.KubeadmCertRootCA.BaseName)
179+
// renew the certificate using the requested renew method
180+
if flags.useAPI {
181+
// renew using K8s certificate API
182+
kubeConfigPath := cmdutil.GetKubeConfigPath(flags.kubeconfigPath)
183+
client, err := kubeconfigutil.ClientSetFromFile(kubeConfigPath)
238184
kubeadmutil.CheckErr(err)
239185

240-
err = renewal.RenewEmbeddedClientCert(kdir, k, renewer)
186+
err = rm.RenewUsingCSRAPI(handler.Name, client)
241187
kubeadmutil.CheckErr(err)
242-
243-
fmt.Printf("Certificate embedded in %s renewed\n", k)
244-
return
245-
}
246-
247-
fmt.Printf("Detected external CA, certificate embedded in %s can't be renewed\n", k)
248-
}
249-
250-
func generateCertRenewalCommand(cert *certsphase.KubeadmCert, cfg *renewConfig) *cobra.Command {
251-
cmd := &cobra.Command{
252-
Use: cert.Name,
253-
Short: fmt.Sprintf("Renew the %s", cert.LongName),
254-
Long: fmt.Sprintf(genericCertRenewLongDesc, cert.LongName, cert.BaseName),
255-
}
256-
addFlags(cmd, cfg)
257-
return cmd
258-
}
259-
260-
func generateEmbeddedCertRenewalCommand(k string, cfg *renewConfig) *cobra.Command {
261-
cmd := &cobra.Command{
262-
Use: k,
263-
Short: fmt.Sprintf("Renew the certificate embedded in %s", k),
264-
Long: fmt.Sprintf(genericCertRenewEmbeddedLongDesc, k),
265-
}
266-
addFlags(cmd, cfg)
267-
return cmd
268-
}
269-
270-
func getRenewer(cfg *renewConfig, caCertBaseName string) (renewal.Interface, error) {
271-
if cfg.useAPI {
272-
kubeConfigPath := cmdutil.GetKubeConfigPath(cfg.kubeconfigPath)
273-
client, err := kubeconfigutil.ClientSetFromFile(kubeConfigPath)
274-
if err != nil {
275-
return nil, err
188+
} else {
189+
// renew using local certificate authorities.
190+
// this operation can't complete in case the certificate key is not provided (external CA)
191+
renewed, err := rm.RenewUsingLocalCA(handler.Name)
192+
kubeadmutil.CheckErr(err)
193+
if !renewed {
194+
fmt.Printf("Detected external %s, %s can't be renewed\n", handler.CABaseName, handler.LongName)
195+
return
276196
}
277-
return renewal.NewCertsAPIRenawal(client), nil
278197
}
279-
280-
caCert, caKey, err := certsphase.LoadCertificateAuthority(cfg.cfg.CertificatesDir, caCertBaseName)
281-
if err != nil {
282-
return nil, err
283-
}
284-
285-
return renewal.NewFileRenewal(caCert, caKey), nil
198+
fmt.Printf("%s renewed\n", handler.LongName)
286199
}

0 commit comments

Comments
 (0)