Kubernetes Operator实现用户管理

SuKai January 5, 2022

在Kubernetes里User只是一个用户身份辨识的ID,没有真正用户管理,k8s一般通过第三方提供用户管理和存储,k8s通过User进行身份验证与权限认证。

Kubernetes用户验证支持X509证书认证,token认证和密码验证几种方式。

RBAC是Kubernetes进行权限控制的方式。用户与角色绑定,赋予角色权限。

今天我们来一起看一下Kubesphere如何通过Operator实现kubernetes用户管理。我们在Kubernetes里创建User自定义资源,使用LDAP存储用户帐号信息。通过Kubernets CertificateSigningRequest请求X509证书,生成Kubeconfig。通过各种自定义Role资源来创建Kubernetes Role与用户绑定,分配用户权限。最终用户通过客户端使用kubeconfig来访问Kubernetes资源。

这个场景不像Dex这种Kubernetes OpenID服务,他不需要在Kubernetes APIServer上进行配置,改变Kubernetes集群的部署配置。

代码主要流程

1, User控制器调谐,创建LDAP用户,创建用户KubeConfig的Configmap

2, 在CreateKubeConfig生成kubeconfig用户信息,创建CertificateSigningRequest

3, 在Informer中监听CertificateSigningRequest事件,Approve请求,更新Configmap中用户kubeconfig的证书

代码实现

主入口,创建Kubernetes集群Client,创建Informer,创建controller,在controller的mgr中添加user, kubeconfig自定义资源控制器。

func run(s *options.AIScopeControllerManagerOptions, ctx context.Context) error {
   kubernetesClient, err := k8s.NewKubernetesClient(s.KubernetesOptions)
   if err != nil {
      klog.Errorf("Failed to create kubernetes clientset %v", err)
      return err
   }

   informerFactory := informers.NewInformerFactories(
      kubernetesClient.Kubernetes())

   mgrOptions := manager.Options{
      Port: 8443,
   }

   if s.LeaderElect {
      mgrOptions = manager.Options{
         Port:                    8443,
         LeaderElection:          s.LeaderElect,
         LeaderElectionNamespace: "aiscope-system",
         LeaderElectionID:        "aiscope-controller-manager-leader-election",
         LeaseDuration:           &s.LeaderElection.LeaseDuration,
         RetryPeriod:             &s.LeaderElection.RetryPeriod,
         RenewDeadline:           &s.LeaderElection.RenewDeadline,
      }
   }

   klog.V(0).Info("setting up manager")
   ctrl.SetLogger(klogr.New())

   mgr, err := manager.New(kubernetesClient.Config(), mgrOptions)
   if err != nil {
      klog.Fatalf("unable to set up overall controller manager: %v", err)
   }

   if err = apis.AddToScheme(mgr.GetScheme()); err != nil {
      klog.Fatalf("unable add APIs to scheme: %v", err)
   }

   // register common meta types into schemas.
   metav1.AddToGroupVersion(mgr.GetScheme(), metav1.SchemeGroupVersion)


   kubeconfigClient := kubeconfig.NewOperator(kubernetesClient.Kubernetes(),
      informerFactory.KubernetesSharedInformerFactory().Core().V1().ConfigMaps().Lister(),
      kubernetesClient.Config())
   userController := user.Reconciler{
      MaxConcurrentReconciles: 4,
      KubeconfigClient:        kubeconfigClient,
   }
   if err = userController.SetupWithManager(mgr); err != nil {
      klog.Fatalf("Unable to create user controller: %v", err)
   }

   if err = addControllers(mgr,
      kubernetesClient,
      informerFactory,
      ctx.Done()); err != nil {
      klog.Fatalf("unable to register controllers to the manager: %v", err)
   }

   // Start cache data after all informer is registered
   klog.V(0).Info("Starting cache resource from apiserver...")
   informerFactory.Start(ctx.Done())

   klog.V(0).Info("Starting the controllers.")
   if err = mgr.Start(ctx); err != nil {
      klog.Fatalf("unable to run the manager: %v", err)
   }

   return nil
}

添加Certificatessigningrequest控制器

