mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-27 13:37:30 +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"],
|
embed = [":go_default_library"],
|
||||||
deps = [
|
deps = [
|
||||||
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
|
"//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/certs:go_default_library",
|
||||||
"//cmd/kubeadm/app/util/kubeconfig:go_default_library",
|
"//cmd/kubeadm/app/util/kubeconfig:go_default_library",
|
||||||
"//cmd/kubeadm/app/util/pkiutil: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
|
// create a CertificateRenewHandler for each kubeConfig file
|
||||||
for _, kubeConfig := range kubeConfigs {
|
for _, kubeConfig := range kubeConfigs {
|
||||||
// create a ReadWriter for certificates embedded in kubeConfig files
|
// 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.
|
// adds the certificateRenewHandler.
|
||||||
// Certificates embedded kubeConfig files in are indexed by fileName, that is a well know constant defined
|
// 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"
|
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||||
certutil "k8s.io/client-go/util/cert"
|
certutil "k8s.io/client-go/util/cert"
|
||||||
"k8s.io/client-go/util/keyutil"
|
"k8s.io/client-go/util/keyutil"
|
||||||
|
certsphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/certs"
|
||||||
pkiutil "k8s.io/kubernetes/cmd/kubeadm/app/util/pkiutil"
|
pkiutil "k8s.io/kubernetes/cmd/kubeadm/app/util/pkiutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -104,14 +105,19 @@ type kubeConfigReadWriter struct {
|
|||||||
kubeConfigFileName string
|
kubeConfigFileName string
|
||||||
kubeConfigFilePath string
|
kubeConfigFilePath string
|
||||||
kubeConfig *clientcmdapi.Config
|
kubeConfig *clientcmdapi.Config
|
||||||
|
baseName string
|
||||||
|
certificateDir string
|
||||||
|
caCert *x509.Certificate
|
||||||
}
|
}
|
||||||
|
|
||||||
// newKubeconfigReadWriter return a new kubeConfigReadWriter
|
// newKubeconfigReadWriter return a new kubeConfigReadWriter
|
||||||
func newKubeconfigReadWriter(kubernetesDir string, kubeConfigFileName string) *kubeConfigReadWriter {
|
func newKubeconfigReadWriter(kubernetesDir string, kubeConfigFileName string, certificateDir, baseName string) *kubeConfigReadWriter {
|
||||||
return &kubeConfigReadWriter{
|
return &kubeConfigReadWriter{
|
||||||
kubernetesDir: kubernetesDir,
|
kubernetesDir: kubernetesDir,
|
||||||
kubeConfigFileName: kubeConfigFileName,
|
kubeConfigFileName: kubeConfigFileName,
|
||||||
kubeConfigFilePath: filepath.Join(kubernetesDir, 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)
|
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
|
// get current context
|
||||||
if _, ok := kubeConfig.Contexts[kubeConfig.CurrentContext]; !ok {
|
if _, ok := kubeConfig.Contexts[kubeConfig.CurrentContext]; !ok {
|
||||||
return nil, errors.Errorf("invalid kubeConfig file %s: missing context %s", rw.kubeConfigFilePath, kubeConfig.CurrentContext)
|
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]
|
cluster := kubeConfig.Clusters[clusterName]
|
||||||
if len(cluster.CertificateAuthorityData) == 0 {
|
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
|
// 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]
|
authInfo := kubeConfig.AuthInfos[authInfoName]
|
||||||
if len(authInfo.ClientCertificateData) == 0 {
|
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
|
// 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 {
|
func (rw *kubeConfigReadWriter) Write(newCert *x509.Certificate, newKey crypto.Signer) error {
|
||||||
// check if Read was called before Write
|
// check if Read was called before Write
|
||||||
if rw.kubeConfig == nil {
|
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
|
// 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")
|
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
|
// get auth info for current context and ensure a client certificate is embedded in it
|
||||||
authInfoName := rw.kubeConfig.Contexts[rw.kubeConfig.CurrentContext].AuthInfo
|
authInfoName := rw.kubeConfig.Contexts[rw.kubeConfig.CurrentContext].AuthInfo
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@ import (
|
|||||||
"k8s.io/client-go/tools/clientcmd"
|
"k8s.io/client-go/tools/clientcmd"
|
||||||
certutil "k8s.io/client-go/util/cert"
|
certutil "k8s.io/client-go/util/cert"
|
||||||
"k8s.io/client-go/util/keyutil"
|
"k8s.io/client-go/util/keyutil"
|
||||||
|
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||||
kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig"
|
kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig"
|
||||||
pkiutil "k8s.io/kubernetes/cmd/kubeadm/app/util/pkiutil"
|
pkiutil "k8s.io/kubernetes/cmd/kubeadm/app/util/pkiutil"
|
||||||
testutil "k8s.io/kubernetes/cmd/kubeadm/test"
|
testutil "k8s.io/kubernetes/cmd/kubeadm/test"
|
||||||
@ -79,15 +80,27 @@ func TestPKICertificateReadWriter(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestKubeconfigReadWriter(t *testing.T) {
|
func TestKubeconfigReadWriter(t *testing.T) {
|
||||||
// creates a tmp folder
|
// creates tmp folders
|
||||||
dir := testutil.SetupTempDir(t)
|
dirKubernetes := testutil.SetupTempDir(t)
|
||||||
defer os.RemoveAll(dir)
|
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
|
// 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
|
// Creates a KubeconfigReadWriter
|
||||||
kubeconfigReadWriter := newKubeconfigReadWriter(dir, "test")
|
kubeconfigReadWriter := newKubeconfigReadWriter(dirKubernetes, "test", dirPKI, caName)
|
||||||
|
|
||||||
// Reads the certificate embedded in a kubeconfig
|
// Reads the certificate embedded in a kubeconfig
|
||||||
readCert, err := kubeconfigReadWriter.Read()
|
readCert, err := kubeconfigReadWriter.Read()
|
||||||
|
Loading…
Reference in New Issue
Block a user