// PerformTLSBootstrap executes a certificate signing request with the // provided connection details. func PerformTLSBootstrap(connection *ConnectionDetails) (*clientcmdapi.Config, error) { csrClient := connection.CertClient.CertificateSigningRequests() fmt.Println("<node/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("<node/csr> failed to generating private key [%v]", err) } cert, err := csr.RequestNodeCertificate(csrClient, key, connection.NodeName) if err != nil { return nil, fmt.Errorf("<node/csr> failed to request signed certificate from the API server [%v]", err) } fmtCert, err := certutil.FormatBytesCert(cert) if err != nil { return nil, fmt.Errorf("<node/csr> failed to format certificate [%v]", err) } fmt.Printf("<node/csr> received signed certificate from the API server:\n%s\n", fmtCert) fmt.Println("<node/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, ) return finalConfig, nil }
// PerformTLSBootstrap creates a RESTful client in order to execute certificate signing request. func PerformTLSBootstrap(s *kubeadmapi.KubeadmConfig, apiEndpoint string, caCert []byte) (*clientcmdapi.Config, error) { // TODO(phase1+) try all the api servers until we find one that works bareClientConfig := kubeadmutil.CreateBasicClientConfig("kubernetes", apiEndpoint, caCert) hostName, err := os.Hostname() if err != nil { return nil, fmt.Errorf("<node/csr> failed to get node hostname [%v]", err) } // TODO: hostname == nodename doesn't hold on all clouds (AWS). // But we don't have a cloudprovider, so we're stuck. glog.Errorf("assuming that hostname is the same as NodeName") nodeName := types.NodeName(hostName) bootstrapClientConfig, err := clientcmd.NewDefaultClientConfig( *kubeadmutil.MakeClientConfigWithToken( bareClientConfig, "kubernetes", fmt.Sprintf("kubelet-%s", nodeName), s.Secrets.BearerToken, ), &clientcmd.ConfigOverrides{}, ).ClientConfig() if err != nil { return nil, fmt.Errorf("<node/csr> failed to create API client configuration [%v]", err) } client, err := unversionedcertificates.NewForConfig(bootstrapClientConfig) if err != nil { return nil, fmt.Errorf("<node/csr> failed to create API client [%v]", err) } csrClient := client.CertificateSigningRequests() // TODO(phase1+) checkCertsAPI() has a side-effect of making first attempt of communicating with the API, // we should _make it more explicit_ and have a user-settable _retry timeout_ to account for potential connectivity issues // (for example user may be bringing up machines in parallel and for some reasons master is slow to boot) if err := checkCertsAPI(bootstrapClientConfig); err != nil { return nil, fmt.Errorf("<node/csr> failed to proceed due to API compatibility issue - %v", err) } fmt.Println("<node/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("<node/csr> failed to generating private key [%v]", err) } cert, err := csr.RequestNodeCertificate(csrClient, key, nodeName) if err != nil { return nil, fmt.Errorf("<node/csr> failed to request signed certificate from the API server [%v]", err) } // TODO(phase1+) print some basic info about the cert fmt.Println("<node/csr> received signed certificate from the API server, generating kubelet configuration") finalConfig := kubeadmutil.MakeClientConfigWithCerts( bareClientConfig, "kubernetes", fmt.Sprintf("kubelet-%s", nodeName), key, cert, ) return finalConfig, nil }
// PerformTLSBootstrap creates a RESTful client in order to execute certificate signing request. func PerformTLSBootstrap(s *kubeadmapi.NodeConfiguration, apiEndpoint string, caCert []byte) (*clientcmdapi.Config, error) { // TODO(phase1+) try all the api servers until we find one that works bareClientConfig := kubeadmutil.CreateBasicClientConfig("kubernetes", apiEndpoint, caCert) hostName, err := os.Hostname() if err != nil { return nil, fmt.Errorf("<node/csr> failed to get node hostname [%v]", err) } // TODO(phase1+) https://github.com/kubernetes/kubernetes/issues/33641 nodeName := types.NodeName(hostName) bootstrapClientConfig, err := clientcmd.NewDefaultClientConfig( *kubeadmutil.MakeClientConfigWithToken( bareClientConfig, "kubernetes", fmt.Sprintf("kubelet-%s", nodeName), s.Secrets.BearerToken, ), &clientcmd.ConfigOverrides{}, ).ClientConfig() if err != nil { return nil, fmt.Errorf("<node/csr> failed to create API client configuration [%v]", err) } client, err := unversionedcertificates.NewForConfig(bootstrapClientConfig) if err != nil { return nil, fmt.Errorf("<node/csr> failed to create API client [%v]", err) } csrClient := client.CertificateSigningRequests() // TODO(phase1+) https://github.com/kubernetes/kubernetes/issues/33643 if err := checkCertsAPI(bootstrapClientConfig); err != nil { return nil, fmt.Errorf("<node/csr> failed to proceed due to API compatibility issue - %v", err) } fmt.Println("<node/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("<node/csr> failed to generating private key [%v]", err) } cert, err := csr.RequestNodeCertificate(csrClient, key, nodeName) if err != nil { return nil, fmt.Errorf("<node/csr> failed to request signed certificate from the API server [%v]", err) } fmtCert, err := certutil.FormatBytesCert(cert) if err != nil { return nil, fmt.Errorf("<node/csr> failed to format certificate [%v]", err) } fmt.Printf("<node/csr> received signed certificate from the API server:\n%s\n", fmtCert) fmt.Println("<node/csr> generating kubelet configuration") finalConfig := kubeadmutil.MakeClientConfigWithCerts( bareClientConfig, "kubernetes", fmt.Sprintf("kubelet-%s", nodeName), key, cert, ) return finalConfig, nil }
// PerformTLSBootstrap executes a node certificate signing request. func PerformTLSBootstrap(cfg *clientcmdapi.Config) error { hostName, err := os.Hostname() if err != nil { return err } name := types.NodeName(hostName) rc, err := clientcmd.NewDefaultClientConfig(*cfg, &clientcmd.ConfigOverrides{}).ClientConfig() if err != nil { return err } c, err := clientset.NewForConfig(rc) if err != nil { return err } 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 generate private key [%v]", err) } // Make sure there are no other nodes in the cluster with identical node name. if err := checkForNodeNameDuplicates(c); err != nil { return 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) } fmt.Printf("[csr] Received signed certificate from the API server") fmt.Println("[csr] Generating kubelet configuration") cfg.AuthInfos["kubelet"] = &clientcmdapi.AuthInfo{ ClientKeyData: key, ClientCertificateData: cert, } cfg.Contexts["kubelet"] = &clientcmdapi.Context{ AuthInfo: "kubelet", Cluster: cfg.Contexts[cfg.CurrentContext].Cluster, } cfg.CurrentContext = "kubelet" return nil }
// PerformTLSBootstrap executes a certificate signing request with the // provided connection details. func PerformTLSBootstrap(cfg *clientcmdapi.Config) error { hostName, err := os.Hostname() if err != nil { return err } name := types.NodeName(hostName) rc, err := clientcmd.NewDefaultClientConfig(*cfg, nil).ClientConfig() if err != nil { return err } c, err := clientset.NewForConfig(rc) if err != nil { return err } fmt.Println("<node/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("<node/csr> failed to generating private key [%v]", err) } cert, err := csr.RequestNodeCertificate(c.Certificates().CertificateSigningRequests(), key, name) if err != nil { return fmt.Errorf("<node/csr> failed to request signed certificate from the API server [%v]", err) } fmtCert, err := certutil.FormatBytesCert(cert) if err != nil { return fmt.Errorf("<node/csr> failed to format certificate [%v]", err) } fmt.Printf("<node/csr> received signed certificate from the API server:\n%s\n", fmtCert) fmt.Println("<node/csr> generating kubelet configuration") cfg.AuthInfos["kubelet"] = &clientcmdapi.AuthInfo{ ClientKeyData: key, ClientCertificateData: []byte(fmtCert), } cfg.Contexts["kubelet"] = &clientcmdapi.Context{ AuthInfo: "kubelet", Cluster: cfg.Contexts[cfg.CurrentContext].Cluster, } cfg.CurrentContext = "kubelet" return nil }
// bootstrapClientCert requests a client cert for kubelet if the kubeconfigPath file does not exist. // The kubeconfig at bootstrapPath is used to request a client certificate from the API server. // On success, a kubeconfig file referencing the generated key and obtained certificate is written to kubeconfigPath. // The certificate and key file are stored in certDir. func bootstrapClientCert(kubeconfigPath string, bootstrapPath string, certDir string, nodeName types.NodeName) error { // Short-circuit if the kubeconfig file already exists. // TODO: inspect the kubeconfig, ensure a rest client can be built from it, verify client cert expiration, etc. _, err := os.Stat(kubeconfigPath) if err == nil { glog.V(2).Infof("Kubeconfig %s exists, skipping bootstrap", kubeconfigPath) return nil } if !os.IsNotExist(err) { glog.Errorf("Error reading kubeconfig %s, skipping bootstrap: %v", kubeconfigPath, err) return err } glog.V(2).Info("Using bootstrap kubeconfig to generate TLS client cert, key and kubeconfig file") bootstrapClientConfig, err := loadRESTClientConfig(bootstrapPath) if err != nil { return fmt.Errorf("unable to load bootstrap kubeconfig: %v", err) } bootstrapClient, err := unversionedcertificates.NewForConfig(bootstrapClientConfig) if err != nil { return fmt.Errorf("unable to create certificates signing request client: %v", err) } success := false // Get the private key. keyPath, err := filepath.Abs(filepath.Join(certDir, defaultKubeletClientKeyFile)) if err != nil { return fmt.Errorf("unable to build bootstrap key path: %v", err) } keyData, generatedKeyFile, err := loadOrGenerateKeyFile(keyPath) if err != nil { return err } if generatedKeyFile { defer func() { if !success { if err := os.Remove(keyPath); err != nil { glog.Warningf("Cannot clean up the key file %q: %v", keyPath, err) } } }() } // Get the cert. certPath, err := filepath.Abs(filepath.Join(certDir, defaultKubeletClientCertificateFile)) if err != nil { return fmt.Errorf("unable to build bootstrap client cert path: %v", err) } certData, err := csr.RequestNodeCertificate(bootstrapClient.CertificateSigningRequests(), keyData, nodeName) if err != nil { return err } if err := certutil.WriteCert(certPath, certData); err != nil { return err } defer func() { if !success { if err := os.Remove(certPath); err != nil { glog.Warningf("Cannot clean up the cert file %q: %v", certPath, err) } } }() // Get the CA data from the bootstrap client config. caFile, caData := bootstrapClientConfig.CAFile, []byte{} if len(caFile) == 0 { caData = bootstrapClientConfig.CAData } // Build resulting kubeconfig. kubeconfigData := clientcmdapi.Config{ // Define a cluster stanza based on the bootstrap kubeconfig. Clusters: map[string]*clientcmdapi.Cluster{"default-cluster": { Server: bootstrapClientConfig.Host, InsecureSkipTLSVerify: bootstrapClientConfig.Insecure, CertificateAuthority: caFile, CertificateAuthorityData: caData, }}, // Define auth based on the obtained client cert. AuthInfos: map[string]*clientcmdapi.AuthInfo{"default-auth": { ClientCertificate: certPath, ClientKey: keyPath, }}, // Define a context that connects the auth info and cluster, and set it as the default Contexts: map[string]*clientcmdapi.Context{"default-context": { Cluster: "default-cluster", AuthInfo: "default-auth", Namespace: "default", }}, CurrentContext: "default-context", } // Marshal to disk if err := clientcmd.WriteToFile(kubeconfigData, kubeconfigPath); err != nil { return err } success = true return nil }