func addControllers(mgr manager.Manager, client k8s.Client, informerFactory informers.InformerFactory, stopCh <-chan struct{}) error {
   kubernetesInformer := informerFactory.KubernetesSharedInformerFactory()

   csrController := certificatesigningrequest.NewController(client.Kubernetes(),
      kubernetesInformer.Certificates().V1().CertificateSigningRequests(),
      kubernetesInformer.Core().V1().ConfigMaps(), client.Config())

   controllers := map[string]manager.Runnable{
      "csr-controller":                csrController,
   }

   for name, ctrl := range controllers {
      if ctrl == nil {
         klog.V(4).Infof("%s is not going to run due to dependent component disabled.", name)
         continue
      }

      if err := mgr.Add(ctrl); err != nil {
         klog.Error(err, "add controller to manager failed", "name", name)
         return err
      }
   }

   return nil
}

User控制器,调用LDAP用户创建,创建用户kubeconfig configmap

func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reconcile.Result, error) {

   // we do not need to sync ldap info when ldapClient is nil
   if r.LdapClient != nil {
      // ignore errors if timeout
      if err = r.waitForSyncToLDAP(user); err != nil {
         // ignore timeout error
         r.Recorder.Event(user, corev1.EventTypeWarning, failedSynced, fmt.Sprintf(syncFailMessage, err))
      }
   }


   if r.KubeconfigClient != nil {
      // ensure user KubeconfigClient configmap is created
      if err = r.KubeconfigClient.CreateKubeConfig(user); err != nil {
         klog.Error(err)
         r.Recorder.Event(user, corev1.EventTypeWarning, failedSynced, fmt.Sprintf(syncFailMessage, err))
         return ctrl.Result{}, err
      }
   }



   return ctrl.Result{}, nil
}

Kubeconfig,createCSR创建certificatesigningrequest,创建ConfigMap

// CreateKubeConfig Create kubeconfig configmap in KubeSphereControlNamespace for the specified user
func (o *operator) CreateKubeConfig(user *iamv1alpha2.User) error {


   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,
      Preferences: clientcmdapi.Preferences{},
      Clusters: map[string]*clientcmdapi.Cluster{defaultClusterName: {
         Server:                   o.config.Host,
         InsecureSkipTLSVerify:    false,
         CertificateAuthorityData: ca,
      }},
      Contexts: map[string]*clientcmdapi.Context{currentContext: {
         Cluster:   defaultClusterName,
         AuthInfo:  user.Name,
         Namespace: defaultNamespace,
      }},
      CurrentContext: currentContext,
   }

   kubeconfig, err := clientcmd.Write(config)
   if err != nil {
      klog.Error(err)
      return err
   }


   // create a new config
   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 = controllerutil.SetControllerReference(user, cm, scheme.Scheme); err != nil {
      klog.Errorln(err)
      return err
   }

   if _, err = o.k8sClient.CoreV1().ConfigMaps(constants.KubeSphereControlNamespace).Create(context.Background(), cm, metav1.CreateOptions{}); err != nil {
      klog.Errorln(err)
      return err
   }

   return nil
}

在CertificateSigningRequest控制器中Approve批准CertificateSigningRequest请求,UpdateKubeconfig最终调用applyCert更新证书

func (c *Controller) reconcile(key string) error {

   // csr create by kubesphere auto approve
   if username := csr.Labels[constants.UsernameLabelKey]; username != "" {
      err = c.Approve(csr)
      if err != nil {
         klog.Error(err)
         return err
      }
      // certificate data is not empty
      if len(csr.Status.Certificate) > 0 {
         err = c.UpdateKubeconfig(csr)
         if err != nil {
            // kubeconfig not generated
            klog.Error(err)
            return err
         }
         // release
         err := c.k8sclient.CertificatesV1().CertificateSigningRequests().Delete(context.Background(), csr.Name, *metav1.NewDeleteOptions(0))
         if err != nil {
            klog.Error(err)
            return err
         }
      }
   }

   c.recorder.Event(csr, corev1.EventTypeNormal, successSynced, messageResourceSynced)
   return nil
}

更新kubeconfig证书ClientKeyData,ClientCertificateData

