From 453ac809c12293613333aa0778a210666e0bef94 Mon Sep 17 00:00:00 2001 From: "Lubomir I. Ivanov" Date: Mon, 30 Dec 2019 00:51:37 +0200 Subject: [PATCH] kubeadm: tolerate whitespace when validating user CA PEMs The function validateKubeConfig() can end up comparing a user generated kubeconfig to a kubeconfig generated by kubeadm. If a user kubeconfig has a CA that is base64 encoded with whitespace, if said kubeconfig is loaded using clientcmd.LoadFromFile() the CertificateAuthorityData bytes will be decoded from base64 and placed in the v1.Config raw. On the other hand a kubeconfig generated by kubeadm will have the ca.crt parsed to a Certificate object with whitespace ignored in the PEM input. Make sure that validateKubeConfig() tolerates whitespace differences when comparing CertificateAuthorityData. --- cmd/kubeadm/app/phases/kubeconfig/kubeconfig.go | 8 +++++++- .../app/phases/kubeconfig/kubeconfig_test.go | 13 +++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/cmd/kubeadm/app/phases/kubeconfig/kubeconfig.go b/cmd/kubeadm/app/phases/kubeconfig/kubeconfig.go index b22e06bcfa0..7ee3f36b348 100644 --- a/cmd/kubeadm/app/phases/kubeconfig/kubeconfig.go +++ b/cmd/kubeadm/app/phases/kubeconfig/kubeconfig.go @@ -235,8 +235,14 @@ func validateKubeConfig(outDir, filename string, config *clientcmdapi.Config) er return errors.Errorf("failed to find the given CurrentContext Cluster in Clusters of the kubeconfig file %s", kubeConfigFilePath) } + // Make sure the compared CAs are whitespace-trimmed. The function clientcmd.LoadFromFile() just decodes + // the base64 CA and places it raw in the v1.Config object. In case the user has extra whitespace + // in the CA they used to create a kubeconfig this comparison to a generated v1.Config will otherwise fail. + caCurrent := bytes.TrimSpace(currentConfig.Clusters[currentCluster].CertificateAuthorityData) + caExpected := bytes.TrimSpace(config.Clusters[expectedCluster].CertificateAuthorityData) + // If the current CA cert on disk doesn't match the expected CA cert, error out because we have a file, but it's stale - if !bytes.Equal(currentConfig.Clusters[currentCluster].CertificateAuthorityData, config.Clusters[expectedCluster].CertificateAuthorityData) { + if !bytes.Equal(caCurrent, caExpected) { return errors.Errorf("a kubeconfig file %q exists already but has got the wrong CA cert", kubeConfigFilePath) } // If the current API Server location on disk doesn't match the expected API server, error out because we have a file, but it's stale diff --git a/cmd/kubeadm/app/phases/kubeconfig/kubeconfig_test.go b/cmd/kubeadm/app/phases/kubeconfig/kubeconfig_test.go index 48060b35fa7..40ad4a79794 100644 --- a/cmd/kubeadm/app/phases/kubeconfig/kubeconfig_test.go +++ b/cmd/kubeadm/app/phases/kubeconfig/kubeconfig_test.go @@ -469,6 +469,14 @@ func TestValidateKubeConfig(t *testing.T) { configWithAnotherClusterCa := setupdKubeConfigWithClientAuth(t, anotherCaCert, anotherCaKey, "https://1.2.3.4:1234", "test-cluster", "myOrg1") configWithAnotherServerURL := setupdKubeConfigWithClientAuth(t, caCert, caKey, "https://4.3.2.1:4321", "test-cluster", "myOrg1") + // create a valid config but with whitespace around the CA PEM. + // validateKubeConfig() should tollerate that. + configWhitespace := config.DeepCopy() + configWhitespaceCtx := configWhitespace.Contexts[configWhitespace.CurrentContext] + configWhitespaceCA := string(configWhitespace.Clusters[configWhitespaceCtx.Cluster].CertificateAuthorityData) + configWhitespaceCA = "\n" + configWhitespaceCA + "\n" + configWhitespace.Clusters[configWhitespaceCtx.Cluster].CertificateAuthorityData = []byte(configWhitespaceCA) + tests := map[string]struct { existingKubeConfig *clientcmdapi.Config kubeConfig *clientcmdapi.Config @@ -493,6 +501,11 @@ func TestValidateKubeConfig(t *testing.T) { kubeConfig: config, expectedError: false, }, + "kubeconfig exist and is valid even if its CA contains whitespace": { + existingKubeConfig: configWhitespace, + kubeConfig: config, + expectedError: false, + }, } for name, test := range tests {