Skip to content

Commit f9c9ecd

Browse files
authored
Merge pull request kubernetes#77180 from fabriziopandini/renew-embedded-certs
kubeadm: renew certificates embedded in kubeconfig files
2 parents 922f360 + 3076644 commit f9c9ecd

File tree

9 files changed

+640
-165
lines changed

9 files changed

+640
-165
lines changed

cmd/kubeadm/app/cmd/alpha/BUILD

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ go_test(
6262
deps = [
6363
"//cmd/kubeadm/app/constants:go_default_library",
6464
"//cmd/kubeadm/app/phases/certs:go_default_library",
65-
"//cmd/kubeadm/app/util/certs:go_default_library",
65+
"//cmd/kubeadm/app/phases/kubeconfig:go_default_library",
6666
"//cmd/kubeadm/app/util/pkiutil:go_default_library",
6767
"//cmd/kubeadm/test:go_default_library",
6868
"//cmd/kubeadm/test/cmd:go_default_library",

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

Lines changed: 124 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
kubeadmapiv1beta2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta2"
2525
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/options"
2626
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
27+
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
2728
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
2829
certsphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/certs"
2930
"k8s.io/kubernetes/cmd/kubeadm/app/phases/certs/renewal"
@@ -34,11 +35,17 @@ import (
3435
)
3536

3637
var (
37-
genericLongDesc = normalizer.LongDesc(`
38+
genericCertRenewLongDesc = normalizer.LongDesc(`
3839
Renew the %[1]s, and save them into %[2]s.cert and %[2]s.key files.
3940
4041
Extra attributes such as SANs will be based on the existing certificates, there is no need to resupply them.
4142
`)
43+
genericCertRenewEmbeddedLongDesc = normalizer.LongDesc(`
44+
Renew the certificate embedded in the kubeconfig file %s.
45+
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.
47+
`)
48+
4249
allLongDesc = normalizer.LongDesc(`
4350
Renew all known certificates necessary to run the control plane. Renewals are run unconditionally, regardless
4451
of expiration date. Renewals can also be run individually for more control.
@@ -66,7 +73,7 @@ func newCmdCertsRenewal() *cobra.Command {
6673
RunE: cmdutil.SubCmdRunE("renew"),
6774
}
6875

69-
cmd.AddCommand(getRenewSubCommands()...)
76+
cmd.AddCommand(getRenewSubCommands(kubeadmconstants.KubernetesDir)...)
7077

7178
return cmd
7279
}
@@ -80,8 +87,15 @@ type renewConfig struct {
8087
csrPath string
8188
}
8289

83-
func getRenewSubCommands() []*cobra.Command {
84-
cfg := &renewConfig{}
90+
func getRenewSubCommands(kdir string) []*cobra.Command {
91+
cfg := &renewConfig{
92+
cfg: kubeadmapiv1beta2.InitConfiguration{
93+
ClusterConfiguration: kubeadmapiv1beta2.ClusterConfiguration{
94+
// Setting kubernetes version to a default value in order to allow a not necessary internet lookup
95+
KubernetesVersion: constants.CurrentKubernetesVersion.String(),
96+
},
97+
},
98+
}
8599
// Default values for the cobra help text
86100
kubeadmscheme.Scheme.Default(&cfg.cfg)
87101

@@ -95,9 +109,11 @@ func getRenewSubCommands() []*cobra.Command {
95109
// Don't offer to renew CAs; would cause serious consequences
96110
for _, cert := range certs {
97111
// get the cobra.Command skeleton for this command
98-
cmd := generateRenewalCommand(cert, cfg)
112+
cmd := generateCertRenewalCommand(cert, cfg)
99113
// get the implementation of renewing this certificate
100-
renewalFunc := generateRenewalFunction(cert, caCert, cfg)
114+
renewalFunc := func(cert *certsphase.KubeadmCert, caCert *certsphase.KubeadmCert) func() {
115+
return func() { renewCert(cert, caCert, cfg) }
116+
}(cert, caCert)
101117
// install the implementation into the command
102118
cmd.Run = func(*cobra.Command, []string) { renewalFunc() }
103119
cmdList = append(cmdList, cmd)
@@ -106,6 +122,27 @@ func getRenewSubCommands() []*cobra.Command {
106122
}
107123
}
108124

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 {
133+
// get the cobra.Command skeleton for this command
134+
cmd := generateEmbeddedCertRenewalCommand(k, cfg)
135+
// get the implementation of renewing this certificate
136+
renewalFunc := func(kdir, k string) func() {
137+
return func() { renewEmbeddedCert(kdir, k, cfg) }
138+
}(kdir, k)
139+
// install the implementation into the command
140+
cmd.Run = func(*cobra.Command, []string) { renewalFunc() }
141+
cmdList = append(cmdList, cmd)
142+
// Collect renewal functions for `renew all`
143+
funcList = append(funcList, renewalFunc)
144+
}
145+
109146
allCmd := &cobra.Command{
110147
Use: "all",
111148
Short: "Renew all available certificates",
@@ -131,53 +168,100 @@ func addFlags(cmd *cobra.Command, cfg *renewConfig) {
131168
cmd.Flags().BoolVar(&cfg.useAPI, "use-api", cfg.useAPI, "Use the Kubernetes certificate API to renew certificates")
132169
}
133170

134-
func generateRenewalFunction(cert *certsphase.KubeadmCert, caCert *certsphase.KubeadmCert, cfg *renewConfig) func() {
135-
return func() {
136-
internalcfg, err := configutil.LoadOrDefaultInitConfiguration(cfg.cfgPath, &cfg.cfg)
137-
kubeadmutil.CheckErr(err)
171+
func renewCert(cert *certsphase.KubeadmCert, caCert *certsphase.KubeadmCert, cfg *renewConfig) {
172+
internalcfg, err := configutil.LoadOrDefaultInitConfiguration(cfg.cfgPath, &cfg.cfg)
173+
kubeadmutil.CheckErr(err)
138174

139-
if cfg.useCSR {
140-
path := cfg.csrPath
141-
if path == "" {
142-
path = cfg.cfg.CertificatesDir
143-
}
144-
err := certsphase.CreateCSR(cert, internalcfg, path)
145-
kubeadmutil.CheckErr(err)
146-
return
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
147181
}
182+
err := certsphase.CreateCSR(cert, internalcfg, path)
183+
kubeadmutil.CheckErr(err)
184+
return
185+
}
148186

149-
var externalCA bool
150-
switch caCert.BaseName {
151-
case kubeadmconstants.CACertAndKeyBaseName:
152-
// Check if an external CA is provided by the user (when the CA Cert is present but the CA Key is not)
153-
externalCA, _ = certsphase.UsingExternalCA(&internalcfg.ClusterConfiguration)
154-
case kubeadmconstants.FrontProxyCACertAndKeyBaseName:
155-
// 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)
156-
externalCA, _ = certsphase.UsingExternalFrontProxyCA(&internalcfg.ClusterConfiguration)
157-
default:
158-
externalCA = false
159-
}
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+
}
160200

161-
if !externalCA {
162-
renewer, err := getRenewer(cfg, caCert.BaseName)
163-
kubeadmutil.CheckErr(err)
201+
if !externalCA {
202+
renewer, err := getRenewer(cfg, caCert.BaseName)
203+
kubeadmutil.CheckErr(err)
164204

165-
err = renewal.RenewExistingCert(internalcfg.CertificatesDir, cert.BaseName, renewer)
166-
kubeadmutil.CheckErr(err)
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+
}
167211

168-
fmt.Printf("Certificate %s renewed\n", cert.Name)
169-
return
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)
217+
kubeadmutil.CheckErr(err)
218+
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
170225
}
226+
err := certsphase.CreateCSR(nil, internalcfg, path)
227+
kubeadmutil.CheckErr(err)
228+
return
229+
}
230+
231+
// otherwise, the renewal operation has to actually renew a certificate
232+
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)
238+
kubeadmutil.CheckErr(err)
171239

172-
fmt.Printf("Detected external %s, certificate %s can't be renewed\n", cert.CAName, cert.Name)
240+
err = renewal.RenewEmbeddedClientCert(kdir, k, renewer)
241+
kubeadmutil.CheckErr(err)
242+
243+
fmt.Printf("Certificate embedded in %s renewed\n", k)
244+
return
173245
}
246+
247+
fmt.Printf("Detected external CA, certificate embedded in %s can't be renewed\n", k)
174248
}
175249

176-
func generateRenewalCommand(cert *certsphase.KubeadmCert, cfg *renewConfig) *cobra.Command {
250+
func generateCertRenewalCommand(cert *certsphase.KubeadmCert, cfg *renewConfig) *cobra.Command {
177251
cmd := &cobra.Command{
178252
Use: cert.Name,
179-
Short: fmt.Sprintf("Generate the %s", cert.LongName),
180-
Long: fmt.Sprintf(genericLongDesc, cert.LongName, cert.BaseName),
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),
181265
}
182266
addFlags(cmd, cfg)
183267
return cmd

0 commit comments

Comments
 (0)