func applyCert(cm *corev1.ConfigMap, csr *certificatesv1.CertificateSigningRequest) *corev1.ConfigMap {
   data := []byte(cm.Data[kubeconfigFileName])
   kubeconfig, err := clientcmd.Load(data)
   if err != nil {
      klog.Error(err)
      return cm
   }

   username := getControlledUsername(cm)
   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)
   if err != nil {
      klog.Error(err)
      return cm
   }

   cm.Data[kubeconfigFileName] = string(data)
   return cm
}

通过API创建GlobalRole, GlobalRoleBinding,对用户和角色进行绑定

amOperator := am.NewOperator(s.KubernetesClient.KubeSphere(),
   s.KubernetesClient.Kubernetes(),
   s.InformerFactory,
   s.DevopsClient)
func (am *amOperator) CreateGlobalRoleBinding(username string, role string) error {
   _, err := am.GetGlobalRole(role)
   if err != nil {
      klog.Error(err)
      return err
   }

   roleBindings, err := am.ListGlobalRoleBindings(username)
   if err != nil {
      klog.Error(err)
      return err
   }

   for _, roleBinding := range roleBindings {
      if role == roleBinding.RoleRef.Name {
         return nil
      }
      err := am.ksclient.IamV1alpha2().GlobalRoleBindings().Delete(context.Background(), roleBinding.Name, *metav1.NewDeleteOptions(0))
      if err != nil {
         if errors.IsNotFound(err) {
            continue
         }
         klog.Error(err)
         return err
      }
   }

   globalRoleBinding := iamv1alpha2.GlobalRoleBinding{
      ObjectMeta: metav1.ObjectMeta{
         Name:   fmt.Sprintf("%s-%s", username, role),
         Labels: map[string]string{iamv1alpha2.UserReferenceLabel: username},
      },
      Subjects: []rbacv1.Subject{
         {
            Kind:     rbacv1.UserKind,
            APIGroup: rbacv1.SchemeGroupVersion.Group,
            Name:     username,
         },
      },
      RoleRef: rbacv1.RoleRef{
         APIGroup: iamv1alpha2.SchemeGroupVersion.Group,
         Kind:     iamv1alpha2.ResourceKindGlobalRole,
         Name:     role,
      },
   }

   if _, err := am.ksclient.IamV1alpha2().GlobalRoleBindings().Create(context.Background(), &globalRoleBinding, metav1.CreateOptions{}); err != nil {
      return err
   }

   return nil
}

最后,用户可以获取kubeconfig,通过客户端来访问kubernetes资源。

示例:

创建user cr资源

apiVersion: iam.aiscope/v1alpha2
kind: User
metadata:
  name: user-sukai
spec:
  email: ycsk02@hotmail.com
  password: sukai

User CR调谐后

sukai@sukai:~$ kubectl describe user user-sukai
Name:         user-sukai
Namespace:
Labels:       <none>
Annotations:  iam.aiscope.io/last-password-change-time: 2022-01-06T07:18:14Z
API Version:  iam.aiscope/v1alpha2
Kind:         User
Metadata:
  Creation Timestamp:  2022-01-06T07:18:13Z
  Finalizers:
    finalizers.aiscope.io/users
  Generation:  2
  Managed Fields:
    API Version:  iam.aiscope/v1alpha2
    Fields Type:  FieldsV1
    fieldsV1:
      f:metadata:
        f:annotations:
          f:iam.aiscope.io/last-password-change-time:
        f:finalizers:
          .:
          v:"finalizers.aiscope.io/users":
      f:spec:
        f:password:
    Manager:      controller-manager.exe
    Operation:    Update
    Time:         2022-01-06T07:18:13Z
    API Version:  iam.aiscope/v1alpha2
    Fields Type:  FieldsV1
    fieldsV1:
      f:metadata:
        f:annotations:
      f:spec:
        .:
        f:email:
    Manager:         kubectl-client-side-apply
    Operation:       Update
    Time:            2022-01-06T07:18:13Z
  Resource Version:  7892023
  UID:               59818b17-db3c-428f-9094-addc18a12e07
Spec:
  Email:     ycsk02@hotmail.com
  Password:  $2a$10$pMFmLPNfyjJK71RRL2wQ7uL7mj6NNGm8Yk6bNp/xW7wiZhuw9oQs.
