diff --git a/cmd/kubeadm/app/cmd/phases/BUILD b/cmd/kubeadm/app/cmd/phases/BUILD index 932f6b732f..08d6431656 100644 --- a/cmd/kubeadm/app/cmd/phases/BUILD +++ b/cmd/kubeadm/app/cmd/phases/BUILD @@ -44,6 +44,7 @@ go_library( "//cmd/kubeadm/app/util/apiclient:go_default_library", "//cmd/kubeadm/app/util/config:go_default_library", "//cmd/kubeadm/app/util/dryrun:go_default_library", + "//cmd/kubeadm/app/util/pkiutil:go_default_library", "//pkg/util/normalizer:go_default_library", "//pkg/version:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", @@ -72,7 +73,9 @@ go_test( "//cmd/kubeadm/app/phases/certs:go_default_library", "//cmd/kubeadm/app/util/pkiutil:go_default_library", "//cmd/kubeadm/test:go_default_library", + "//cmd/kubeadm/test/certs:go_default_library", "//pkg/version:go_default_library", + "//vendor/github.com/spf13/cobra:go_default_library", ], ) diff --git a/cmd/kubeadm/app/cmd/phases/certs.go b/cmd/kubeadm/app/cmd/phases/certs.go index 778cd6c368..cf82a5be68 100644 --- a/cmd/kubeadm/app/cmd/phases/certs.go +++ b/cmd/kubeadm/app/cmd/phases/certs.go @@ -31,6 +31,7 @@ import ( kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants" certsphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/certs" kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util" + "k8s.io/kubernetes/cmd/kubeadm/app/util/pkiutil" "k8s.io/kubernetes/pkg/util/normalizer" ) @@ -228,9 +229,13 @@ func runCAPhase(ca *certsphase.KubeadmCert) func(c workflow.RunData) error { return errors.New("certs phase invoked with an invalid data struct") } - // if external CA mode, skips certificate authority generation - if data.ExternalCA() { - fmt.Printf("[certs] External CA mode: Using existing %s certificate authority\n", ca.BaseName) + // TODO(EKF): can we avoid loading these certificates every time? + if _, err := pkiutil.TryLoadCertFromDisk(data.CertificateDir(), ca.BaseName); err == nil { + if _, err := pkiutil.TryLoadKeyFromDisk(data.CertificateDir(), ca.BaseName); err == nil { + fmt.Printf("[certs] Using existing %s certificate authority\n", ca.BaseName) + return nil + } + fmt.Printf("[certs] Using existing %s keyless certificate authority", ca.BaseName) return nil } @@ -257,9 +262,18 @@ func runCertPhase(cert *certsphase.KubeadmCert, caCert *certsphase.KubeadmCert) return errors.New("certs phase invoked with an invalid data struct") } - // if external CA mode, skip certificate generation - if data.ExternalCA() { - fmt.Printf("[certs] External CA mode: Using existing %s certificate\n", cert.BaseName) + // TODO(EKF): can we avoid loading these certificates every time? + if certData, _, err := pkiutil.TryLoadCertAndKeyFromDisk(data.CertificateDir(), cert.BaseName); err == nil { + caCertData, err := pkiutil.TryLoadCertFromDisk(data.CertificateDir(), caCert.BaseName) + if err != nil { + return errors.Wrapf(err, "couldn't load CA certificate %s", caCert.Name) + } + + if err := certData.CheckSignatureFrom(caCertData); err != nil { + return errors.Wrapf(err, "[certs] certificate %s not signed by CA certificate %s", cert.BaseName, caCert.BaseName) + } + + fmt.Printf("[certs] Using existing %s certificate and key on disk\n", cert.BaseName) return nil } diff --git a/cmd/kubeadm/app/cmd/phases/certs_test.go b/cmd/kubeadm/app/cmd/phases/certs_test.go index 06fc61ce63..3e9c49d8d6 100644 --- a/cmd/kubeadm/app/cmd/phases/certs_test.go +++ b/cmd/kubeadm/app/cmd/phases/certs_test.go @@ -20,11 +20,13 @@ import ( "os" "testing" + "github.com/spf13/cobra" kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" "k8s.io/kubernetes/cmd/kubeadm/app/cmd/phases/workflow" "k8s.io/kubernetes/cmd/kubeadm/app/phases/certs" "k8s.io/kubernetes/cmd/kubeadm/app/util/pkiutil" testutil "k8s.io/kubernetes/cmd/kubeadm/test" + certstestutil "k8s.io/kubernetes/cmd/kubeadm/test/certs" ) type testCertsData struct { @@ -51,6 +53,9 @@ func TestCertsWithCSRs(t *testing.T) { // global vars csrOnly = true csrDir = certDir + defer func() { + csrOnly = false + }() phase := NewCertsPhase() // find the api cert phase @@ -75,3 +80,28 @@ func TestCertsWithCSRs(t *testing.T) { t.Fatalf("couldn't load certificate %q: %v", cert.BaseName, err) } } + +func TestCreateSparseCerts(t *testing.T) { + for _, test := range certstestutil.GetSparseCertTestCases(t) { + t.Run(test.Name, func(t *testing.T) { + tmpdir := testutil.SetupTempDir(t) + defer os.RemoveAll(tmpdir) + + certstestutil.WritePKIFiles(t, tmpdir, test.Files) + + r := workflow.NewRunner() + r.AppendPhase(NewCertsPhase()) + r.SetDataInitializer(func(*cobra.Command) (workflow.RunData, error) { + certsData := &testCertsData{ + cfg: testutil.GetDefaultInternalConfig(t), + } + certsData.cfg.CertificatesDir = tmpdir + return certsData, nil + }) + + if err := r.Run(); (err != nil) != test.ExpectError { + t.Fatalf("expected error to be %t, got %t (%v)", test.ExpectError, (err != nil), err) + } + }) + } +}