mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 19:56:01 +00:00
kubeadm: update embedded CA in kubeconfig files on renewal
While kubeadm does not support CA rotation, the users might still attempt to perform this manually. For kubeconfig files, updating to a new CA is not reflected and users need to embed new CA PEM manually. On kubeconfig cert renewal, always keep the embedded CA in sync with the one on disk. Includes a couple of typo fixes.
This commit is contained in:
parent
f9250c4f95
commit
0ba5891519
@ -41,6 +41,7 @@ go_test(
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
|
||||
"//cmd/kubeadm/app/constants:go_default_library",
|
||||
"//cmd/kubeadm/app/util/certs:go_default_library",
|
||||
"//cmd/kubeadm/app/util/kubeconfig:go_default_library",
|
||||
"//cmd/kubeadm/app/util/pkiutil:go_default_library",
|
||||
|
@ -154,7 +154,8 @@ func NewManager(cfg *kubeadmapi.ClusterConfiguration, kubernetesDir string) (*Ma
|
||||
// create a CertificateRenewHandler for each kubeConfig file
|
||||
for _, kubeConfig := range kubeConfigs {
|
||||
// create a ReadWriter for certificates embedded in kubeConfig files
|
||||
kubeConfigReadWriter := newKubeconfigReadWriter(kubernetesDir, kubeConfig.fileName)
|
||||
kubeConfigReadWriter := newKubeconfigReadWriter(kubernetesDir, kubeConfig.fileName,
|
||||
rm.cfg.CertificatesDir, kubeadmconstants.CACertAndKeyBaseName)
|
||||
|
||||
// adds the certificateRenewHandler.
|
||||
// Certificates embedded kubeConfig files in are indexed by fileName, that is a well know constant defined
|
||||
|
@ -28,6 +28,7 @@ import (
|
||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
certutil "k8s.io/client-go/util/cert"
|
||||
"k8s.io/client-go/util/keyutil"
|
||||
certsphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/certs"
|
||||
pkiutil "k8s.io/kubernetes/cmd/kubeadm/app/util/pkiutil"
|
||||
)
|
||||
|
||||
@ -104,14 +105,19 @@ type kubeConfigReadWriter struct {
|
||||
kubeConfigFileName string
|
||||
kubeConfigFilePath string
|
||||
kubeConfig *clientcmdapi.Config
|
||||
baseName string
|
||||
certificateDir string
|
||||
caCert *x509.Certificate
|
||||
}
|
||||
|
||||
// newKubeconfigReadWriter return a new kubeConfigReadWriter
|
||||
func newKubeconfigReadWriter(kubernetesDir string, kubeConfigFileName string) *kubeConfigReadWriter {
|
||||
func newKubeconfigReadWriter(kubernetesDir string, kubeConfigFileName string, certificateDir, baseName string) *kubeConfigReadWriter {
|
||||
return &kubeConfigReadWriter{
|
||||
kubernetesDir: kubernetesDir,
|
||||
kubeConfigFileName: kubeConfigFileName,
|
||||
kubeConfigFilePath: filepath.Join(kubernetesDir, kubeConfigFileName),
|
||||
certificateDir: certificateDir,
|
||||
baseName: baseName,
|
||||
}
|
||||
}
|
||||
|
||||
@ -130,6 +136,16 @@ func (rw *kubeConfigReadWriter) Read() (*x509.Certificate, error) {
|
||||
return nil, errors.Wrapf(err, "failed to load kubeConfig file %s", rw.kubeConfigFilePath)
|
||||
}
|
||||
|
||||
// The CA cert is required for updating kubeconfig files.
|
||||
// For local CA renewal, the local CA on disk could have changed, thus a reload is needed.
|
||||
// For CSR renewal we assume the same CA on disk is mounted for usage with KCM's
|
||||
// '--cluster-signing-cert-file' flag.
|
||||
caCert, _, err := certsphase.LoadCertificateAuthority(rw.certificateDir, rw.baseName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rw.caCert = caCert
|
||||
|
||||
// get current context
|
||||
if _, ok := kubeConfig.Contexts[kubeConfig.CurrentContext]; !ok {
|
||||
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) {
|
||||
|
||||
cluster := kubeConfig.Clusters[clusterName]
|
||||
if len(cluster.CertificateAuthorityData) == 0 {
|
||||
return nil, errors.Errorf("kubeConfig file %s does not have and embedded server certificate", rw.kubeConfigFilePath)
|
||||
return nil, errors.Errorf("kubeConfig file %s does not have an embedded server certificate", rw.kubeConfigFilePath)
|
||||
}
|
||||
|
||||
// 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) {
|
||||
|
||||
authInfo := kubeConfig.AuthInfos[authInfoName]
|
||||
if len(authInfo.ClientCertificateData) == 0 {
|
||||
return nil, errors.Errorf("kubeConfig file %s does not have and embedded client certificate", rw.kubeConfigFilePath)
|
||||
return nil, errors.Errorf("kubeConfig file %s does not have an embedded client certificate", rw.kubeConfigFilePath)
|
||||
}
|
||||
|
||||
// parse the client certificate, retrive the cert config and then renew it
|
||||
@ -174,7 +190,7 @@ func (rw *kubeConfigReadWriter) Read() (*x509.Certificate, error) {
|
||||
func (rw *kubeConfigReadWriter) Write(newCert *x509.Certificate, newKey crypto.Signer) error {
|
||||
// check if Read was called before Write
|
||||
if rw.kubeConfig == nil {
|
||||
return errors.Errorf("failed to Write kubeConfig file with renewd certs. It is necessary to call Read before Write")
|
||||
return errors.Errorf("failed to Write kubeConfig file with renewed certs. It is necessary to call Read before Write")
|
||||
}
|
||||
|
||||
// encodes the new key
|
||||
@ -183,6 +199,12 @@ func (rw *kubeConfigReadWriter) Write(newCert *x509.Certificate, newKey crypto.S
|
||||
return errors.Wrapf(err, "failed to marshal private key to PEM")
|
||||
}
|
||||
|
||||
// Update the embedded CA in the kubeconfig file.
|
||||
// This assumes that the user has kept the current context to the desired one.
|
||||
clusterName := rw.kubeConfig.Contexts[rw.kubeConfig.CurrentContext].Cluster
|
||||
cluster := rw.kubeConfig.Clusters[clusterName]
|
||||
cluster.CertificateAuthorityData = pkiutil.EncodeCertPEM(rw.caCert)
|
||||
|
||||
// get auth info for current context and ensure a client certificate is embedded in it
|
||||
authInfoName := rw.kubeConfig.Contexts[rw.kubeConfig.CurrentContext].AuthInfo
|
||||
|
||||
|
@ -27,6 +27,7 @@ import (
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
certutil "k8s.io/client-go/util/cert"
|
||||
"k8s.io/client-go/util/keyutil"
|
||||
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||
kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig"
|
||||
pkiutil "k8s.io/kubernetes/cmd/kubeadm/app/util/pkiutil"
|
||||
testutil "k8s.io/kubernetes/cmd/kubeadm/test"
|
||||
@ -79,15 +80,27 @@ func TestPKICertificateReadWriter(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestKubeconfigReadWriter(t *testing.T) {
|
||||
// creates a tmp folder
|
||||
dir := testutil.SetupTempDir(t)
|
||||
defer os.RemoveAll(dir)
|
||||
// creates tmp folders
|
||||
dirKubernetes := testutil.SetupTempDir(t)
|
||||
defer os.RemoveAll(dirKubernetes)
|
||||
dirPKI := testutil.SetupTempDir(t)
|
||||
defer os.RemoveAll(dirPKI)
|
||||
|
||||
// write the CA cert and key to the temporary PKI dir
|
||||
caName := kubeadmconstants.CACertAndKeyBaseName
|
||||
if err := pkiutil.WriteCertAndKey(
|
||||
dirPKI,
|
||||
caName,
|
||||
testCACert,
|
||||
testCAKey); err != nil {
|
||||
t.Fatalf("couldn't write out certificate %s to %s", caName, dirPKI)
|
||||
}
|
||||
|
||||
// creates a certificate and then embeds it into a kubeconfig file
|
||||
cert := writeTestKubeconfig(t, dir, "test", testCACert, testCAKey)
|
||||
cert := writeTestKubeconfig(t, dirKubernetes, "test", testCACert, testCAKey)
|
||||
|
||||
// Creates a KubeconfigReadWriter
|
||||
kubeconfigReadWriter := newKubeconfigReadWriter(dir, "test")
|
||||
kubeconfigReadWriter := newKubeconfigReadWriter(dirKubernetes, "test", dirPKI, caName)
|
||||
|
||||
// Reads the certificate embedded in a kubeconfig
|
||||
readCert, err := kubeconfigReadWriter.Read()
|
||||
|
Loading…
Reference in New Issue
Block a user