Skip to content

Commit 261eba4

Browse files
authored
fix: regenerate mismatched apiserver CA artifacts (#95)
1 parent 2876e05 commit 261eba4

1 file changed

Lines changed: 70 additions & 6 deletions

File tree

server/certs.go

Lines changed: 70 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package server
22

33
import (
4+
"bytes"
45
"crypto/ecdsa"
56
"crypto/elliptic"
67
"crypto/rand"
@@ -35,17 +36,29 @@ func ensureCerts(dir, ip, advertiseIP string) error {
3536
admCrtFile := filepath.Join(dir, "admin.crt")
3637
kubeletKeyFile := filepath.Join(dir, "apiserver-kubelet-client.key")
3738
kubeletCrtFile := filepath.Join(dir, "apiserver-kubelet-client.crt")
39+
dependentFiles := []string{
40+
caKeyFile, caCertFile, srvKeyFile, srvCrtFile,
41+
admKeyFile, admCrtFile, kubeletKeyFile, kubeletCrtFile,
42+
filepath.Join(dir, "webhook.crt"),
43+
filepath.Join(dir, "webhook.key"),
44+
filepath.Join(dir, "authz-webhook.kubeconfig"),
45+
filepath.Join(dir, "controller-manager.kubeconfig"),
46+
filepath.Join(dir, "scheduler.kubeconfig"),
47+
}
3848

3949
// kube-apiserver fails ECDSA signature verification on self-signed ECDSA CAs.
4050
// If the existing CA key is ECDSA, remove all derived certs so they are
4151
// regenerated as RSA below.
4252
if fileExists(caKeyFile) && caKeyIsECDSA(caKeyFile) {
43-
for _, f := range []string{
44-
caKeyFile, caCertFile, srvKeyFile, srvCrtFile,
45-
admKeyFile, admCrtFile, kubeletKeyFile, kubeletCrtFile,
46-
} {
47-
_ = os.Remove(f)
48-
}
53+
removeAllIfExists(dependentFiles...)
54+
}
55+
56+
// If the CA cert and key drift out of sync, every newly signed client/server
57+
// cert will be unverifiable even though all files still parse successfully.
58+
// In that case remove the CA and all dependent artifacts so startup can
59+
// regenerate a consistent chain.
60+
if !existingCACertKeyUsable(caCertFile, caKeyFile) {
61+
removeAllIfExists(dependentFiles...)
4962
}
5063

5164
var caKey *rsa.PrivateKey
@@ -417,6 +430,57 @@ func removeIfExists(path string) error {
417430
return nil
418431
}
419432

433+
func removeAllIfExists(paths ...string) {
434+
for _, path := range paths {
435+
_ = os.Remove(path)
436+
}
437+
}
438+
439+
func existingCACertKeyUsable(certFile, keyFile string) bool {
440+
if !fileExists(certFile) && !fileExists(keyFile) {
441+
return true
442+
}
443+
if !fileExists(certFile) || !fileExists(keyFile) {
444+
return false
445+
}
446+
447+
keyPEM, err := os.ReadFile(keyFile)
448+
if err != nil {
449+
return false
450+
}
451+
keyBlock, _ := pem.Decode(keyPEM)
452+
if keyBlock == nil {
453+
return false
454+
}
455+
key, err := x509.ParsePKCS1PrivateKey(keyBlock.Bytes)
456+
if err != nil {
457+
return false
458+
}
459+
460+
certPEM, err := os.ReadFile(certFile)
461+
if err != nil {
462+
return false
463+
}
464+
certBlock, _ := pem.Decode(certPEM)
465+
if certBlock == nil {
466+
return false
467+
}
468+
cert, err := x509.ParseCertificate(certBlock.Bytes)
469+
if err != nil || !cert.IsCA {
470+
return false
471+
}
472+
473+
certPubDER, err := x509.MarshalPKIXPublicKey(cert.PublicKey)
474+
if err != nil {
475+
return false
476+
}
477+
keyPubDER, err := x509.MarshalPKIXPublicKey(&key.PublicKey)
478+
if err != nil {
479+
return false
480+
}
481+
return bytes.Equal(certPubDER, keyPubDER)
482+
}
483+
420484
func uniqueIPs(addrs ...string) []net.IP {
421485
seen := map[string]bool{}
422486
var result []net.IP

0 commit comments

Comments
 (0)