diff --git a/cmd/kubeadm/app/phases/kubeconfig/kubeconfig_test.go b/cmd/kubeadm/app/phases/kubeconfig/kubeconfig_test.go index fc2441fc0e0b..2aae6934296a 100644 --- a/cmd/kubeadm/app/phases/kubeconfig/kubeconfig_test.go +++ b/cmd/kubeadm/app/phases/kubeconfig/kubeconfig_test.go @@ -697,6 +697,120 @@ func TestValidateKubeconfigsForExternalCA(t *testing.T) { } } +func TestValidateKubeconfigsForExternalCAWithIntermediateCA(t *testing.T) { + tmpDir := testutil.SetupTempDir(t) + defer os.RemoveAll(tmpDir) + pkiDir := filepath.Join(tmpDir, "pki") + + initConfig := &kubeadmapi.InitConfiguration{ + ClusterConfiguration: kubeadmapi.ClusterConfiguration{ + CertificatesDir: pkiDir, + }, + LocalAPIEndpoint: kubeadmapi.APIEndpoint{ + BindPort: 1234, + AdvertiseAddress: "1.2.3.4", + }, + } + + // creates CA, write to pkiDir and remove ca.key to get into external CA condition + caCert, caKey := certstestutil.SetupCertificateAuthority(t) + intermediateCaCert, _ := certstestutil.SetupIntermediateCertificateAuthority(t, caCert, caKey) + + var certs []*x509.Certificate + certs = append(certs, caCert) + certs = append(certs, intermediateCaCert) + + if err := pkiutil.WriteCertBundle(pkiDir, kubeadmconstants.CACertAndKeyBaseName, certs); err != nil { + t.Fatalf("failure while saving CA certificate containing a intermediary CA: %v", err) + } + + // create a valid config + config := setupdKubeConfigWithClientAuth(t, caCert, caKey, "https://1.2.3.4:1234", "test-cluster", "myOrg1") + + // create a config with another CA + anotherCaCert, anotherCaKey := certstestutil.SetupCertificateAuthority(t) + configWithAnotherClusterCa := setupdKubeConfigWithClientAuth(t, anotherCaCert, anotherCaKey, "https://1.2.3.4:1234", "test-cluster", "myOrg1") + + // create a config with another server URL + configWithAnotherServerURL := setupdKubeConfigWithClientAuth(t, caCert, caKey, "https://4.3.2.1:4321", "test-cluster", "myOrg1") + + tests := map[string]struct { + filesToWrite map[string]*clientcmdapi.Config + initConfig *kubeadmapi.InitConfiguration + expectedError bool + }{ + "files don't exist": { + initConfig: initConfig, + expectedError: true, + }, + "some files don't exist": { + filesToWrite: map[string]*clientcmdapi.Config{ + kubeadmconstants.AdminKubeConfigFileName: config, + kubeadmconstants.SuperAdminKubeConfigFileName: config, + kubeadmconstants.KubeletKubeConfigFileName: config, + }, + initConfig: initConfig, + expectedError: true, + }, + "some files have invalid CA": { + filesToWrite: map[string]*clientcmdapi.Config{ + kubeadmconstants.AdminKubeConfigFileName: config, + kubeadmconstants.SuperAdminKubeConfigFileName: config, + kubeadmconstants.KubeletKubeConfigFileName: config, + kubeadmconstants.ControllerManagerKubeConfigFileName: configWithAnotherClusterCa, + kubeadmconstants.SchedulerKubeConfigFileName: config, + }, + initConfig: initConfig, + expectedError: true, + }, + "some files have a different Server URL": { + filesToWrite: map[string]*clientcmdapi.Config{ + kubeadmconstants.AdminKubeConfigFileName: config, + kubeadmconstants.SuperAdminKubeConfigFileName: config, + kubeadmconstants.KubeletKubeConfigFileName: config, + kubeadmconstants.ControllerManagerKubeConfigFileName: config, + kubeadmconstants.SchedulerKubeConfigFileName: configWithAnotherServerURL, + }, + initConfig: initConfig, + }, + "all files are valid": { + filesToWrite: map[string]*clientcmdapi.Config{ + kubeadmconstants.AdminKubeConfigFileName: config, + kubeadmconstants.SuperAdminKubeConfigFileName: config, + kubeadmconstants.KubeletKubeConfigFileName: config, + kubeadmconstants.ControllerManagerKubeConfigFileName: config, + kubeadmconstants.SchedulerKubeConfigFileName: config, + }, + initConfig: initConfig, + expectedError: false, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + tmpdir := testutil.SetupTempDir(t) + defer os.RemoveAll(tmpdir) + + for name, config := range test.filesToWrite { + if err := createKubeConfigFileIfNotExists(tmpdir, name, config); err != nil { + t.Errorf("createKubeConfigFileIfNotExists failed: %v", err) + } + } + + err := ValidateKubeconfigsForExternalCA(tmpdir, test.initConfig) + if (err != nil) != test.expectedError { + t.Fatalf(dedent.Dedent( + "ValidateKubeconfigsForExternalCA failed\n%s\nexpected error: %t\n\tgot: %t\nerror: %v"), + name, + test.expectedError, + (err != nil), + err, + ) + } + }) + } +} + // setupdKubeConfigWithClientAuth is a test utility function that wraps buildKubeConfigFromSpec for building a KubeConfig object With ClientAuth func setupdKubeConfigWithClientAuth(t *testing.T, caCert *x509.Certificate, caKey crypto.Signer, APIServer, clientName, clustername string, organizations ...string) *clientcmdapi.Config { spec := &kubeConfigSpec{ diff --git a/cmd/kubeadm/app/util/certs/util.go b/cmd/kubeadm/app/util/certs/util.go index 8d6fff4ffc74..40e89f083ade 100644 --- a/cmd/kubeadm/app/util/certs/util.go +++ b/cmd/kubeadm/app/util/certs/util.go @@ -20,13 +20,12 @@ import ( "crypto" "crypto/rsa" "crypto/x509" + certutil "k8s.io/client-go/util/cert" + "k8s.io/client-go/util/keyutil" "net" "path/filepath" "testing" - certutil "k8s.io/client-go/util/cert" - "k8s.io/client-go/util/keyutil" - "k8s.io/kubernetes/cmd/kubeadm/app/util/pkiutil" ) @@ -34,8 +33,7 @@ import ( // CertificateAuthority cert/key pair func SetupCertificateAuthority(t *testing.T) (*x509.Certificate, crypto.Signer) { caCert, caKey, err := pkiutil.NewCertificateAuthority(&pkiutil.CertConfig{ - Config: certutil.Config{CommonName: "kubernetes"}, - }) + Config: certutil.Config{CommonName: "kubernetes"}}) if err != nil { t.Fatalf("failure while generating CA certificate and key: %v", err) } @@ -43,6 +41,18 @@ func SetupCertificateAuthority(t *testing.T) (*x509.Certificate, crypto.Signer) return caCert, caKey } +// SetupIntermediateCertificateAuthority is a utility function for kubeadm testing that creates a +// CertificateAuthority cert/key pair +func SetupIntermediateCertificateAuthority(t *testing.T, caCert *x509.Certificate, caKey crypto.Signer) (*x509.Certificate, crypto.Signer) { + intermediateCaCert, intermediateCaKey, err := pkiutil.NewIntermediateCertificateAuthority(caCert, caKey, &pkiutil.CertConfig{ + Config: certutil.Config{CommonName: "kubernetes Intermediate CA"}}) + if err != nil { + t.Fatalf("failure while generating intermediate CA certificate and key: %v", err) + } + + return intermediateCaCert, intermediateCaKey +} + // AssertCertificateIsSignedByCa is a utility function for kubeadm testing that asserts if a given certificate is signed // by the expected CA func AssertCertificateIsSignedByCa(t *testing.T, cert *x509.Certificate, signingCa *x509.Certificate) {