Skip to content

Commit b893aa7

Browse files
authored
Merge pull request kubernetes#88052 from neolit123/1.18-renew-use-ca-in-kubeconfig
kubeadm: update embedded CA in kubeconfig files on renewal
2 parents 8ed008a + 0ba5891 commit b893aa7

File tree

4 files changed

+47
-10
lines changed

4 files changed

+47
-10
lines changed

cmd/kubeadm/app/phases/certs/renewal/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ go_test(
4141
embed = [":go_default_library"],
4242
deps = [
4343
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
44+
"//cmd/kubeadm/app/constants:go_default_library",
4445
"//cmd/kubeadm/app/util/certs:go_default_library",
4546
"//cmd/kubeadm/app/util/kubeconfig:go_default_library",
4647
"//cmd/kubeadm/app/util/pkiutil:go_default_library",

cmd/kubeadm/app/phases/certs/renewal/manager.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,8 @@ func NewManager(cfg *kubeadmapi.ClusterConfiguration, kubernetesDir string) (*Ma
154154
// create a CertificateRenewHandler for each kubeConfig file
155155
for _, kubeConfig := range kubeConfigs {
156156
// create a ReadWriter for certificates embedded in kubeConfig files
157-
kubeConfigReadWriter := newKubeconfigReadWriter(kubernetesDir, kubeConfig.fileName)
157+
kubeConfigReadWriter := newKubeconfigReadWriter(kubernetesDir, kubeConfig.fileName,
158+
rm.cfg.CertificatesDir, kubeadmconstants.CACertAndKeyBaseName)
158159

159160
// adds the certificateRenewHandler.
160161
// Certificates embedded kubeConfig files in are indexed by fileName, that is a well know constant defined

cmd/kubeadm/app/phases/certs/renewal/readwriter.go

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
2929
certutil "k8s.io/client-go/util/cert"
3030
"k8s.io/client-go/util/keyutil"
31+
certsphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/certs"
3132
pkiutil "k8s.io/kubernetes/cmd/kubeadm/app/util/pkiutil"
3233
)
3334

@@ -104,14 +105,19 @@ type kubeConfigReadWriter struct {
104105
kubeConfigFileName string
105106
kubeConfigFilePath string
106107
kubeConfig *clientcmdapi.Config
108+
baseName string
109+
certificateDir string
110+
caCert *x509.Certificate
107111
}
108112

109113
// newKubeconfigReadWriter return a new kubeConfigReadWriter
110-
func newKubeconfigReadWriter(kubernetesDir string, kubeConfigFileName string) *kubeConfigReadWriter {
114+
func newKubeconfigReadWriter(kubernetesDir string, kubeConfigFileName string, certificateDir, baseName string) *kubeConfigReadWriter {
111115
return &kubeConfigReadWriter{
112116
kubernetesDir: kubernetesDir,
113117
kubeConfigFileName: kubeConfigFileName,
114118
kubeConfigFilePath: filepath.Join(kubernetesDir, kubeConfigFileName),
119+
certificateDir: certificateDir,
120+
baseName: baseName,
115121
}
116122
}
117123

@@ -130,6 +136,16 @@ func (rw *kubeConfigReadWriter) Read() (*x509.Certificate, error) {
130136
return nil, errors.Wrapf(err, "failed to load kubeConfig file %s", rw.kubeConfigFilePath)
131137
}
132138

139+
// The CA cert is required for updating kubeconfig files.
140+
// For local CA renewal, the local CA on disk could have changed, thus a reload is needed.
141+
// For CSR renewal we assume the same CA on disk is mounted for usage with KCM's
142+
// '--cluster-signing-cert-file' flag.
143+
caCert, _, err := certsphase.LoadCertificateAuthority(rw.certificateDir, rw.baseName)
144+
if err != nil {
145+
return nil, err
146+
}
147+
rw.caCert = caCert
148+
133149
// get current context
134150
if _, ok := kubeConfig.Contexts[kubeConfig.CurrentContext]; !ok {
135151
return nil, errors.Errorf("invalid kubeConfig file %s: missing context %s", rw.kubeConfigFilePath, kubeConfig.CurrentContext)
@@ -143,7 +159,7 @@ func (rw *kubeConfigReadWriter) Read() (*x509.Certificate, error) {
143159

144160
cluster := kubeConfig.Clusters[clusterName]
145161
if len(cluster.CertificateAuthorityData) == 0 {
146-
return nil, errors.Errorf("kubeConfig file %s does not have and embedded server certificate", rw.kubeConfigFilePath)
162+
return nil, errors.Errorf("kubeConfig file %s does not have an embedded server certificate", rw.kubeConfigFilePath)
147163
}
148164

149165
// get auth info for current context and ensure a client certificate is embedded in it
@@ -154,7 +170,7 @@ func (rw *kubeConfigReadWriter) Read() (*x509.Certificate, error) {
154170

155171
authInfo := kubeConfig.AuthInfos[authInfoName]
156172
if len(authInfo.ClientCertificateData) == 0 {
157-
return nil, errors.Errorf("kubeConfig file %s does not have and embedded client certificate", rw.kubeConfigFilePath)
173+
return nil, errors.Errorf("kubeConfig file %s does not have an embedded client certificate", rw.kubeConfigFilePath)
158174
}
159175

160176
// parse the client certificate, retrive the cert config and then renew it
@@ -174,7 +190,7 @@ func (rw *kubeConfigReadWriter) Read() (*x509.Certificate, error) {
174190
func (rw *kubeConfigReadWriter) Write(newCert *x509.Certificate, newKey crypto.Signer) error {
175191
// check if Read was called before Write
176192
if rw.kubeConfig == nil {
177-
return errors.Errorf("failed to Write kubeConfig file with renewd certs. It is necessary to call Read before Write")
193+
return errors.Errorf("failed to Write kubeConfig file with renewed certs. It is necessary to call Read before Write")
178194
}
179195

180196
// encodes the new key
@@ -183,6 +199,12 @@ func (rw *kubeConfigReadWriter) Write(newCert *x509.Certificate, newKey crypto.S
183199
return errors.Wrapf(err, "failed to marshal private key to PEM")
184200
}
185201

202+
// Update the embedded CA in the kubeconfig file.
203+
// This assumes that the user has kept the current context to the desired one.
204+
clusterName := rw.kubeConfig.Contexts[rw.kubeConfig.CurrentContext].Cluster
205+
cluster := rw.kubeConfig.Clusters[clusterName]
206+
cluster.CertificateAuthorityData = pkiutil.EncodeCertPEM(rw.caCert)
207+
186208
// get auth info for current context and ensure a client certificate is embedded in it
187209
authInfoName := rw.kubeConfig.Contexts[rw.kubeConfig.CurrentContext].AuthInfo
188210

cmd/kubeadm/app/phases/certs/renewal/readwriter_test.go

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"k8s.io/client-go/tools/clientcmd"
2828
certutil "k8s.io/client-go/util/cert"
2929
"k8s.io/client-go/util/keyutil"
30+
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
3031
kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig"
3132
pkiutil "k8s.io/kubernetes/cmd/kubeadm/app/util/pkiutil"
3233
testutil "k8s.io/kubernetes/cmd/kubeadm/test"
@@ -79,15 +80,27 @@ func TestPKICertificateReadWriter(t *testing.T) {
7980
}
8081

8182
func TestKubeconfigReadWriter(t *testing.T) {
82-
// creates a tmp folder
83-
dir := testutil.SetupTempDir(t)
84-
defer os.RemoveAll(dir)
83+
// creates tmp folders
84+
dirKubernetes := testutil.SetupTempDir(t)
85+
defer os.RemoveAll(dirKubernetes)
86+
dirPKI := testutil.SetupTempDir(t)
87+
defer os.RemoveAll(dirPKI)
88+
89+
// write the CA cert and key to the temporary PKI dir
90+
caName := kubeadmconstants.CACertAndKeyBaseName
91+
if err := pkiutil.WriteCertAndKey(
92+
dirPKI,
93+
caName,
94+
testCACert,
95+
testCAKey); err != nil {
96+
t.Fatalf("couldn't write out certificate %s to %s", caName, dirPKI)
97+
}
8598

8699
// creates a certificate and then embeds it into a kubeconfig file
87-
cert := writeTestKubeconfig(t, dir, "test", testCACert, testCAKey)
100+
cert := writeTestKubeconfig(t, dirKubernetes, "test", testCACert, testCAKey)
88101

89102
// Creates a KubeconfigReadWriter
90-
kubeconfigReadWriter := newKubeconfigReadWriter(dir, "test")
103+
kubeconfigReadWriter := newKubeconfigReadWriter(dirKubernetes, "test", dirPKI, caName)
91104

92105
// Reads the certificate embedded in a kubeconfig
93106
readCert, err := kubeconfigReadWriter.Read()

0 commit comments

Comments
 (0)