From 720f3b45aae6aadb8d91f6233922ebcb61d02c4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20K=C3=A4ldstr=C3=B6m?= Date: Tue, 3 Jan 2017 23:40:07 +0200 Subject: [PATCH] Refactor the pki, cert, kubeconfig code in the kubeadm binary into two separate and logically independent phases --- .../app/apis/kubeadm/v1alpha1/types.go | 7 - cmd/kubeadm/app/cmd/BUILD | 2 + cmd/kubeadm/app/cmd/init.go | 76 +++---- cmd/kubeadm/app/cmd/join.go | 4 +- cmd/kubeadm/app/master/BUILD | 5 - cmd/kubeadm/app/master/apiclient.go | 4 +- cmd/kubeadm/app/master/kubeconfig.go | 59 ------ cmd/kubeadm/app/master/kubeconfig_test.go | 70 ------- cmd/kubeadm/app/master/pki.go | 193 ------------------ cmd/kubeadm/app/node/BUILD | 1 + cmd/kubeadm/app/node/bootstrap.go | 17 +- cmd/kubeadm/app/node/csr.go | 31 +-- cmd/kubeadm/app/phases/certs/BUILD | 38 ++++ cmd/kubeadm/app/phases/certs/certs.go | 102 +++++++++ cmd/kubeadm/app/phases/certs/certs_test.go | 77 +++++++ cmd/kubeadm/app/phases/certs/doc.go | 41 ++++ cmd/kubeadm/app/phases/certs/pki_helpers.go | 110 ++++++++++ .../certs/pki_helpers_test.go} | 97 +-------- cmd/kubeadm/app/phases/kubeconfig/BUILD | 32 +++ cmd/kubeadm/app/phases/kubeconfig/doc.go | 33 +++ .../app/phases/kubeconfig/kubeconfig.go | 164 +++++++++++++++ .../kubeconfig}/kubeconfig_test.go | 136 ++++++------ cmd/kubeadm/app/util/BUILD | 5 - cmd/kubeadm/app/util/kubeconfig.go | 96 --------- federation/pkg/kubefed/init/BUILD | 2 +- federation/pkg/kubefed/init/init.go | 11 +- 26 files changed, 728 insertions(+), 685 deletions(-) delete mode 100644 cmd/kubeadm/app/master/kubeconfig.go delete mode 100644 cmd/kubeadm/app/master/kubeconfig_test.go delete mode 100644 cmd/kubeadm/app/master/pki.go create mode 100644 cmd/kubeadm/app/phases/certs/BUILD create mode 100644 cmd/kubeadm/app/phases/certs/certs.go create mode 100644 cmd/kubeadm/app/phases/certs/certs_test.go create mode 100644 cmd/kubeadm/app/phases/certs/doc.go create mode 100644 cmd/kubeadm/app/phases/certs/pki_helpers.go rename cmd/kubeadm/app/{master/pki_test.go => phases/certs/pki_helpers_test.go} (57%) create mode 100644 cmd/kubeadm/app/phases/kubeconfig/BUILD create mode 100644 cmd/kubeadm/app/phases/kubeconfig/doc.go create mode 100644 cmd/kubeadm/app/phases/kubeconfig/kubeconfig.go rename cmd/kubeadm/app/{util => phases/kubeconfig}/kubeconfig_test.go (57%) delete mode 100644 cmd/kubeadm/app/util/kubeconfig.go diff --git a/cmd/kubeadm/app/apis/kubeadm/v1alpha1/types.go b/cmd/kubeadm/app/apis/kubeadm/v1alpha1/types.go index f05cc6c8c3d..e1ae49bdb08 100644 --- a/cmd/kubeadm/app/apis/kubeadm/v1alpha1/types.go +++ b/cmd/kubeadm/app/apis/kubeadm/v1alpha1/types.go @@ -70,13 +70,6 @@ type Etcd struct { KeyFile string `json:"keyFile"` } -type Secrets struct { - GivenToken string `json:"givenToken"` // dot-separated `.` set by the user - TokenID string `json:"tokenID"` // optional on master side, will be generated if not specified - Token []byte `json:"token"` // optional on master side, will be generated if not specified - BearerToken string `json:"bearerToken"` // set based on Token -} - type NodeConfiguration struct { metav1.TypeMeta `json:",inline"` diff --git a/cmd/kubeadm/app/cmd/BUILD b/cmd/kubeadm/app/cmd/BUILD index f73cfa285a1..424b75ea515 100644 --- a/cmd/kubeadm/app/cmd/BUILD +++ b/cmd/kubeadm/app/cmd/BUILD @@ -27,6 +27,8 @@ go_library( "//cmd/kubeadm/app/discovery:go_default_library", "//cmd/kubeadm/app/master:go_default_library", "//cmd/kubeadm/app/node:go_default_library", + "//cmd/kubeadm/app/phases/certs:go_default_library", + "//cmd/kubeadm/app/phases/kubeconfig:go_default_library", "//cmd/kubeadm/app/preflight:go_default_library", "//cmd/kubeadm/app/util:go_default_library", "//pkg/api:go_default_library", diff --git a/cmd/kubeadm/app/cmd/init.go b/cmd/kubeadm/app/cmd/init.go index 711d132375c..2ce77d19db9 100644 --- a/cmd/kubeadm/app/cmd/init.go +++ b/cmd/kubeadm/app/cmd/init.go @@ -20,6 +20,7 @@ import ( "fmt" "io" "io/ioutil" + "path" "github.com/renstrom/dedent" "github.com/spf13/cobra" @@ -30,6 +31,10 @@ import ( "k8s.io/kubernetes/cmd/kubeadm/app/cmd/flags" "k8s.io/kubernetes/cmd/kubeadm/app/discovery" kubemaster "k8s.io/kubernetes/cmd/kubeadm/app/master" + + certphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/certs" + kubeconfigphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/kubeconfig" + "k8s.io/kubernetes/cmd/kubeadm/app/preflight" kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util" "k8s.io/kubernetes/pkg/api" @@ -103,31 +108,6 @@ func NewCmdInit(out io.Writer) *cobra.Command { cmd.PersistentFlags().StringVar(&cfgPath, "config", cfgPath, "Path to kubeadm config file") - // TODO (phase1+) @errordeveloper make the flags below not show up in --help but rather on --advanced-help - cmd.PersistentFlags().StringSliceVar( - &cfg.Etcd.Endpoints, "external-etcd-endpoints", cfg.Etcd.Endpoints, - "etcd endpoints to use, in case you have an external cluster", - ) - cmd.PersistentFlags().MarkDeprecated("external-etcd-endpoints", "this flag will be removed when componentconfig exists") - - cmd.PersistentFlags().StringVar( - &cfg.Etcd.CAFile, "external-etcd-cafile", cfg.Etcd.CAFile, - "etcd certificate authority certificate file. Note: The path must be in /etc/ssl/certs", - ) - cmd.PersistentFlags().MarkDeprecated("external-etcd-cafile", "this flag will be removed when componentconfig exists") - - cmd.PersistentFlags().StringVar( - &cfg.Etcd.CertFile, "external-etcd-certfile", cfg.Etcd.CertFile, - "etcd client certificate file. Note: The path must be in /etc/ssl/certs", - ) - cmd.PersistentFlags().MarkDeprecated("external-etcd-certfile", "this flag will be removed when componentconfig exists") - - cmd.PersistentFlags().StringVar( - &cfg.Etcd.KeyFile, "external-etcd-keyfile", cfg.Etcd.KeyFile, - "etcd client key file. Note: The path must be in /etc/ssl/certs", - ) - cmd.PersistentFlags().MarkDeprecated("external-etcd-keyfile", "this flag will be removed when componentconfig exists") - cmd.PersistentFlags().BoolVar( &skipPreFlight, "skip-preflight-checks", skipPreFlight, "skip preflight checks normally run before modifying the system", @@ -215,6 +195,13 @@ func (i *Init) Validate() error { // Run executes master node provisioning, including certificates, needed static pod manifests, etc. func (i *Init) Run(out io.Writer) error { + // PHASE 1: Generate certificates + caCert, err := certphase.CreatePKIAssets(i.cfg, kubeadmapi.GlobalEnvParams.HostPKIPath) + if err != nil { + return err + } + + // Exception: if i.cfg.Discovery.Token != nil { if err := kubemaster.PrepareTokenDiscovery(i.cfg.Discovery.Token); err != nil { return err @@ -224,36 +211,23 @@ func (i *Init) Run(out io.Writer) error { } } + // PHASE 2: Generate kubeconfig files for the admin and the kubelet + + // TODO this is not great, but there is only one address we can use here + // so we'll pick the first one, there is much of chance to have an empty + // slice by the time this gets called + masterEndpoint := fmt.Sprintf("https://%s:%d", i.cfg.API.AdvertiseAddresses[0], i.cfg.API.Port) + err = kubeconfigphase.CreateAdminAndKubeletKubeConfig(masterEndpoint, kubeadmapi.GlobalEnvParams.HostPKIPath, kubeadmapi.GlobalEnvParams.KubernetesDir) + if err != nil { + return err + } + + // Phase 3: Bootstrap the control plane if err := kubemaster.WriteStaticPodManifests(i.cfg); err != nil { return err } - caKey, caCert, err := kubemaster.CreatePKIAssets(i.cfg) - if err != nil { - return err - } - - kubeconfigs, err := kubemaster.CreateCertsAndConfigForClients(i.cfg.API, []string{"kubelet", "admin"}, caKey, caCert) - if err != nil { - return err - } - - // kubeadm is responsible for writing the following kubeconfig file, which - // kubelet should be waiting for. Help user avoid foot-shooting by refusing to - // write a file that has already been written (the kubelet will be up and - // running in that case - they'd need to stop the kubelet, remove the file, and - // start it again in that case). - // TODO(phase1+) this is no longer the right place to guard against foo-shooting, - // we need to decide how to handle existing files (it may be handy to support - // importing existing files, may be we could even make our command idempotant, - // or at least allow for external PKI and stuff) - for name, kubeconfig := range kubeconfigs { - if err := kubeadmutil.WriteKubeconfigIfNotExists(name, kubeconfig); err != nil { - return err - } - } - - client, err := kubemaster.CreateClientAndWaitForAPI(kubeconfigs["admin"]) + client, err := kubemaster.CreateClientAndWaitForAPI(path.Join(kubeadmapi.GlobalEnvParams.KubernetesDir, kubeconfigphase.AdminKubeConfigFileName)) if err != nil { return err } diff --git a/cmd/kubeadm/app/cmd/join.go b/cmd/kubeadm/app/cmd/join.go index ef3ae3356dc..27bd2160e60 100644 --- a/cmd/kubeadm/app/cmd/join.go +++ b/cmd/kubeadm/app/cmd/join.go @@ -20,6 +20,7 @@ import ( "fmt" "io" "io/ioutil" + "path" "github.com/renstrom/dedent" "github.com/spf13/cobra" @@ -29,6 +30,7 @@ import ( "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation" "k8s.io/kubernetes/cmd/kubeadm/app/discovery" kubenode "k8s.io/kubernetes/cmd/kubeadm/app/node" + kubeconfigphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/kubeconfig" "k8s.io/kubernetes/cmd/kubeadm/app/preflight" kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util" "k8s.io/kubernetes/pkg/api" @@ -159,7 +161,7 @@ func (j *Join) Run(out io.Writer) error { } } - err = kubeadmutil.WriteKubeconfigIfNotExists("kubelet", cfg) + err = kubeconfigphase.WriteKubeconfigToDisk(path.Join(kubeadmapi.GlobalEnvParams.KubernetesDir, kubeconfigphase.KubeletKubeConfigFileName), cfg) if err != nil { return err } diff --git a/cmd/kubeadm/app/master/BUILD b/cmd/kubeadm/app/master/BUILD index 2d2e42e5791..995ed0e6529 100644 --- a/cmd/kubeadm/app/master/BUILD +++ b/cmd/kubeadm/app/master/BUILD @@ -14,9 +14,7 @@ go_library( "addons.go", "apiclient.go", "discovery.go", - "kubeconfig.go", "manifests.go", - "pki.go", "tokens.go", ], tags = ["automanaged"], @@ -51,9 +49,7 @@ go_test( "addons_test.go", "apiclient_test.go", "discovery_test.go", - "kubeconfig_test.go", "manifests_test.go", - "pki_test.go", "tokens_test.go", ], library = ":go_default_library", @@ -62,7 +58,6 @@ go_test( "//cmd/kubeadm/app/apis/kubeadm:go_default_library", "//cmd/kubeadm/app/util:go_default_library", "//pkg/api/v1:go_default_library", - "//pkg/util/cert:go_default_library", "//pkg/util/intstr:go_default_library", ], ) diff --git a/cmd/kubeadm/app/master/apiclient.go b/cmd/kubeadm/app/master/apiclient.go index d80965f417b..66e72aba2bd 100644 --- a/cmd/kubeadm/app/master/apiclient.go +++ b/cmd/kubeadm/app/master/apiclient.go @@ -60,8 +60,8 @@ func CreateClientFromFile(path string) (*clientset.Clientset, error) { return createAPIClient(adminKubeconfig) } -func CreateClientAndWaitForAPI(adminConfig *clientcmdapi.Config) (*clientset.Clientset, error) { - client, err := createAPIClient(adminConfig) +func CreateClientAndWaitForAPI(file string) (*clientset.Clientset, error) { + client, err := CreateClientFromFile(file) if err != nil { return nil, err } diff --git a/cmd/kubeadm/app/master/kubeconfig.go b/cmd/kubeadm/app/master/kubeconfig.go deleted file mode 100644 index 811e0dff2e5..00000000000 --- a/cmd/kubeadm/app/master/kubeconfig.go +++ /dev/null @@ -1,59 +0,0 @@ -/* -Copyright 2016 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package master - -import ( - "crypto/rsa" - "crypto/x509" - "fmt" - - kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" - kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util" - clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api" - certutil "k8s.io/kubernetes/pkg/util/cert" -) - -func CreateCertsAndConfigForClients(cfg kubeadmapi.API, clientNames []string, caKey *rsa.PrivateKey, caCert *x509.Certificate) (map[string]*clientcmdapi.Config, error) { - - basicClientConfig := kubeadmutil.CreateBasicClientConfig( - "kubernetes", - // TODO this is not great, but there is only one address we can use here - // so we'll pick the first one, there is much of chance to have an empty - // slice by the time this gets called - fmt.Sprintf("https://%s:%d", cfg.AdvertiseAddresses[0], cfg.Port), - certutil.EncodeCertPEM(caCert), - ) - - configs := map[string]*clientcmdapi.Config{} - - for _, client := range clientNames { - key, cert, err := newClientKeyAndCert(caCert, caKey) - if err != nil { - return nil, fmt.Errorf("failure while creating %s client certificate - [%v]", client, err) - } - config := kubeadmutil.MakeClientConfigWithCerts( - basicClientConfig, - "kubernetes", - client, - certutil.EncodePrivateKeyPEM(key), - certutil.EncodeCertPEM(cert), - ) - configs[client] = config - } - - return configs, nil -} diff --git a/cmd/kubeadm/app/master/kubeconfig_test.go b/cmd/kubeadm/app/master/kubeconfig_test.go deleted file mode 100644 index 7fd3533dd33..00000000000 --- a/cmd/kubeadm/app/master/kubeconfig_test.go +++ /dev/null @@ -1,70 +0,0 @@ -/* -Copyright 2016 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package master - -import ( - "crypto/rand" - "crypto/rsa" - "crypto/x509" - "testing" - - kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" -) - -func TestCreateCertsAndConfigForClients(t *testing.T) { - var tests = []struct { - a kubeadmapi.API - cn []string - caKeySize int - expected bool - }{ - { - a: kubeadmapi.API{AdvertiseAddresses: []string{"foo"}}, - cn: []string{"localhost"}, - caKeySize: 128, - expected: false, - }, - { - a: kubeadmapi.API{AdvertiseAddresses: []string{"foo"}}, - cn: []string{}, - caKeySize: 128, - expected: true, - }, - { - a: kubeadmapi.API{AdvertiseAddresses: []string{"foo"}}, - cn: []string{"localhost"}, - caKeySize: 2048, - expected: true, - }, - } - - for _, rt := range tests { - caKey, err := rsa.GenerateKey(rand.Reader, rt.caKeySize) - if err != nil { - t.Fatalf("Couldn't create rsa Private Key") - } - caCert := &x509.Certificate{} - _, actual := CreateCertsAndConfigForClients(rt.a, rt.cn, caKey, caCert) - if (actual == nil) != rt.expected { - t.Errorf( - "failed CreateCertsAndConfigForClients:\n\texpected: %t\n\t actual: %t", - rt.expected, - (actual == nil), - ) - } - } -} diff --git a/cmd/kubeadm/app/master/pki.go b/cmd/kubeadm/app/master/pki.go deleted file mode 100644 index 46fbbdb4c3f..00000000000 --- a/cmd/kubeadm/app/master/pki.go +++ /dev/null @@ -1,193 +0,0 @@ -/* -Copyright 2016 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package master - -import ( - "crypto/rsa" - "crypto/x509" - "fmt" - "net" - "path" - - kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" - "k8s.io/kubernetes/pkg/registry/core/service/ipallocator" - certutil "k8s.io/kubernetes/pkg/util/cert" -) - -func newCertificateAuthority() (*rsa.PrivateKey, *x509.Certificate, error) { - key, err := certutil.NewPrivateKey() - if err != nil { - return nil, nil, fmt.Errorf("unable to create private key [%v]", err) - } - - config := certutil.Config{ - CommonName: "kubernetes", - } - - cert, err := certutil.NewSelfSignedCACert(config, key) - if err != nil { - return nil, nil, fmt.Errorf("unable to create self-signed certificate [%v]", err) - } - - return key, cert, nil -} - -func newServerKeyAndCert(cfg *kubeadmapi.MasterConfiguration, caCert *x509.Certificate, caKey *rsa.PrivateKey, altNames certutil.AltNames) (*rsa.PrivateKey, *x509.Certificate, error) { - key, err := certutil.NewPrivateKey() - if err != nil { - return nil, nil, fmt.Errorf("unable to create private key [%v]", err) - } - - internalAPIServerFQDN := []string{ - "kubernetes", - "kubernetes.default", - "kubernetes.default.svc", - fmt.Sprintf("kubernetes.default.svc.%s", cfg.Networking.DNSDomain), - } - - _, n, err := net.ParseCIDR(cfg.Networking.ServiceSubnet) - if err != nil { - return nil, nil, fmt.Errorf("error parsing CIDR %q: %v", cfg.Networking.ServiceSubnet, err) - } - internalAPIServerVirtualIP, err := ipallocator.GetIndexedIP(n, 1) - if err != nil { - return nil, nil, fmt.Errorf("unable to allocate IP address for the API server from the given CIDR (%q) [%v]", &cfg.Networking.ServiceSubnet, err) - } - - altNames.IPs = append(altNames.IPs, internalAPIServerVirtualIP) - altNames.DNSNames = append(altNames.DNSNames, internalAPIServerFQDN...) - - config := certutil.Config{ - CommonName: "kube-apiserver", - AltNames: altNames, - } - cert, err := certutil.NewSignedCert(config, key, caCert, caKey) - if err != nil { - return nil, nil, fmt.Errorf("unable to sign certificate [%v]", err) - } - - return key, cert, nil -} - -func newClientKeyAndCert(caCert *x509.Certificate, caKey *rsa.PrivateKey) (*rsa.PrivateKey, *x509.Certificate, error) { - key, err := certutil.NewPrivateKey() - if err != nil { - return nil, nil, fmt.Errorf("unable to create private key [%v]", err) - } - - config := certutil.Config{ - CommonName: "kubernetes-admin", - } - cert, err := certutil.NewSignedCert(config, key, caCert, caKey) - if err != nil { - return nil, nil, fmt.Errorf("unable to sign certificate [%v]", err) - } - - return key, cert, nil -} - -func writeKeysAndCert(pkiPath string, name string, key *rsa.PrivateKey, cert *x509.Certificate) error { - publicKeyPath, privateKeyPath, certificatePath := pathsKeysCerts(pkiPath, name) - - if key != nil { - if err := certutil.WriteKey(privateKeyPath, certutil.EncodePrivateKeyPEM(key)); err != nil { - return fmt.Errorf("unable to write private key file (%q) [%v]", privateKeyPath, err) - } - if pubKey, err := certutil.EncodePublicKeyPEM(&key.PublicKey); err == nil { - if err := certutil.WriteKey(publicKeyPath, pubKey); err != nil { - return fmt.Errorf("unable to write public key file (%q) [%v]", publicKeyPath, err) - } - } else { - return fmt.Errorf("unable to encode public key to PEM [%v]", err) - } - } - - if cert != nil { - if err := certutil.WriteCert(certificatePath, certutil.EncodeCertPEM(cert)); err != nil { - return fmt.Errorf("unable to write certificate file (%q) [%v]", certificatePath, err) - } - } - - return nil -} - -func pathsKeysCerts(pkiPath, name string) (string, string, string) { - return path.Join(pkiPath, fmt.Sprintf("%s-pub.pem", name)), - path.Join(pkiPath, fmt.Sprintf("%s-key.pem", name)), - path.Join(pkiPath, fmt.Sprintf("%s.pem", name)) -} - -func newServiceAccountKey() (*rsa.PrivateKey, error) { - key, err := certutil.NewPrivateKey() - if err != nil { - return nil, err - } - return key, nil -} - -// CreatePKIAssets will create and write to disk all PKI assets necessary to establish the control plane. -// It first generates a self-signed CA certificate, a server certificate (signed by the CA) and a key for -// signing service account tokens. It returns CA key and certificate, which is convenient for use with -// client config funcs. -func CreatePKIAssets(cfg *kubeadmapi.MasterConfiguration) (*rsa.PrivateKey, *x509.Certificate, error) { - var ( - err error - altNames certutil.AltNames - ) - - for _, a := range cfg.API.AdvertiseAddresses { - if ip := net.ParseIP(a); ip != nil { - altNames.IPs = append(altNames.IPs, ip) - } else { - return nil, nil, fmt.Errorf("could not parse ip %q", a) - } - } - altNames.DNSNames = append(altNames.DNSNames, cfg.API.ExternalDNSNames...) - - pkiPath := kubeadmapi.GlobalEnvParams.HostPKIPath - - caKey, caCert, err := newCertificateAuthority() - if err != nil { - return nil, nil, fmt.Errorf("failure while creating CA keys and certificate [%v]", err) - } - - if err := writeKeysAndCert(pkiPath, "ca", caKey, caCert); err != nil { - return nil, nil, fmt.Errorf("failure while saving CA keys and certificate [%v]", err) - } - fmt.Println("[certificates] Generated Certificate Authority key and certificate.") - - apiKey, apiCert, err := newServerKeyAndCert(cfg, caCert, caKey, altNames) - if err != nil { - return nil, nil, fmt.Errorf("failure while creating API server keys and certificate [%v]", err) - } - - if err := writeKeysAndCert(pkiPath, "apiserver", apiKey, apiCert); err != nil { - return nil, nil, fmt.Errorf("failure while saving API server keys and certificate [%v]", err) - } - fmt.Println("[certificates] Generated API Server key and certificate") - - saKey, err := newServiceAccountKey() - if err != nil { - return nil, nil, fmt.Errorf("failure while creating service account signing keys [%v]", err) - } - if err := writeKeysAndCert(pkiPath, "sa", saKey, nil); err != nil { - return nil, nil, fmt.Errorf("failure while saving service account signing keys [%v]", err) - } - fmt.Println("[certificates] Generated Service Account signing keys") - fmt.Printf("[certificates] Created keys and certificates in %q\n", pkiPath) - return caKey, caCert, nil -} diff --git a/cmd/kubeadm/app/node/BUILD b/cmd/kubeadm/app/node/BUILD index c101ae4979c..d9ba2fd436e 100644 --- a/cmd/kubeadm/app/node/BUILD +++ b/cmd/kubeadm/app/node/BUILD @@ -18,6 +18,7 @@ go_library( tags = ["automanaged"], deps = [ "//cmd/kubeadm/app/apis/kubeadm:go_default_library", + "//cmd/kubeadm/app/phases/kubeconfig:go_default_library", "//cmd/kubeadm/app/util:go_default_library", "//pkg/api/v1:go_default_library", "//pkg/apis/certificates:go_default_library", diff --git a/cmd/kubeadm/app/node/bootstrap.go b/cmd/kubeadm/app/node/bootstrap.go index 69817be469d..ed2f08ee044 100644 --- a/cmd/kubeadm/app/node/bootstrap.go +++ b/cmd/kubeadm/app/node/bootstrap.go @@ -23,6 +23,7 @@ import ( "time" kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" + kubeconfigphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/kubeconfig" kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util" "k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/apis/certificates" @@ -109,13 +110,15 @@ func EstablishMasterConnection(c *kubeadmapi.TokenDiscovery, clusterInfo *kubead // creates a set of clients for this endpoint func createClients(caCert []byte, endpoint, token string, nodeName types.NodeName) (*clientset.Clientset, error) { - bareClientConfig := kubeadmutil.CreateBasicClientConfig("kubernetes", endpoint, caCert) - bootstrapClientConfig, err := clientcmd.NewDefaultClientConfig( - *kubeadmutil.MakeClientConfigWithToken( - bareClientConfig, "kubernetes", fmt.Sprintf("kubelet-%s", nodeName), token, - ), - &clientcmd.ConfigOverrides{}, - ).ClientConfig() + clientConfig := kubeconfigphase.MakeClientConfigWithToken( + endpoint, + "kubernetes", + fmt.Sprintf("kubelet-%s", nodeName), + caCert, + token, + ) + + bootstrapClientConfig, err := clientcmd.NewDefaultClientConfig(*clientConfig, &clientcmd.ConfigOverrides{}).ClientConfig() if err != nil { return nil, fmt.Errorf("failed to create API client configuration [%v]", err) } diff --git a/cmd/kubeadm/app/node/csr.go b/cmd/kubeadm/app/node/csr.go index d144814cb09..9c279e11c82 100644 --- a/cmd/kubeadm/app/node/csr.go +++ b/cmd/kubeadm/app/node/csr.go @@ -20,7 +20,7 @@ import ( "fmt" "os" - kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util" + kubeconfigphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/kubeconfig" "k8s.io/kubernetes/pkg/client/clientset_generated/clientset" "k8s.io/kubernetes/pkg/client/unversioned/clientcmd" clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api" @@ -30,15 +30,13 @@ import ( ) func PerformTLSBootstrapDeprecated(connection *ConnectionDetails) (*clientcmdapi.Config, error) { - csrClient := connection.CertClient.CertificateSigningRequests() - fmt.Println("[csr] Created API client to obtain unique certificate for this node, generating keys and certificate signing request") key, err := certutil.MakeEllipticPrivateKeyPEM() if err != nil { return nil, fmt.Errorf("failed to generating private key [%v]", err) } - cert, err := csr.RequestNodeCertificate(csrClient, key, connection.NodeName) + cert, err := csr.RequestNodeCertificate(connection.CertClient.CertificateSigningRequests(), key, connection.NodeName) if err != nil { return nil, fmt.Errorf("failed to request signed certificate from the API server [%v]", err) } @@ -49,13 +47,16 @@ func PerformTLSBootstrapDeprecated(connection *ConnectionDetails) (*clientcmdapi fmt.Printf("[csr] Received signed certificate from the API server:\n%s\n", fmtCert) fmt.Println("[csr] Generating kubelet configuration") - bareClientConfig := kubeadmutil.CreateBasicClientConfig("kubernetes", connection.Endpoint, connection.CACert) - finalConfig := kubeadmutil.MakeClientConfigWithCerts( - bareClientConfig, "kubernetes", fmt.Sprintf("kubelet-%s", connection.NodeName), - key, cert, + newConfig := kubeconfigphase.MakeClientConfigWithCerts( + connection.Endpoint, + "kubernetes", + fmt.Sprintf("kubelet-%s", connection.NodeName), + connection.CACert, + key, + cert, ) - return finalConfig, nil + return newConfig, nil } // PerformTLSBootstrap executes a certificate signing request with the @@ -75,22 +76,22 @@ func PerformTLSBootstrap(cfg *clientcmdapi.Config) error { if err != nil { return err } - fmt.Println(" created API client to obtain unique certificate for this node, generating keys and certificate signing request") + fmt.Println("[csr] Created API client to obtain unique certificate for this node, generating keys and certificate signing request") key, err := certutil.MakeEllipticPrivateKeyPEM() if err != nil { - return fmt.Errorf(" failed to generating private key [%v]", err) + return fmt.Errorf("failed to generating private key [%v]", err) } cert, err := csr.RequestNodeCertificate(c.Certificates().CertificateSigningRequests(), key, name) if err != nil { - return fmt.Errorf(" failed to request signed certificate from the API server [%v]", err) + return fmt.Errorf("failed to request signed certificate from the API server [%v]", err) } fmtCert, err := certutil.FormatBytesCert(cert) if err != nil { - return fmt.Errorf(" failed to format certificate [%v]", err) + return fmt.Errorf("failed to format certificate [%v]", err) } - fmt.Printf(" received signed certificate from the API server:\n%s\n", fmtCert) - fmt.Println(" generating kubelet configuration") + fmt.Printf("[csr] Received signed certificate from the API server") + fmt.Println("[csr] Generating kubelet configuration") cfg.AuthInfos["kubelet"] = &clientcmdapi.AuthInfo{ ClientKeyData: key, diff --git a/cmd/kubeadm/app/phases/certs/BUILD b/cmd/kubeadm/app/phases/certs/BUILD new file mode 100644 index 00000000000..45bc8496b28 --- /dev/null +++ b/cmd/kubeadm/app/phases/certs/BUILD @@ -0,0 +1,38 @@ +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +load( + "@io_bazel_rules_go//go:def.bzl", + "go_library", + "go_test", +) + +go_test( + name = "go_default_test", + srcs = [ + "certs_test.go", + "pki_helpers_test.go", + ], + library = ":go_default_library", + tags = ["automanaged"], + deps = [ + "//cmd/kubeadm/app/apis/kubeadm:go_default_library", + "//pkg/util/cert:go_default_library", + ], +) + +go_library( + name = "go_default_library", + srcs = [ + "certs.go", + "doc.go", + "pki_helpers.go", + ], + tags = ["automanaged"], + deps = [ + "//cmd/kubeadm/app/apis/kubeadm:go_default_library", + "//pkg/registry/core/service/ipallocator:go_default_library", + "//pkg/util/cert:go_default_library", + ], +) diff --git a/cmd/kubeadm/app/phases/certs/certs.go b/cmd/kubeadm/app/phases/certs/certs.go new file mode 100644 index 00000000000..cd0396eb1b6 --- /dev/null +++ b/cmd/kubeadm/app/phases/certs/certs.go @@ -0,0 +1,102 @@ +/* +Copyright 2016 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package certs + +import ( + "crypto/x509" + "fmt" + "net" + "os" + + kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" + "k8s.io/kubernetes/pkg/registry/core/service/ipallocator" + certutil "k8s.io/kubernetes/pkg/util/cert" +) + +// CreatePKIAssets will create and write to disk all PKI assets necessary to establish the control plane. +// It first generates a self-signed CA certificate, a server certificate (signed by the CA) and a key for +// signing service account tokens. It returns CA key and certificate, which is convenient for use with +// client config funcs. +func CreatePKIAssets(cfg *kubeadmapi.MasterConfiguration, pkiPath string) (*x509.Certificate, error) { + altNames := certutil.AltNames{} + + // First, define all domains this cert should be signed for + internalAPIServerFQDN := []string{ + "kubernetes", + "kubernetes.default", + "kubernetes.default.svc", + fmt.Sprintf("kubernetes.default.svc.%s", cfg.Networking.DNSDomain), + } + hostname, err := os.Hostname() + if err != nil { + return nil, fmt.Errorf("couldn't get the hostname: %v", err) + } + altNames.DNSNames = append(cfg.API.ExternalDNSNames, hostname) + altNames.DNSNames = append(altNames.DNSNames, internalAPIServerFQDN...) + + // then, add all IP addresses we're bound to + for _, a := range cfg.API.AdvertiseAddresses { + if ip := net.ParseIP(a); ip != nil { + altNames.IPs = append(altNames.IPs, ip) + } else { + return nil, fmt.Errorf("could not parse ip %q", a) + } + } + // and lastly, extract the internal IP address for the API server + _, n, err := net.ParseCIDR(cfg.Networking.ServiceSubnet) + if err != nil { + return nil, fmt.Errorf("error parsing CIDR %q: %v", cfg.Networking.ServiceSubnet, err) + } + internalAPIServerVirtualIP, err := ipallocator.GetIndexedIP(n, 1) + if err != nil { + return nil, fmt.Errorf("unable to allocate IP address for the API server from the given CIDR (%q) [%v]", &cfg.Networking.ServiceSubnet, err) + } + + altNames.IPs = append(altNames.IPs, internalAPIServerVirtualIP) + + caKey, caCert, err := newCertificateAuthority() + if err != nil { + return nil, fmt.Errorf("failure while creating CA keys and certificate [%v]", err) + } + + if err := writeKeysAndCert(pkiPath, "ca", caKey, caCert); err != nil { + return nil, fmt.Errorf("failure while saving CA keys and certificate [%v]", err) + } + fmt.Println("[certificates] Generated Certificate Authority key and certificate.") + + apiKey, apiCert, err := newServerKeyAndCert(caCert, caKey, altNames) + if err != nil { + return nil, fmt.Errorf("failure while creating API server keys and certificate [%v]", err) + } + + if err := writeKeysAndCert(pkiPath, "apiserver", apiKey, apiCert); err != nil { + return nil, fmt.Errorf("failure while saving API server keys and certificate [%v]", err) + } + fmt.Println("[certificates] Generated API Server key and certificate") + + // Generate a private key for service accounts + saKey, err := certutil.NewPrivateKey() + if err != nil { + return nil, fmt.Errorf("failure while creating service account signing keys [%v]", err) + } + if err := writeKeysAndCert(pkiPath, "sa", saKey, nil); err != nil { + return nil, fmt.Errorf("failure while saving service account signing keys [%v]", err) + } + fmt.Println("[certificates] Generated Service Account signing keys") + fmt.Printf("[certificates] Created keys and certificates in %q\n", pkiPath) + return caCert, nil +} diff --git a/cmd/kubeadm/app/phases/certs/certs_test.go b/cmd/kubeadm/app/phases/certs/certs_test.go new file mode 100644 index 00000000000..2377646fc69 --- /dev/null +++ b/cmd/kubeadm/app/phases/certs/certs_test.go @@ -0,0 +1,77 @@ +/* +Copyright 2016 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package certs + +import ( + "fmt" + "io/ioutil" + "os" + "testing" + + kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" +) + +func TestCreatePKIAssets(t *testing.T) { + tmpdir, err := ioutil.TempDir("", "") + if err != nil { + t.Fatalf("Couldn't create tmpdir") + } + defer os.Remove(tmpdir) + + var tests = []struct { + cfg *kubeadmapi.MasterConfiguration + expected bool + }{ + { + cfg: &kubeadmapi.MasterConfiguration{}, + expected: false, + }, + { + // CIDR too small + cfg: &kubeadmapi.MasterConfiguration{ + API: kubeadmapi.API{AdvertiseAddresses: []string{"10.0.0.1"}}, + Networking: kubeadmapi.Networking{ServiceSubnet: "10.0.0.1/1"}, + }, + expected: false, + }, + { + // CIDR invalid + cfg: &kubeadmapi.MasterConfiguration{ + API: kubeadmapi.API{AdvertiseAddresses: []string{"10.0.0.1"}}, + Networking: kubeadmapi.Networking{ServiceSubnet: "invalid"}, + }, + expected: false, + }, + { + cfg: &kubeadmapi.MasterConfiguration{ + API: kubeadmapi.API{AdvertiseAddresses: []string{"10.0.0.1"}}, + Networking: kubeadmapi.Networking{ServiceSubnet: "10.0.0.1/24"}, + }, + expected: true, + }, + } + for _, rt := range tests { + _, actual := CreatePKIAssets(rt.cfg, fmt.Sprintf("%s/etc/kubernetes/pki", tmpdir)) + if (actual == nil) != rt.expected { + t.Errorf( + "failed CreatePKIAssets with an error:\n\texpected: %t\n\t actual: %t", + rt.expected, + (actual == nil), + ) + } + } +} diff --git a/cmd/kubeadm/app/phases/certs/doc.go b/cmd/kubeadm/app/phases/certs/doc.go new file mode 100644 index 00000000000..e92cf488e62 --- /dev/null +++ b/cmd/kubeadm/app/phases/certs/doc.go @@ -0,0 +1,41 @@ +/* +Copyright 2016 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package certs + +/* + + PHASE: CERTIFICATES + + INPUTS: + From MasterConfiguration + .API.AdvertiseAddresses is needed for knowing which IPs the certs should be signed for + .API.ExternalDNSNames is needed for knowing which DNS names the certs should be signed for + .Networking.DNSDomain is needed for knowing which DNS name the internal kubernetes service has + .Networking.ServiceSubnet is needed for knowing which IP the internal kubernetes service is going to point to + The PKIPath is required for knowing where all certificates should be stored + + OUTPUTS: + Files to PKIPath (default /etc/kubernetes/pki): + - apiserver-key.pem + - apiserver-pub.pem + - apiserver.pem + - ca-key.pem + - ca-pub.pem + - ca.pem + - sa-key.pem + - sa-pub.pem +*/ diff --git a/cmd/kubeadm/app/phases/certs/pki_helpers.go b/cmd/kubeadm/app/phases/certs/pki_helpers.go new file mode 100644 index 00000000000..fab73001945 --- /dev/null +++ b/cmd/kubeadm/app/phases/certs/pki_helpers.go @@ -0,0 +1,110 @@ +/* +Copyright 2016 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package certs + +import ( + "crypto/rsa" + "crypto/x509" + "fmt" + "path" + + certutil "k8s.io/kubernetes/pkg/util/cert" +) + +func newCertificateAuthority() (*rsa.PrivateKey, *x509.Certificate, error) { + key, err := certutil.NewPrivateKey() + if err != nil { + return nil, nil, fmt.Errorf("unable to create private key [%v]", err) + } + + config := certutil.Config{ + CommonName: "kubernetes", + } + + cert, err := certutil.NewSelfSignedCACert(config, key) + if err != nil { + return nil, nil, fmt.Errorf("unable to create self-signed certificate [%v]", err) + } + + return key, cert, nil +} + +func newServerKeyAndCert(caCert *x509.Certificate, caKey *rsa.PrivateKey, altNames certutil.AltNames) (*rsa.PrivateKey, *x509.Certificate, error) { + key, err := certutil.NewPrivateKey() + if err != nil { + return nil, nil, fmt.Errorf("unable to create private key [%v]", err) + } + + config := certutil.Config{ + CommonName: "kube-apiserver", + AltNames: altNames, + } + cert, err := certutil.NewSignedCert(config, key, caCert, caKey) + if err != nil { + return nil, nil, fmt.Errorf("unable to sign certificate [%v]", err) + } + + return key, cert, nil +} + +func NewClientKeyAndCert(caCert *x509.Certificate, caKey *rsa.PrivateKey) (*rsa.PrivateKey, *x509.Certificate, error) { + key, err := certutil.NewPrivateKey() + if err != nil { + return nil, nil, fmt.Errorf("unable to create private key [%v]", err) + } + + config := certutil.Config{ + CommonName: "kubernetes-client", + } + cert, err := certutil.NewSignedCert(config, key, caCert, caKey) + if err != nil { + return nil, nil, fmt.Errorf("unable to sign certificate [%v]", err) + } + + return key, cert, nil +} + +func writeKeysAndCert(pkiPath string, name string, key *rsa.PrivateKey, cert *x509.Certificate) error { + publicKeyPath, privateKeyPath, certificatePath := pathsKeysCerts(pkiPath, name) + + if key != nil { + if err := certutil.WriteKey(privateKeyPath, certutil.EncodePrivateKeyPEM(key)); err != nil { + return fmt.Errorf("unable to write private key file (%q) [%v]", privateKeyPath, err) + } + if pubKey, err := certutil.EncodePublicKeyPEM(&key.PublicKey); err == nil { + if err := certutil.WriteKey(publicKeyPath, pubKey); err != nil { + return fmt.Errorf("unable to write public key file (%q) [%v]", publicKeyPath, err) + } + } else { + return fmt.Errorf("unable to encode public key to PEM [%v]", err) + } + } + + if cert != nil { + if err := certutil.WriteCert(certificatePath, certutil.EncodeCertPEM(cert)); err != nil { + return fmt.Errorf("unable to write certificate file (%q) [%v]", certificatePath, err) + } + } + + return nil +} + +func pathsKeysCerts(pkiPath, name string) (string, string, string) { + return path.Join(pkiPath, fmt.Sprintf("%s-pub.pem", name)), + path.Join(pkiPath, fmt.Sprintf("%s-key.pem", name)), + path.Join(pkiPath, fmt.Sprintf("%s.pem", name)) +} diff --git a/cmd/kubeadm/app/master/pki_test.go b/cmd/kubeadm/app/phases/certs/pki_helpers_test.go similarity index 57% rename from cmd/kubeadm/app/master/pki_test.go rename to cmd/kubeadm/app/phases/certs/pki_helpers_test.go index 76a370575d3..a0e72f4a3ad 100644 --- a/cmd/kubeadm/app/master/pki_test.go +++ b/cmd/kubeadm/app/phases/certs/pki_helpers_test.go @@ -14,19 +14,16 @@ See the License for the specific language governing permissions and limitations under the License. */ -package master +package certs import ( "crypto/rand" "crypto/rsa" "crypto/x509" - "fmt" "io/ioutil" "os" "testing" - "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" - kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" certutil "k8s.io/kubernetes/pkg/util/cert" ) @@ -53,38 +50,16 @@ func TestNewCertificateAuthority(t *testing.T) { func TestNewServerKeyAndCert(t *testing.T) { var tests = []struct { - cfg *kubeadmapi.MasterConfiguration caKeySize int expected bool }{ - { - // given CIDR too small - cfg: &kubeadmapi.MasterConfiguration{ - Networking: kubeadm.Networking{ServiceSubnet: "10.0.0.1/1"}, - }, - caKeySize: 2048, - expected: false, - }, - { - // bad CIDR - cfg: &kubeadmapi.MasterConfiguration{ - Networking: kubeadm.Networking{ServiceSubnet: "foo"}, - }, - caKeySize: 2048, - expected: false, - }, { // RSA key too small - cfg: &kubeadmapi.MasterConfiguration{ - Networking: kubeadm.Networking{ServiceSubnet: "10.0.0.1/24"}, - }, caKeySize: 128, expected: false, }, { - cfg: &kubeadmapi.MasterConfiguration{ - Networking: kubeadm.Networking{ServiceSubnet: "10.0.0.1/24"}, - }, + // Should succeed caKeySize: 2048, expected: true, }, @@ -97,7 +72,7 @@ func TestNewServerKeyAndCert(t *testing.T) { } caCert := &x509.Certificate{} altNames := certutil.AltNames{} - _, _, actual := newServerKeyAndCert(rt.cfg, caCert, caKey, altNames) + _, _, actual := newServerKeyAndCert(caCert, caKey, altNames) if (actual == nil) != rt.expected { t.Errorf( "failed newServerKeyAndCert:\n\texpected: %t\n\t actual: %t", @@ -130,10 +105,10 @@ func TestNewClientKeyAndCert(t *testing.T) { t.Fatalf("Couldn't create rsa Private Key") } caCert := &x509.Certificate{} - _, _, actual := newClientKeyAndCert(caCert, caKey) + _, _, actual := NewClientKeyAndCert(caCert, caKey) if (actual == nil) != rt.expected { t.Errorf( - "failed newClientKeyAndCert:\n\texpected: %t\n\t actual: %t", + "failed NewClientKeyAndCert:\n\texpected: %t\n\t actual: %t", rt.expected, (actual == nil), ) @@ -194,65 +169,3 @@ func TestPathsKeysCerts(t *testing.T) { } } } - -func TestNewServiceAccountKey(t *testing.T) { - r, err := newServiceAccountKey() - if r == nil { - t.Errorf( - "failed newServiceAccountKey, rsa key == nil", - ) - } - if err != nil { - t.Errorf( - "failed newServiceAccountKey with an error: %v", - err, - ) - } -} - -func TestCreatePKIAssets(t *testing.T) { - tmpdir, err := ioutil.TempDir("", "") - if err != nil { - t.Fatalf("Couldn't create tmpdir") - } - defer os.Remove(tmpdir) - - // set up tmp GlobalEnvParams values for testing - oldEnv := kubeadmapi.GlobalEnvParams - kubeadmapi.GlobalEnvParams.HostPKIPath = fmt.Sprintf("%s/etc/kubernetes/pki", tmpdir) - defer func() { kubeadmapi.GlobalEnvParams = oldEnv }() - - var tests = []struct { - cfg *kubeadmapi.MasterConfiguration - expected bool - }{ - { - cfg: &kubeadmapi.MasterConfiguration{}, - expected: false, - }, - { - cfg: &kubeadmapi.MasterConfiguration{ - API: kubeadm.API{AdvertiseAddresses: []string{"10.0.0.1"}}, - Networking: kubeadm.Networking{ServiceSubnet: "10.0.0.1/1"}, - }, - expected: false, - }, - { - cfg: &kubeadmapi.MasterConfiguration{ - API: kubeadm.API{AdvertiseAddresses: []string{"10.0.0.1"}}, - Networking: kubeadm.Networking{ServiceSubnet: "10.0.0.1/24"}, - }, - expected: true, - }, - } - for _, rt := range tests { - _, _, actual := CreatePKIAssets(rt.cfg) - if (actual == nil) != rt.expected { - t.Errorf( - "failed CreatePKIAssets with an error:\n\texpected: %t\n\t actual: %t", - rt.expected, - (actual == nil), - ) - } - } -} diff --git a/cmd/kubeadm/app/phases/kubeconfig/BUILD b/cmd/kubeadm/app/phases/kubeconfig/BUILD new file mode 100644 index 00000000000..ad1c7e31125 --- /dev/null +++ b/cmd/kubeadm/app/phases/kubeconfig/BUILD @@ -0,0 +1,32 @@ +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +load( + "@io_bazel_rules_go//go:def.bzl", + "go_library", + "go_test", +) + +go_test( + name = "go_default_test", + srcs = ["kubeconfig_test.go"], + library = ":go_default_library", + tags = ["automanaged"], + deps = ["//cmd/kubeadm/app/apis/kubeadm:go_default_library"], +) + +go_library( + name = "go_default_library", + srcs = [ + "doc.go", + "kubeconfig.go", + ], + tags = ["automanaged"], + deps = [ + "//cmd/kubeadm/app/phases/certs:go_default_library", + "//pkg/client/unversioned/clientcmd:go_default_library", + "//pkg/client/unversioned/clientcmd/api:go_default_library", + "//pkg/util/cert:go_default_library", + ], +) diff --git a/cmd/kubeadm/app/phases/kubeconfig/doc.go b/cmd/kubeadm/app/phases/kubeconfig/doc.go new file mode 100644 index 00000000000..c69a278432a --- /dev/null +++ b/cmd/kubeadm/app/phases/kubeconfig/doc.go @@ -0,0 +1,33 @@ +/* +Copyright 2016 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package kubeconfig + +/* + + PHASE: KUBECONFIG + + INPUTS: + From MasterConfiguration + (.API.AdvertiseAddresses is currently needed for knowing the) MasterAPIEndpoint is required so the KubeConfig file knows where to find the master + The KubernetesDir path is required for knowing where to put the KubeConfig files + The PKIPath is required for knowing where all certificates should be stored + + OUTPUTS: + Files to KubernetesDir (default /etc/kubernetes): + - admin.conf + - kubelet.conf +*/ diff --git a/cmd/kubeadm/app/phases/kubeconfig/kubeconfig.go b/cmd/kubeadm/app/phases/kubeconfig/kubeconfig.go new file mode 100644 index 00000000000..afb1739fdd2 --- /dev/null +++ b/cmd/kubeadm/app/phases/kubeconfig/kubeconfig.go @@ -0,0 +1,164 @@ +/* +Copyright 2016 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package kubeconfig + +import ( + "crypto/ecdsa" + "crypto/rsa" + "crypto/x509" + "fmt" + "os" + "path" + + certphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/certs" + "k8s.io/kubernetes/pkg/client/unversioned/clientcmd" + clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api" + certutil "k8s.io/kubernetes/pkg/util/cert" +) + +const ( + KubernetesDirPermissions = 0700 + AdminKubeConfigFileName = "admin.conf" + KubeletKubeConfigFileName = "kubelet.conf" +) + +// This function is called from the main init and does the work for the default phase behaviour +// TODO: Make an integration test for this function that runs after the certificates phase +// and makes sure that those two phases work well together... +func CreateAdminAndKubeletKubeConfig(masterEndpoint, pkiDir, outDir string) error { + // Parse the certificate from a file + caCertPath := path.Join(pkiDir, "ca.pem") + caCerts, err := certutil.CertsFromFile(caCertPath) + if err != nil { + return fmt.Errorf("couldn't load the CA cert file %s: %v", caCertPath, err) + } + // We are only putting one certificate in the CA certificate pem file, so it's safe to just use the first one + caCert := caCerts[0] + + // Parse the rsa private key from a file + caKeyPath := path.Join(pkiDir, "ca-key.pem") + priv, err := certutil.PrivateKeyFromFile(caKeyPath) + if err != nil { + return fmt.Errorf("couldn't load the CA private key file %s: %v", caKeyPath, err) + } + var caKey *rsa.PrivateKey + switch k := priv.(type) { + case *rsa.PrivateKey: + caKey = k + case *ecdsa.PrivateKey: + // TODO: Abstract rsa.PrivateKey away and make certutil.NewSignedCert accept a ecdsa.PrivateKey as well + // After that, we can support generating kubeconfig files from ecdsa private keys as well + return fmt.Errorf("the CA private key file %s isn't in RSA format", caKeyPath) + default: + return fmt.Errorf("the CA private key file %s isn't in RSA format", caKeyPath) + } + + // User admin should have full access to the cluster + if err := createKubeConfigFileForClient(masterEndpoint, "admin", outDir, caCert, caKey); err != nil { + return fmt.Errorf("couldn't create a kubeconfig file for admin: %v", err) + } + + // TODO: The kubelet should have limited access to the cluster + if err := createKubeConfigFileForClient(masterEndpoint, "kubelet", outDir, caCert, caKey); err != nil { + return fmt.Errorf("couldn't create a kubeconfig file for kubelet: %v", err) + } + + return nil +} + +func createKubeConfigFileForClient(masterEndpoint, client, outDir string, caCert *x509.Certificate, caKey *rsa.PrivateKey) error { + key, cert, err := certphase.NewClientKeyAndCert(caCert, caKey) + if err != nil { + return fmt.Errorf("failure while creating %s client certificate [%v]", client, err) + } + + config := MakeClientConfigWithCerts( + masterEndpoint, + "kubernetes", + client, + certutil.EncodeCertPEM(caCert), + certutil.EncodePrivateKeyPEM(key), + certutil.EncodeCertPEM(cert), + ) + + // Write it now to a file + filepath := path.Join(outDir, fmt.Sprintf("%s.conf", client)) + return WriteKubeconfigToDisk(filepath, config) +} + +func WriteKubeconfigToDisk(filepath string, kubeconfig *clientcmdapi.Config) error { + // Make sure the dir exists or can be created + if err := os.MkdirAll(path.Dir(filepath), KubernetesDirPermissions); err != nil { + return fmt.Errorf("failed to create directory %q [%v]", path.Dir(filepath), err) + } + + // If err == nil, the file exists. Oops, we don't allow the file to exist already, fail. + if _, err := os.Stat(filepath); err == nil { + return fmt.Errorf("kubeconfig file %s already exists, but must not exist.", filepath) + } + + if err := clientcmd.WriteToFile(*kubeconfig, filepath); err != nil { + return fmt.Errorf("failed to write to %q [%v]", filepath, err) + } + + fmt.Printf("[kubeconfig] Wrote KubeConfig file to disk: %q\n", filepath) + return nil +} + +func createBasicClientConfig(serverURL string, clusterName string, userName string, caCert []byte) *clientcmdapi.Config { + config := clientcmdapi.NewConfig() + + // Make a new cluster, specify the endpoint we'd like to talk to and the ca cert we're gonna use + cluster := clientcmdapi.NewCluster() + cluster.Server = serverURL + cluster.CertificateAuthorityData = caCert + + // Specify a context where we're using that cluster and the username as the auth information + contextName := fmt.Sprintf("%s@%s", userName, clusterName) + context := clientcmdapi.NewContext() + context.Cluster = clusterName + context.AuthInfo = userName + + // Lastly, apply the created objects above to the config + config.Clusters[clusterName] = cluster + config.Contexts[contextName] = context + config.CurrentContext = contextName + return config +} + +// Creates a clientcmdapi.Config object with access to the API server with client certificates +func MakeClientConfigWithCerts(serverURL, clusterName, userName string, caCert []byte, clientKey []byte, clientCert []byte) *clientcmdapi.Config { + config := createBasicClientConfig(serverURL, clusterName, userName, caCert) + + authInfo := clientcmdapi.NewAuthInfo() + authInfo.ClientKeyData = clientKey + authInfo.ClientCertificateData = clientCert + + config.AuthInfos[userName] = authInfo + return config +} + +// Creates a clientcmdapi.Config object with access to the API server with a token +func MakeClientConfigWithToken(serverURL, clusterName, userName string, caCert []byte, token string) *clientcmdapi.Config { + config := createBasicClientConfig(serverURL, clusterName, userName, caCert) + + authInfo := clientcmdapi.NewAuthInfo() + authInfo.Token = token + + config.AuthInfos[userName] = authInfo + return config +} diff --git a/cmd/kubeadm/app/util/kubeconfig_test.go b/cmd/kubeadm/app/phases/kubeconfig/kubeconfig_test.go similarity index 57% rename from cmd/kubeadm/app/util/kubeconfig_test.go rename to cmd/kubeadm/app/phases/kubeconfig/kubeconfig_test.go index 5e9869f4a2e..b543226751d 100644 --- a/cmd/kubeadm/app/util/kubeconfig_test.go +++ b/cmd/kubeadm/app/phases/kubeconfig/kubeconfig_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package util +package kubeconfig import ( "bytes" @@ -25,7 +25,6 @@ import ( "testing" kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" - clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api" ) const ( @@ -33,65 +32,54 @@ const ( clusters: - cluster: server: "" - name: "" -contexts: [] -current-context: "" + name: k8s +contexts: +- context: + cluster: k8s + user: user1 + name: user1@k8s +current-context: user1@k8s kind: Config preferences: {} -users: [] +users: +- name: user1 + user: + token: abc ` configOut2 = `apiVersion: v1 clusters: - cluster: - server: "" + server: localhost:8080 name: kubernetes -contexts: [] -current-context: "" +contexts: +- context: + cluster: kubernetes + user: user2 + name: user2@kubernetes +current-context: user2@kubernetes kind: Config preferences: {} -users: [] +users: +- name: user2 + user: + token: cba ` ) type configClient struct { - c string - s string - ca []byte + clusterName string + userName string + serverURL string + caCert []byte } type configClientWithCerts struct { - c *clientcmdapi.Config - clusterName string - userName string - clientKey []byte - clientCert []byte + clientKey []byte + clientCert []byte } type configClientWithToken struct { - c *clientcmdapi.Config - clusterName string - userName string - token string -} - -func TestCreateBasicClientConfig(t *testing.T) { - var createBasicTest = []struct { - cc configClient - expected string - }{ - {configClient{}, ""}, - {configClient{c: "kubernetes"}, ""}, - } - for _, rt := range createBasicTest { - c := CreateBasicClientConfig(rt.cc.c, rt.cc.s, rt.cc.ca) - if c.Kind != rt.expected { - t.Errorf( - "failed CreateBasicClientConfig:\n\texpected: %s\n\t actual: %s", - c.Kind, - rt.expected, - ) - } - } + token string } func TestMakeClientConfigWithCerts(t *testing.T) { @@ -101,23 +89,22 @@ func TestMakeClientConfigWithCerts(t *testing.T) { expected string }{ {configClient{}, configClientWithCerts{}, ""}, - {configClient{c: "kubernetes"}, configClientWithCerts{}, ""}, + {configClient{clusterName: "kubernetes"}, configClientWithCerts{}, ""}, } for _, rt := range createBasicTest { - c := CreateBasicClientConfig(rt.cc.c, rt.cc.s, rt.cc.ca) - rt.ccWithCerts.c = c cwc := MakeClientConfigWithCerts( - rt.ccWithCerts.c, - rt.ccWithCerts.clusterName, - rt.ccWithCerts.userName, + rt.cc.serverURL, + rt.cc.clusterName, + rt.cc.userName, + rt.cc.caCert, rt.ccWithCerts.clientKey, rt.ccWithCerts.clientCert, ) if cwc.Kind != rt.expected { t.Errorf( "failed MakeClientConfigWithCerts:\n\texpected: %s\n\t actual: %s", - c.Kind, rt.expected, + cwc.Kind, ) } } @@ -130,28 +117,27 @@ func TestMakeClientConfigWithToken(t *testing.T) { expected string }{ {configClient{}, configClientWithToken{}, ""}, - {configClient{c: "kubernetes"}, configClientWithToken{}, ""}, + {configClient{clusterName: "kubernetes"}, configClientWithToken{}, ""}, } for _, rt := range createBasicTest { - c := CreateBasicClientConfig(rt.cc.c, rt.cc.s, rt.cc.ca) - rt.ccWithToken.c = c cwc := MakeClientConfigWithToken( - rt.ccWithToken.c, - rt.ccWithToken.clusterName, - rt.ccWithToken.userName, + rt.cc.serverURL, + rt.cc.clusterName, + rt.cc.userName, + rt.cc.caCert, rt.ccWithToken.token, ) if cwc.Kind != rt.expected { t.Errorf( "failed MakeClientConfigWithCerts:\n\texpected: %s\n\t actual: %s", - c.Kind, rt.expected, + cwc.Kind, ) } } } -func TestWriteKubeconfigIfNotExists(t *testing.T) { +func TestWriteKubeconfigToDisk(t *testing.T) { tmpdir, err := ioutil.TempDir("", "") if err != nil { t.Fatalf("Couldn't create tmpdir") @@ -162,37 +148,41 @@ func TestWriteKubeconfigIfNotExists(t *testing.T) { oldEnv := kubeadmapi.GlobalEnvParams kubeadmapi.GlobalEnvParams = kubeadmapi.SetEnvParams() kubeadmapi.GlobalEnvParams.KubernetesDir = fmt.Sprintf("%s/etc/kubernetes", tmpdir) - kubeadmapi.GlobalEnvParams.HostPKIPath = fmt.Sprintf("%s/etc/kubernetes/pki", tmpdir) - kubeadmapi.GlobalEnvParams.HostEtcdPath = fmt.Sprintf("%s/var/lib/etcd", tmpdir) - kubeadmapi.GlobalEnvParams.DiscoveryImage = fmt.Sprintf("%s/var/lib/etcd", tmpdir) defer func() { kubeadmapi.GlobalEnvParams = oldEnv }() var writeConfig = []struct { - name string - cc configClient - expected error - file []byte + name string + cc configClient + ccWithToken configClientWithToken + expected error + file []byte }{ - {"test1", configClient{}, nil, []byte(configOut1)}, - {"test2", configClient{c: "kubernetes"}, nil, []byte(configOut2)}, + {"test1", configClient{clusterName: "k8s", userName: "user1"}, configClientWithToken{token: "abc"}, nil, []byte(configOut1)}, + {"test2", configClient{clusterName: "kubernetes", userName: "user2", serverURL: "localhost:8080"}, configClientWithToken{token: "cba"}, nil, []byte(configOut2)}, } for _, rt := range writeConfig { - c := CreateBasicClientConfig(rt.cc.c, rt.cc.s, rt.cc.ca) - err := WriteKubeconfigIfNotExists(rt.name, c) + c := MakeClientConfigWithToken( + rt.cc.serverURL, + rt.cc.clusterName, + rt.cc.userName, + rt.cc.caCert, + rt.ccWithToken.token, + ) + configPath := filepath.Join(kubeadmapi.GlobalEnvParams.KubernetesDir, fmt.Sprintf("%s.conf", rt.name)) + err := WriteKubeconfigToDisk(configPath, c) if err != rt.expected { t.Errorf( - "failed WriteKubeconfigIfNotExists with an error:\n\texpected: %s\n\t actual: %s", - err, + "failed WriteKubeconfigToDisk with an error:\n\texpected: %s\n\t actual: %s", rt.expected, + err, ) } - configPath := filepath.Join(kubeadmapi.GlobalEnvParams.KubernetesDir, fmt.Sprintf("%s.conf", rt.name)) newFile, err := ioutil.ReadFile(configPath) if !bytes.Equal(newFile, rt.file) { t.Errorf( - "failed WriteKubeconfigIfNotExists config write:\n\texpected: %s\n\t actual: %s", - newFile, + "failed WriteKubeconfigToDisk config write:\n\texpected: %s\n\t actual: %s", rt.file, + newFile, ) } } diff --git a/cmd/kubeadm/app/util/BUILD b/cmd/kubeadm/app/util/BUILD index 1ca9e5aa78a..00998129fa5 100644 --- a/cmd/kubeadm/app/util/BUILD +++ b/cmd/kubeadm/app/util/BUILD @@ -12,7 +12,6 @@ go_library( name = "go_default_library", srcs = [ "error.go", - "kubeconfig.go", "tokens.go", "version.go", ], @@ -26,8 +25,6 @@ go_library( "//pkg/api/v1:go_default_library", "//pkg/apis/meta/v1:go_default_library", "//pkg/client/clientset_generated/clientset:go_default_library", - "//pkg/client/unversioned/clientcmd:go_default_library", - "//pkg/client/unversioned/clientcmd/api:go_default_library", ], ) @@ -35,7 +32,6 @@ go_test( name = "go_default_test", srcs = [ "error_test.go", - "kubeconfig_test.go", "tokens_test.go", "version_test.go", ], @@ -44,6 +40,5 @@ go_test( deps = [ "//cmd/kubeadm/app/apis/kubeadm:go_default_library", "//cmd/kubeadm/app/preflight:go_default_library", - "//pkg/client/unversioned/clientcmd/api:go_default_library", ], ) diff --git a/cmd/kubeadm/app/util/kubeconfig.go b/cmd/kubeadm/app/util/kubeconfig.go deleted file mode 100644 index cf5c1b21e76..00000000000 --- a/cmd/kubeadm/app/util/kubeconfig.go +++ /dev/null @@ -1,96 +0,0 @@ -/* -Copyright 2016 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package util - -import ( - "fmt" - "os" - "path" - - kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" - "k8s.io/kubernetes/pkg/client/unversioned/clientcmd" - clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api" -) - -func CreateBasicClientConfig(clusterName string, serverURL string, caCert []byte) *clientcmdapi.Config { - cluster := clientcmdapi.NewCluster() - cluster.Server = serverURL - cluster.CertificateAuthorityData = caCert - - config := clientcmdapi.NewConfig() - config.Clusters[clusterName] = cluster - - return config -} - -func MakeClientConfigWithCerts(config *clientcmdapi.Config, clusterName string, userName string, clientKey []byte, clientCert []byte) *clientcmdapi.Config { - newConfig := config - name := fmt.Sprintf("%s@%s", userName, clusterName) - - authInfo := clientcmdapi.NewAuthInfo() - authInfo.ClientKeyData = clientKey - authInfo.ClientCertificateData = clientCert - - context := clientcmdapi.NewContext() - context.Cluster = clusterName - context.AuthInfo = userName - - newConfig.AuthInfos[userName] = authInfo - newConfig.Contexts[name] = context - newConfig.CurrentContext = name - - return newConfig -} - -func MakeClientConfigWithToken(config *clientcmdapi.Config, clusterName string, userName string, token string) *clientcmdapi.Config { - newConfig := config - name := fmt.Sprintf("%s@%s", userName, clusterName) - - authInfo := clientcmdapi.NewAuthInfo() - authInfo.Token = token - - context := clientcmdapi.NewContext() - context.Cluster = clusterName - context.AuthInfo = userName - - newConfig.AuthInfos[userName] = authInfo - newConfig.Contexts[name] = context - newConfig.CurrentContext = name - - return newConfig -} - -func WriteKubeconfigIfNotExists(name string, kubeconfig *clientcmdapi.Config) error { - if err := os.MkdirAll(kubeadmapi.GlobalEnvParams.KubernetesDir, 0700); err != nil { - return fmt.Errorf("failed to create directory %q [%v]", kubeadmapi.GlobalEnvParams.KubernetesDir, err) - } - - filename := path.Join(kubeadmapi.GlobalEnvParams.KubernetesDir, fmt.Sprintf("%s.conf", name)) - // Create and open the file, only if it does not already exist. - f, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY|os.O_EXCL, 0600) - if err != nil { - return fmt.Errorf("failed to create %q, it already exists [%v]", filename, err) - } - f.Close() - - if err := clientcmd.WriteToFile(*kubeconfig, filename); err != nil { - return fmt.Errorf("failed to write to %q [%v]", filename, err) - } - - fmt.Printf("[kubeconfig] Wrote KubeConfig file to disk: %q\n", filename) - return nil -} diff --git a/federation/pkg/kubefed/init/BUILD b/federation/pkg/kubefed/init/BUILD index 69b5a61d339..86f9d25cb87 100644 --- a/federation/pkg/kubefed/init/BUILD +++ b/federation/pkg/kubefed/init/BUILD @@ -13,7 +13,7 @@ go_library( srcs = ["init.go"], tags = ["automanaged"], deps = [ - "//cmd/kubeadm/app/util:go_default_library", + "//cmd/kubeadm/app/phases/kubeconfig:go_default_library", "//federation/pkg/kubefed/util:go_default_library", "//pkg/api:go_default_library", "//pkg/api/resource:go_default_library", diff --git a/federation/pkg/kubefed/init/init.go b/federation/pkg/kubefed/init/init.go index 591f1554ad6..98554cb7963 100644 --- a/federation/pkg/kubefed/init/init.go +++ b/federation/pkg/kubefed/init/init.go @@ -36,7 +36,7 @@ import ( "strings" "time" - kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util" + kubeadmkubeconfigphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/kubeconfig" "k8s.io/kubernetes/federation/pkg/kubefed/util" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/resource" @@ -360,16 +360,11 @@ func createAPIServerCredentialsSecret(clientset *client.Clientset, namespace, cr } func createControllerManagerKubeconfigSecret(clientset *client.Clientset, namespace, name, svcName, kubeconfigName string, entKeyPairs *entityKeyPairs, dryRun bool) (*api.Secret, error) { - basicClientConfig := kubeadmutil.CreateBasicClientConfig( - name, + config := kubeadmkubeconfigphase.MakeClientConfigWithCerts( fmt.Sprintf("https://%s", svcName), - certutil.EncodeCertPEM(entKeyPairs.ca.Cert), - ) - - config := kubeadmutil.MakeClientConfigWithCerts( - basicClientConfig, name, "federation-controller-manager", + certutil.EncodeCertPEM(entKeyPairs.ca.Cert), certutil.EncodePrivateKeyPEM(entKeyPairs.controllerManager.Key), certutil.EncodeCertPEM(entKeyPairs.controllerManager.Cert), )