mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 03:41:45 +00:00
Merge pull request #88052 from neolit123/1.18-renew-use-ca-in-kubeconfig
kubeadm: update embedded CA in kubeconfig files on renewal
This commit is contained in:
commit
b893aa707e
@ -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