Events:      <none>

创建的csr请求

sukai@sukai:~$ kubectl get csr
NAME                        AGE   SIGNERNAME                            REQUESTOR          REQUESTEDDURATION   CONDITION
user-sukai-csr-1641432770   40m   kubernetes.io/kube-apiserver-client   kubernetes-admin   <none>              Approved,Issued
user-sukai-csr-1641432771   40m   kubernetes.io/kube-apiserver-client   kubernetes-admin   <none>              Approved,Issued
user-sukai-csr-1641432772   40m   kubernetes.io/kube-apiserver-client   kubernetes-admin   <none>              Approved,Issued
user-sukai-csr-1641432774   40m   kubernetes.io/kube-apiserver-client   kubernetes-admin   <none>              Approved,Issued
user-sukai-csr-1641432776   40m   kubernetes.io/kube-apiserver-client   kubernetes-admin   <none>              Approved,Issued
user-sukai-csr-1641432782   40m   kubernetes.io/kube-apiserver-client   kubernetes-admin   <none>              Approved,Issued
user-sukai-csr-1641432792   39m   kubernetes.io/kube-apiserver-client   kubernetes-admin   <none>              Approved,Issued
user-sukai-csr-1641432813   39m   kubernetes.io/kube-apiserver-client   kubernetes-admin   <none>              Approved,Issued

最终生成的kubeconfig configmap

