diff --git a/cmd/kubeadm/app/phases/kubeconfig/kubeconfig.go b/cmd/kubeadm/app/phases/kubeconfig/kubeconfig.go index 23a54501902..c1058849868 100644 --- a/cmd/kubeadm/app/phases/kubeconfig/kubeconfig.go +++ b/cmd/kubeadm/app/phases/kubeconfig/kubeconfig.go @@ -22,6 +22,7 @@ import ( "crypto/x509" "fmt" "io" + "io/ioutil" "os" "path/filepath" "time" @@ -238,6 +239,18 @@ func validateKubeConfig(outDir, filename string, config *clientcmdapi.Config) er // 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) + if len(caCurrent) == 0 { + // fallback to load CA cert data from external CA file + clusterCAFilePath := currentConfig.Clusters[currentCluster].CertificateAuthority + if len(clusterCAFilePath) > 0 { + clusterCABytes, err := ioutil.ReadFile(clusterCAFilePath) + if err != nil { + klog.Warningf("failed to load CA cert from %q for kubeconfig %q, %v", clusterCAFilePath, kubeConfigFilePath, err) + } else { + caCurrent = bytes.TrimSpace(clusterCABytes) + } + } + } 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 diff --git a/cmd/kubeadm/app/phases/kubeconfig/kubeconfig_test.go b/cmd/kubeadm/app/phases/kubeconfig/kubeconfig_test.go index 17c2a24cdb6..459f8bf3b5c 100644 --- a/cmd/kubeadm/app/phases/kubeconfig/kubeconfig_test.go +++ b/cmd/kubeadm/app/phases/kubeconfig/kubeconfig_test.go @@ -22,6 +22,7 @@ import ( "crypto/x509" "fmt" "io" + "io/ioutil" "os" "path/filepath" "reflect" @@ -483,6 +484,25 @@ 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") + configWithSameClusterCaByExternalFile := config.DeepCopy() + currentCtx, exists := configWithSameClusterCaByExternalFile.Contexts[configWithSameClusterCaByExternalFile.CurrentContext] + if !exists { + t.Fatal("failed to find CurrentContext in Contexts of the kubeconfig") + } + if configWithSameClusterCaByExternalFile.Clusters[currentCtx.Cluster] == nil { + t.Fatal("failed to find the given CurrentContext Cluster in Clusters of the kubeconfig") + } + tmpfile, err := ioutil.TempFile("", "external-ca.crt") + if err != nil { + t.Fatal(err) + } + defer os.Remove(tmpfile.Name()) + if _, err := tmpfile.Write(pkiutil.EncodeCertPEM(caCert)); err != nil { + t.Fatal(err) + } + configWithSameClusterCaByExternalFile.Clusters[currentCtx.Cluster].CertificateAuthorityData = nil + configWithSameClusterCaByExternalFile.Clusters[currentCtx.Cluster].CertificateAuthority = tmpfile.Name() + // create a valid config but with whitespace around the CA PEM. // validateKubeConfig() should tolerate that. configWhitespace := config.DeepCopy() @@ -519,6 +539,11 @@ func TestValidateKubeConfig(t *testing.T) { kubeConfig: config, expectedError: false, }, + "kubeconfig exist and is valid even if its CA is provided as an external file": { + existingKubeConfig: configWithSameClusterCaByExternalFile, + kubeConfig: config, + expectedError: false, + }, } for name, test := range tests {