diff --git a/pkg/controller/certificatesigningrequest/certificatesigningrequest_controller.go b/pkg/controller/certificatesigningrequest/certificatesigningrequest_controller.go index 533ecf68575c569dd62419c732e033d1ed489d9a..f23b9b24737d2e59ff5b711db0871f53822b8f35 100644 --- a/pkg/controller/certificatesigningrequest/certificatesigningrequest_controller.go +++ b/pkg/controller/certificatesigningrequest/certificatesigningrequest_controller.go @@ -220,6 +220,7 @@ func (c *Controller) reconcile(key string) error { if len(csr.Status.Certificate) > 0 { err = c.UpdateKubeconfig(csr) if err != nil { + // kubeconfig not generated klog.Error(err) return err } @@ -258,7 +259,6 @@ func (c *Controller) Approve(csr *certificatesv1beta1.CertificateSigningRequest) // approve csr csr, err := c.k8sclient.CertificatesV1beta1().CertificateSigningRequests().UpdateApproval(csr) - if err != nil { klog.Errorln(err) return err @@ -269,12 +269,9 @@ func (c *Controller) Approve(csr *certificatesv1beta1.CertificateSigningRequest) func (c *Controller) UpdateKubeconfig(csr *certificatesv1beta1.CertificateSigningRequest) error { username := csr.Labels[constants.UsernameLabelKey] - - err := c.kubeconfigOperator.UpdateKubeconfig(username, csr.Status.Certificate) - + err := c.kubeconfigOperator.UpdateKubeconfig(username, csr) if err != nil { klog.Error(err) } - return err } diff --git a/pkg/models/kubeconfig/kubeconfig.go b/pkg/models/kubeconfig/kubeconfig.go index 35b31f9e7da471d00776c3f995c8219124b0ae48..dd2caafb1ba700dd80b89058fe4fec5e1f591f54 100644 --- a/pkg/models/kubeconfig/kubeconfig.go +++ b/pkg/models/kubeconfig/kubeconfig.go @@ -44,7 +44,7 @@ import ( ) const ( - inClusterCAFilePath = "/run/secrets/kubernetes.io/serviceaccount/ca.crt" + inClusterCAFilePath = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt" configMapPrefix = "kubeconfig-" kubeconfigNameFormat = configMapPrefix + "%s" defaultClusterName = "local" @@ -52,12 +52,13 @@ const ( kubeconfigFileName = "config" configMapKind = "ConfigMap" configMapAPIVersion = "v1" + privateKeyAnnotation = "kubesphere.io/private-key" ) type Interface interface { GetKubeConfig(username string) (string, error) CreateKubeConfig(user *iamv1alpha2.User) error - UpdateKubeconfig(username string, certificate []byte) error + UpdateKubeconfig(username string, csr *certificatesv1beta1.CertificateSigningRequest) error } type operator struct { @@ -76,11 +77,8 @@ func NewReadOnlyOperator(configMapInformer corev1informers.ConfigMapInformer, ma } func (o *operator) CreateKubeConfig(user *iamv1alpha2.User) error { - configName := fmt.Sprintf(kubeconfigNameFormat, user.Name) - _, err := o.configMapInformer.Lister().ConfigMaps(constants.KubeSphereControlNamespace).Get(configName) - // already exist if err == nil { return nil @@ -104,15 +102,12 @@ func (o *operator) CreateKubeConfig(user *iamv1alpha2.User) error { } } - clientKey, err := o.createCSR(user.Name) - - if err != nil { + if err = o.createCSR(user.Name); err != nil { klog.Errorln(err) return err } currentContext := fmt.Sprintf("%s@%s", user.Name, defaultClusterName) - config := clientcmdapi.Config{ Kind: configMapKind, APIVersion: configMapAPIVersion, @@ -122,9 +117,6 @@ func (o *operator) CreateKubeConfig(user *iamv1alpha2.User) error { InsecureSkipTLSVerify: false, CertificateAuthorityData: ca, }}, - AuthInfos: map[string]*clientcmdapi.AuthInfo{user.Name: { - ClientKeyData: clientKey, - }}, Contexts: map[string]*clientcmdapi.Context{currentContext: { Cluster: defaultClusterName, AuthInfo: user.Name, @@ -134,26 +126,29 @@ func (o *operator) CreateKubeConfig(user *iamv1alpha2.User) error { } kubeconfig, err := clientcmd.Write(config) - if err != nil { klog.Error(err) return err } - cm := &corev1.ConfigMap{TypeMeta: metav1.TypeMeta{Kind: configMapKind, APIVersion: configMapAPIVersion}, - ObjectMeta: metav1.ObjectMeta{Name: configName, Labels: map[string]string{constants.UsernameLabelKey: user.Name}}, - Data: map[string]string{kubeconfigFileName: string(kubeconfig)}} - - err = controllerutil.SetControllerReference(user, cm, scheme.Scheme) + cm := &corev1.ConfigMap{ + TypeMeta: metav1.TypeMeta{ + Kind: configMapKind, + APIVersion: configMapAPIVersion, + }, + ObjectMeta: metav1.ObjectMeta{ + Name: configName, + Labels: map[string]string{constants.UsernameLabelKey: user.Name}, + }, + Data: map[string]string{kubeconfigFileName: string(kubeconfig)}, + } - if err != nil { + if err = controllerutil.SetControllerReference(user, cm, scheme.Scheme); err != nil { klog.Errorln(err) return err } - _, err = o.k8sClient.CoreV1().ConfigMaps(constants.KubeSphereControlNamespace).Create(cm) - - if err != nil { + if _, err = o.k8sClient.CoreV1().ConfigMaps(constants.KubeSphereControlNamespace).Create(cm); err != nil { klog.Errorln(err) return err } @@ -170,23 +165,19 @@ func (o *operator) GetKubeConfig(username string) (string, error) { } data := []byte(configMap.Data[kubeconfigFileName]) - kubeconfig, err := clientcmd.Load(data) - if err != nil { klog.Errorln(err) return "", err } masterURL := o.masterURL - // server host override if cluster := kubeconfig.Clusters[defaultClusterName]; cluster != nil && masterURL != "" { cluster.Server = masterURL } data, err = clientcmd.Write(*kubeconfig) - if err != nil { klog.Errorln(err) return "", err @@ -195,55 +186,49 @@ func (o *operator) GetKubeConfig(username string) (string, error) { return string(data), nil } -func (o *operator) createCSR(username string) ([]byte, error) { +func (o *operator) createCSR(username string) error { csrConfig := &certutil.Config{ CommonName: username, Organization: nil, AltNames: certutil.AltNames{}, Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, } + x509csr, x509key, err := pkiutil.NewCSRAndKey(csrConfig) if err != nil { klog.Errorln(err) - return nil, err + return err } var csrBuffer, keyBuffer bytes.Buffer - - err = pem.Encode(&keyBuffer, &pem.Block{Type: "PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(x509key)}) - - if err != nil { + if err = pem.Encode(&keyBuffer, &pem.Block{Type: "PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(x509key)}); err != nil { klog.Errorln(err) - return nil, err + return err } - csrBytes, err := x509.CreateCertificateRequest(rand.Reader, x509csr, x509key) - - if err != nil { + var csrBytes []byte + if csrBytes, err = x509.CreateCertificateRequest(rand.Reader, x509csr, x509key); err != nil { klog.Errorln(err) - return nil, err + return err } - err = pem.Encode(&csrBuffer, &pem.Block{Type: "CERTIFICATE REQUEST", Bytes: csrBytes}) - - if err != nil { + if err = pem.Encode(&csrBuffer, &pem.Block{Type: "CERTIFICATE REQUEST", Bytes: csrBytes}); err != nil { klog.Errorln(err) - return nil, err + return err } csr := csrBuffer.Bytes() key := keyBuffer.Bytes() - csrName := fmt.Sprintf("%s-csr-%d", username, time.Now().Unix()) - k8sCSR := &certificatesv1beta1.CertificateSigningRequest{ TypeMeta: metav1.TypeMeta{ Kind: "CertificateSigningRequest", APIVersion: "certificates.k8s.io/v1beta1", }, ObjectMeta: metav1.ObjectMeta{ - Name: csrName, - Labels: map[string]string{constants.UsernameLabelKey: username}, + Name: csrName, + Labels: map[string]string{constants.UsernameLabelKey: username}, + Annotations: map[string]string{privateKeyAnnotation: string(key)}, }, Spec: certificatesv1beta1.CertificateSigningRequestSpec{ Request: csr, @@ -254,17 +239,16 @@ func (o *operator) createCSR(username string) ([]byte, error) { } // create csr - k8sCSR, err = o.k8sClient.CertificatesV1beta1().CertificateSigningRequests().Create(k8sCSR) - - if err != nil { + if _, err = o.k8sClient.CertificatesV1beta1().CertificateSigningRequests().Create(k8sCSR); err != nil { klog.Errorln(err) - return nil, err + return err } - return key, nil + return nil } -func (o *operator) UpdateKubeconfig(username string, certificate []byte) error { +// Update client key and client certificate after CertificateSigningRequest has been approved +func (o *operator) UpdateKubeconfig(username string, csr *certificatesv1beta1.CertificateSigningRequest) error { configName := fmt.Sprintf(kubeconfigNameFormat, username) configMap, err := o.k8sClient.CoreV1().ConfigMaps(constants.KubeSphereControlNamespace).Get(configName, metav1.GetOptions{}) if err != nil { @@ -272,7 +256,7 @@ func (o *operator) UpdateKubeconfig(username string, certificate []byte) error { return err } - configMap = appendCert(configMap, certificate) + configMap = applyCert(configMap, csr) _, err = o.k8sClient.CoreV1().ConfigMaps(constants.KubeSphereControlNamespace).Update(configMap) if err != nil { klog.Errorln(err) @@ -281,33 +265,31 @@ func (o *operator) UpdateKubeconfig(username string, certificate []byte) error { return nil } -func appendCert(cm *corev1.ConfigMap, cert []byte) *corev1.ConfigMap { +func applyCert(cm *corev1.ConfigMap, csr *certificatesv1beta1.CertificateSigningRequest) *corev1.ConfigMap { data := []byte(cm.Data[kubeconfigFileName]) - kubeconfig, err := clientcmd.Load(data) - - // ignore if invalid format if err != nil { - klog.Warning(err) + klog.Error(err) return cm } username := getControlledUsername(cm) - - if kubeconfig.AuthInfos[username] != nil { - kubeconfig.AuthInfos[username].ClientCertificateData = cert + privateKey := csr.Annotations[privateKeyAnnotation] + clientCert := csr.Status.Certificate + kubeconfig.AuthInfos = map[string]*clientcmdapi.AuthInfo{ + username: { + ClientKeyData: []byte(privateKey), + ClientCertificateData: clientCert, + }, } data, err = clientcmd.Write(*kubeconfig) - - // ignore if invalid format if err != nil { - klog.Warning(err) + klog.Error(err) return cm } cm.Data[kubeconfigFileName] = string(data) - return cm }