sukai@sukai:~$ kubectl -n aiscope-controls-system get cm kubeconfig-user-sukai -o yaml
apiVersion: v1
data:
  config: |
    apiVersion: v1
    clusters:
    - cluster:
        certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUMvakNDQWVhZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRJeE1USXdNVEEyTWpVeE9Gb1hEVE14TVRFeU9UQTJNalV4T0Zvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTlhiCldpRXlVcm1Ld01RTUxGS25QWXpkaTY5NmJWc0ZiUUhCWFpjN0V4YWFtWXphWE83dEttVDIyWGt5WEZvMElkYmoKOWtnOTQ2YTkwSlRIVmdWUzJWUGFaQ1ZQR3pVblhPVm1TREVrK1NWeXBmYXhyV20yWm1JckNKOUIyNTh0bk1GKwovZkhCRmw2M3JnLzJod3VzUkcrWXdJN1V1VHpoZlp0NmoyRzhaeHdTNUhMR2o5dDB3T1ZjMmgxL1dpMVJHRHpECllON0pKV0c1cURoZXdVaHJIc1JidnhRcmNEb3o0SE9VdjVRSnJSckZDS0UxQjI4SE1PV0FrK0NTZ0FSSFAxM2IKc0IrUU9KUXFaRnRyZUMzYXRoWXJVZGRYUExCcjRYMGNFVnZRbXozVFVFTGNDM205OTRvQkxua0dMak92Sk9vWAp2T0QvMEhBQVhzYXExeUJoV204Q0F3RUFBYU5aTUZjd0RnWURWUjBQQVFIL0JBUURBZ0trTUE4R0ExVWRFd0VCCi93UUZNQU1CQWY4d0hRWURWUjBPQkJZRUZGMm13Skw3VHdycnJVTVVjOHpnMU81TEdzQ0NNQlVHQTFVZEVRUU8KTUF5Q0NtdDFZbVZ5Ym1WMFpYTXdEUVlKS29aSWh2Y05BUUVMQlFBRGdnRUJBSEp1TlJneVlLbWNaSmFPQkNJKwpXS1NNeTlUZWUwNFp0eEVNYmdLZWV4eEhsd2IrZTVUWVZKM1JMOUI0cVJ4YnVPaW9wai9vN09hL3BRMUxGWC9yCnJCUWw0WCtUUktUaGRKU2ZoaS9ZM3pyd1VEVkxOVEF4YklFLyswdS9Ib1FFQ1FWRUIyNGllS3JIeFZUYktNWE8KbVFQZWY4VjduSkFQa1BqTFUvSjBZV2Z2YTBFQ0ZjTi9pVEZYVkJOMndGdWp6OHJFeGNqK2VhYXUwcUNTUzNmVwpqQ1NPZGlBYXF6ZTdiSzEzNENlMGF0a3ArZkFXZSsvZkI4M2xVVjY3ZDIxVk5ERHN6Z081RGJFZitNQ2M2VzRNCkdmS1RXcXpMRVFkU21ndEV0a01RbVNCWWV5ZkpGbUF4KzZ0aHB0djNVOHpRbVRnRlMzTmFwWjZielR3dWZ0REYKak1rPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
        server: https://192.168.10.7:6443
      name: local
    contexts:
    - context:
        cluster: local
        namespace: default
        user: user-sukai
      name: user-sukai@local
    current-context: user-sukai@local
    kind: Config
    preferences: {}
    users:
    - name: user-sukai
      user:
        client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURDakNDQWZLZ0F3SUJBZ0lRTGxHc1BWMVJvUG1uWFVteHdUS1UxakFOQmdrcWhraUc5dzBCQVFzRkFEQVYKTVJNd0VRWURWUVFERXdwcmRXSmxjbTVsZEdWek1CNFhEVEl5TURFd05qQTNNVEkxTjFvWERUSXpNREV3TmpBMwpNVEkxTjFvd0ZURVRNQkVHQTFVRUF4TUtkWE5sY2kxemRXdGhhVENDQVNJd0RRWUpLb1pJaHZjTkFRRUJCUUFECmdnRVBBRENDQVFvQ2dnRUJBS0RFSUFwSENwLzVJYzJZQjg5Rmlsb0tSaVpWWlE3c3lPRE44MktsTXlwYWVCNysKbnA2REJVdTZYTzJId0w5WC8yejgrYmduT0hxZVIyc1h5aXdQYzFGcWkrRG92czhyN3g4RHNEc3l4UlJHODhoaQoxV3h0NEwrSkRucCt0NzJzTUN4cjNWcTlMWklUZFhWMDJGclZIcUw2MnBFRzRWRGd2ZGswTFArSnFhMzU3T3lFCm9hZExTRzNReHpxNGxRVTgzb3VBVCtPamRmM3NyeVJsQ2ZYS2o3N0hWdkhaRHBYSjAySS95aTAxQUN2Z20zWUEKWGJiQUNVQ3Q4eVpBb25aRXQ3Y3RWQUpic0JrZFNQaGFvNDNDZ0duVDZFZ0VuWlBObnoyUVc4VEhsSUdvRkF4VwpUL2lmTW5KdUtnOWs3WE1hSzIvc3VyRVI2VUQ2Zm51QkJWdkJSUnNDQXdFQUFhTldNRlF3RGdZRFZSMFBBUUgvCkJBUURBZ1dnTUJNR0ExVWRKUVFNTUFvR0NDc0dBUVVGQndNQ01Bd0dBMVVkRXdFQi93UUNNQUF3SHdZRFZSMGoKQkJnd0ZvQVVYYWJBa3Z0UEN1dXRReFJ6ek9EVTdrc2F3SUl3RFFZSktvWklodmNOQVFFTEJRQURnZ0VCQUxOKwpRUmY5TDNxMXlEWnAvblpmWnF2dXZDQlV1SGFEWDNCczdKbUlySGU2ZURSdGhMMjVqZVY2VEdId0tFQmdYOHE2Cnd5RzZnVC9uYXViZi9kR2ovYXB6YW9QaWFLa0xlQkd0TEhBK2lzQlVNNzBsRzZLMXdaZzZveVVkT0JLcFM0cGUKMWhROTZjelJRbVViMnVpVE0rK3FBRVBmVlI3bHdGQXNlRVg2WE1XQnZVYzBsNFd1T3VPRXdIcW0xeWpuY0V6WQpPWkVnVGZqQ1lWTWFnSEtzOUk3RmdpaTlvZHZwWkZIWnFLV0gxSVB3R3BmRXVXeG44eURldnhxMnY2Zi9vUlhTCjkyVTBUMkVQSGNZS3NpT0JDTk90R1p2czNpNHBiTFZDa2l1VFlvZzdhczBoWnlrRlpOMXV5WVp0bFhQSkRLZVEKU3Nla1pkbHhQYy8xcHpydG1HMD0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
        client-key-data: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUVvd0lCQUFLQ0FRRUFvTVFnQ2tjS24va2h6WmdIejBXS1dncEdKbFZsRHV6STRNM3pZcVV6S2xwNEh2NmUKbm9NRlM3cGM3WWZBdjFmL2JQejV1Q2M0ZXA1SGF4ZktMQTl6VVdxTDRPaSt6eXZ2SHdPd096TEZGRWJ6eUdMVgpiRzNndjRrT2VuNjN2YXd3TEd2ZFdyMHRraE4xZFhUWVd0VWVvdnJha1FiaFVPQzkyVFFzLzRtcHJmbnM3SVNoCnAwdEliZERIT3JpVkJUemVpNEJQNDZOMS9leXZKR1VKOWNxUHZzZFc4ZGtPbGNuVFlqL0tMVFVBSytDYmRnQmQKdHNBSlFLM3pKa0NpZGtTM3R5MVVBbHV3R1IxSStGcWpqY0tBYWRQb1NBU2RrODJmUFpCYnhNZVVnYWdVREZaUAorSjh5Y200cUQyVHRjeG9yYit5NnNSSHBRUHArZTRFRlc4RkZHd0lEQVFBQkFvSUJBQkgzK2RETVFlN0JiTHJLCi9FeDViRTVHQ2JNdEpqOTF6Ym42cXZKaW5vWmtXRHEyb01uOHdQSEc0YTRXMXo5THVadlg1cDFqbk5kdnEzSFgKMWR2NDJoM0dkOTNxaGJFb0t2RGZlNm9TTVo3amswblphaHRWYUtKZjBrTjB1RnExelNpWjRjTExsRFZZZ1c5ZwpZUEkrRWMrTGxEUlRmWW1KcFE2SzIyQ3daVXV4anlCNkE2YTBZOW1wUVI5QkVVUWZtczMzcXl0YW9YOFZXZ2xFClVjWmRkSUxXV0NrdVpvMTdNcUk5SGdZNnd3WWN3WlJuMmU2VGkxd3h1MHVBR3ozNjhQeTJTaHJ3d3VWM1VRTmcKbkRuTEwwMjZvWWhUZXRnSjZoUVlBN1B6SmdXbXhpekZoOVc0OVJKWlhFL1F3cHJ6TkdWSDEzSUNtUHlXa2lmdAptL3lEWC9FQ2dZRUF4VDE4WjJrL2g4eHFocS85dTYrYWxVazk3TnhSWU1YS2dlOUk5RW91OG9vT0I0MFBzdFJ5CnpXczhhc1hRWWRXK3NVUkZVT0pPMWFEaThicGxGM1RVczU1L3NJVW8rMVFWRjlXOFVNdkVqdzNnV0tFU2hBbnQKeXJTRGNqVENjWmg1RHhHakowT05ndzZMcFQxV3M2M29SN2NkdEVvZ1I2YUVkNVdQWjNQQmNKa0NnWUVBMEtqeQpOSTQzanhJKzFtSjJWTVJiTSt1STZLTVlMWTI2ZXJQSlpQSDhtZklMN2Q0L3ZVUTdYL0ZjTkE0a0tmOE1PTzJpCnBPQ05neTFETmx4VGgvODZ1Ri9iL0xTUHg4OE9DK3pxVU5zKzM5Rmh0ZXd0eVZBY3h0NjkzWUdwU3JsOFZSQVoKU3cyYUJ4RG11WHR6TFpWU1dFdE9TSDdZN0MxNG4wSHY4VGhNajlNQ2dZRUFtSytoVnpnOWF2V09YVmR0MTFYNApGNnJNR2tqdllqZWJMWHk4QUFoUlVZWVhtRGJWdVQwRzVnZ21qQTAzNUJTZit2LzduTUtqL25IK1hOeExGNTVrCmJldTdzejFSM1VWWTBzdXRiT3BnN2REekpBa0VtVnhLVFVueUczM0dMRU81S3pZZkUrMFNaaXJqWlhZWFlSNjIKR3BZaGs0aHlkcVRzRk5xZFdadGRXcmtDZ1lBd3BKSUNLbjFOUHlXaStNVTVNYVZKVDBsVlltQUtqcFhBY1JVcwpFVFdmOWN0T0lwZWRXY0MxdHlDVmlnNW9NK3IzZ241K0RWTXdGMmNwenhBeURnLzBWM1NEVHR5TjZma09VcWExClBzZERpaDVMT01uYnVtOWE5U1l4OHo0eUMxZXV4TmdBcFNVWkxKbDgrQWg3d2VtMlo2ZlNRcS96THc2Rm9ldDcKd25JbXZRS0JnQjFKY2VVZXM5QURjd2NwTldna21teFZXNkN5UVVJTFRxUHVEbFY2RE95TDJNaTNXeXNGamphawpvU05maWxoVGxaOTJ1M0NsTDlQQnBWVDF2eE1kOFA5K0FzdFlPVlpCcHRNU1IvRDJUdms4WnZGYjV4b3ZWUkQwCjVqVk1GN094WXlCRktMVVlTL2Z1cStaNVhpSmdqUFhzRUpyZ3V0STRsZURtWkF4bnBQWFEKLS0tLS1FTkQgUFJJVkFURSBLRVktLS0tLQo=
