// MaybeGenerateServingCerts generates serving certificates if requested and needed. func (c *Config) MaybeGenerateServingCerts(alternateIPs ...net.IP) error { // It would be nice to set a fqdn subject alt name, but only the kubelets know, the apiserver is clueless // alternateDNS = append(alternateDNS, "kubernetes.default.svc.CLUSTER.DNS.NAME") if c.SecureServingInfo != nil && c.SecureServingInfo.ServerCert.Generate { canReadCertAndKey, err := certutil.CanReadCertAndKey(c.SecureServingInfo.ServerCert.CertFile, c.SecureServingInfo.ServerCert.KeyFile) if err != nil { return err } if canReadCertAndKey { return nil } // TODO (cjcullen): Is ClusterIP the right address to sign a cert with? alternateDNS := []string{"kubernetes.default.svc", "kubernetes.default", "kubernetes", "localhost"} if cert, key, err := certutil.GenerateSelfSignedCertKey(c.PublicAddress.String(), alternateIPs, alternateDNS); err != nil { return fmt.Errorf("unable to generate self signed cert: %v", err) } else { if err := certutil.WriteCert(c.SecureServingInfo.ServerCert.CertFile, cert); err != nil { return err } if err := certutil.WriteKey(c.SecureServingInfo.ServerCert.KeyFile, key); err != nil { return err } glog.Infof("Generated self-signed cert (%s, %s)", c.SecureServingInfo.ServerCert.CertFile, c.SecureServingInfo.ServerCert.KeyFile) } } return nil }
func writeKeysAndCert(pkiPath string, name string, key *rsa.PrivateKey, cert *x509.Certificate) error { var ( publicKeyPath = path.Join(pkiPath, fmt.Sprintf("%s-pub.pem", name)) privateKeyPath = path.Join(pkiPath, fmt.Sprintf("%s-key.pem", name)) certificatePath = path.Join(pkiPath, fmt.Sprintf("%s.pem", 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 }
// InitializeTLS checks for a configured TLSCertFile and TLSPrivateKeyFile: if unspecified a new self-signed // certificate and key file are generated. Returns a configured server.TLSOptions object. func InitializeTLS(kc *componentconfig.KubeletConfiguration) (*server.TLSOptions, error) { if kc.TLSCertFile == "" && kc.TLSPrivateKeyFile == "" { kc.TLSCertFile = path.Join(kc.CertDirectory, "kubelet.crt") kc.TLSPrivateKeyFile = path.Join(kc.CertDirectory, "kubelet.key") canReadCertAndKey, err := certutil.CanReadCertAndKey(kc.TLSCertFile, kc.TLSPrivateKeyFile) if err != nil { return nil, err } if !canReadCertAndKey { cert, key, err := certutil.GenerateSelfSignedCertKey(nodeutil.GetHostname(kc.HostnameOverride), nil, nil) if err != nil { return nil, fmt.Errorf("unable to generate self signed cert: %v", err) } if err := certutil.WriteCert(kc.TLSCertFile, cert); err != nil { return nil, err } if err := certutil.WriteKey(kc.TLSPrivateKeyFile, key); err != nil { return nil, err } glog.V(4).Infof("Using self-signed cert (%s, %s)", kc.TLSCertFile, kc.TLSPrivateKeyFile) } } tlsOptions := &server.TLSOptions{ Config: &tls.Config{ // Can't use SSLv3 because of POODLE and BEAST // Can't use TLSv1.0 because of POODLE and BEAST using CBC cipher // Can't use TLSv1.1 because of RC4 cipher usage MinVersion: tls.VersionTLS12, }, CertFile: kc.TLSCertFile, KeyFile: kc.TLSPrivateKeyFile, } if len(kc.Authentication.X509.ClientCAFile) > 0 { clientCAs, err := cert.NewPool(kc.Authentication.X509.ClientCAFile) if err != nil { return nil, fmt.Errorf("unable to load client CA file %s: %v", kc.Authentication.X509.ClientCAFile, err) } // Specify allowed CAs for client certificates tlsOptions.Config.ClientCAs = clientCAs // Populate PeerCertificates in requests, but don't reject connections without verified certificates tlsOptions.Config.ClientAuth = tls.RequestClientCert } return tlsOptions, nil }
func (s *SecureServingOptions) MaybeDefaultWithSelfSignedCerts(publicAddress string, alternateIPs ...net.IP) error { if s == nil { return nil } keyCert := &s.ServerCert.CertKey if s.ServingOptions.BindPort == 0 || len(keyCert.CertFile) != 0 || len(keyCert.KeyFile) != 0 { return nil } keyCert.CertFile = path.Join(s.ServerCert.CertDirectory, s.ServerCert.PairName+".crt") keyCert.KeyFile = path.Join(s.ServerCert.CertDirectory, s.ServerCert.PairName+".key") canReadCertAndKey, err := certutil.CanReadCertAndKey(keyCert.CertFile, keyCert.KeyFile) if err != nil { return err } if !canReadCertAndKey { // TODO: It would be nice to set a fqdn subject alt name, but only the kubelets know, the apiserver is clueless // alternateDNS = append(alternateDNS, "kubernetes.default.svc.CLUSTER.DNS.NAME") // TODO (cjcullen): Is ClusterIP the right address to sign a cert with? alternateDNS := []string{"kubernetes.default.svc", "kubernetes.default", "kubernetes"} // add either the bind address or localhost to the valid alternates bindIP := s.ServingOptions.BindAddress.String() if bindIP == "0.0.0.0" { alternateDNS = append(alternateDNS, "localhost") } else { alternateIPs = append(alternateIPs, s.ServingOptions.BindAddress) } if cert, key, err := certutil.GenerateSelfSignedCertKey(publicAddress, alternateIPs, alternateDNS); err != nil { return fmt.Errorf("unable to generate self signed cert: %v", err) } else { if err := certutil.WriteCert(keyCert.CertFile, cert); err != nil { return err } if err := certutil.WriteKey(keyCert.KeyFile, key); err != nil { return err } glog.Infof("Generated self-signed cert (%s, %s)", keyCert.CertFile, keyCert.KeyFile) } } 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 }