kind: ConfigMap
metadata:
  creationTimestamp: "2022-01-06T07:18:13Z"
  labels:
    aiscope.io/username: user-sukai
  managedFields:
  - apiVersion: v1
    fieldsType: FieldsV1
    fieldsV1:
      f:data:
        .: {}
        f:config: {}
      f:metadata:
        f:labels:
          .: {}
          f:aiscope.io/username: {}
        f:ownerReferences:
          .: {}
          k:{"uid":"59818b17-db3c-428f-9094-addc18a12e07"}: {}
    manager: controller-manager.exe
    operation: Update
    time: "2022-01-06T07:18:13Z"
  name: kubeconfig-user-sukai
  namespace: aiscope-controls-system
  ownerReferences:
  - apiVersion: iam.aiscope/v1alpha2
    blockOwnerDeletion: true
    controller: true
    kind: User
    name: user-sukai
    uid: 59818b17-db3c-428f-9094-addc18a12e07
  resourceVersion: "7892042"
  uid: b08afeb8-2a00-4b04-9ea2-b9111d8c80c6

使用kubeconfig来请求Kubernetes资源,已经能够通过kubeconfig访问到Kubernetes API,这里因为没有进行Role绑定,所以被权限禁止。

sukai@sukai:~/.kube$ kubectl --kubeconfig config.test-sukai get all
Error from server (Forbidden): pods is forbidden: User "user-sukai" cannot list resource "pods" in API group "" in the namespace "default"
Error from server (Forbidden): replicationcontrollers is forbidden: User "user-sukai" cannot list resource "replicationcontrollers" in API group "" in the namespace "default"
Error from server (Forbidden): services is forbidden: User "user-sukai" cannot list resource "services" in API group "" in the namespace "default"
Error from server (Forbidden): daemonsets.apps is forbidden: User "user-sukai" cannot list resource "daemonsets" in API group "apps" in the namespace "default"
Error from server (Forbidden): deployments.apps is forbidden: User "user-sukai" cannot list resource "deployments" in API group "apps" in the namespace "default"
Error from server (Forbidden): replicasets.apps is forbidden: User "user-sukai" cannot list resource "replicasets" in API group "apps" in the namespace "default"
Error from server (Forbidden): statefulsets.apps is forbidden: User "user-sukai" cannot list resource "statefulsets" in API group "apps" in the namespace "default"
Error from server (Forbidden): horizontalpodautoscalers.autoscaling is forbidden: User "user-sukai" cannot list resource "horizontalpodautoscalers" in API group "autoscaling" in the namespace "default"
Error from server (Forbidden): cronjobs.batch is forbidden: User "user-sukai" cannot list resource "cronjobs" in API group "batch" in the namespace "default"
Error from server (Forbidden): jobs.batch is forbidden: User "user-sukai" cannot list resource "jobs" in API group "batch" in the namespace "default"
sukai@sukai:~/.kube$
sukai@sukai:~/.kube$