1. 前言《好好学K8S:K8S中用户账户的RBAC鉴权》 一文中,实现了用户账户(UserAccount)的授权方法和访问K8S的方法。 本文中,我们主要学习服务账户(ServiceAccount)的授权方法和访问K8S的方法。
2. 服务账户授权2.1. 账户授权概述UserAccount授权的方法,是把UserAccount通过 RoleBinding/ClusterRoleBinding 和 Role/ClusterRole 绑定。
ServiceAccount授权的方法,与UserAccount授权的方法相同:把ServiceAccount通过 RoleBinding/ClusterRoleBinding 和 Role/ClusterRole 绑定。
因此,ServiceAccount与UserAccount的授权命令基本相同,只有几个参数的差异。授权方法完全可以参考《好好学K8S:K8S中用户账户的RBAC鉴权》 。
创建账户的方法,参考文档《为K8S创建用户账户和服务账户》 假设当前我们有一个用户账户jane
,和一个服务账户default:harkin
。下面我们对比一下用户账户和服务账户授权的差异。
2.2. 命令实现授权2.2.1. 用户账户授权例如:用户账户jane,授权default namespace下的pod查看和创建权限。
1 2 3 kubectl create role --help kubectl create role developer --resource=pods --verb=list,create -n default kubectl create rolebinding dev-user-binding --role=developer --user=jane -n default
2.2.2. 服务账户授权例如:default namespace下的sa账户harkin,授权default namespace下的pod查看和创建权限。
1 2 3 kubectl create role --help kubectl create role developer --resource=pods --verb=list,get,create -n default kubectl create rolebinding dev-user-binding --role=developer --serviceaccount=default:harkin -n default
2.3. manifest实现授权2.3.1. 用户账户授权1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 --- kind: Role apiVersion: rbac.authorization.k8s.io/v1 metadata: namespace: default name: developer rules: - apiGroups: ["" ] resources: ["pods" ] verbs: ["list" , "get" , "create" ] --- kind: RoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: dev-user-binding subjects: - kind: User name: jane apiGroup: rbac.authorization.k8s.io roleRef: kind: Role name: developer apiGroup: rbac.authorization.k8s.iomaster
2.3.2. 服务账户授权1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 --- kind: Role apiVersion: rbac.authorization.k8s.io/v1 metadata: namespace: default name: developer rules: - apiGroups: ["" ] resources: ["pods" ] verbs: ["list" , "get" , "create" ] --- kind: RoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: dev-user-binding subjects: - kind: ServiceAccount name: harkin namespace: default roleRef: kind: Role name: developer apiGroup: rbac.authorization.k8s.io
3. 用户账户访问K8S集群想要让Pod使用ServiceAccount访问K8S APIServer,基本步骤如下:
1、向Pod分配ServiceAccount
1 2 3 4 5 6 7 8 9 apiVersion: v1 kind: Pod metadata: name: my-pod spec: serviceAccountName: harkin containers: - name: my-container image: myimage
当Pod启动时,Kubernetes会自动挂载ServiceAccount的凭据到Pod里运行的容器中。 ServiceAccount的JWT令牌和CA证书会被自动挂载到Pod的/var/run/secrets/kubernetes.io/serviceaccount
路径下。
Token的路径: /var/run/secrets/kubernetes.io/serviceaccount/token
CA证书的路径: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
2、使用ServiceAccount的令牌访问APIServer 当Pod使用ServiceAccount时,容器内的应用程序可以使用这些凭据(Token和CA证书)来与Kubernetes API服务器进行认证和通信。
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 package mainimport ( "context" "fmt" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" ) func main () { var kubeconfig *rest.Config var err error if kubeconfig, err = rest.InClusterConfig(); err != nil { panic (err.Error()) } clientset, err := kubernetes.NewForConfig(kubeconfig) if err != nil { panic (err.Error()) } pods, err := clientset.CoreV1().Pods("default" ).List(context.Background(), metav1.ListOptions{}) if err != nil { panic (err.Error()) } fmt.Printf("There are %d pods in the default namespace\n" , len (pods.Items)) for _, pod := range pods.Items { fmt.Printf("Pod name: %s in namespace: %s\n" , pod.Name, pod.Namespace) } }
在这个Go程序中,它使用rest.InClusterConfig()获取集群内的配置,这个过程会自动从挂载的ServiceAccount令牌和CA证书位置读取信息。然后,创建一个Clientset来查询集群内default namespace下的所有Pod。
4. 手动指定Token访问K8S集群在 Go 程序中,如果我们不是从集群内的 Pod 访问 Kubernetes API 而是想手动指定 Token 和 CA 证书文件的路径(例如在集群外部或在特殊情况下访问),我们可以使用 client-go
库的 rest
包来自定义配置。
以下是一个示例代码,它展示了如何在 Go 中手动配置访问 Kubernetes API 的客户端,包括指定 Token 和 CA 证书路径:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 package mainimport ( "context" "fmt" "os" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" ) func main () { tokenPath := "/Users/vk/tmp/serviceaccount/token" caPath := "/Users/vk/tmp/serviceaccount/ca.crt" host := "https://192.168.50.133:6443" token, err := os.ReadFile(tokenPath) if err != nil { panic (err.Error()) } caCert, err := os.ReadFile(caPath) if err != nil { panic (err.Error()) } config := &rest.Config{ Host: host, TLSClientConfig: rest.TLSClientConfig{ CAData: caCert, }, BearerToken: string (token), } clientset, err := kubernetes.NewForConfig(config) if err != nil { panic (err.Error()) } pods, err := clientset.CoreV1().Pods("default" ).List(context.Background(), metav1.ListOptions{}) if err != nil { panic (err.Error()) } fmt.Printf("There are %d pods in the default namespace\n" , len (pods.Items)) for _, pod := range pods.Items { fmt.Printf("Pod name: %s in namespace: %s\n" , pod.Name, pod.Namespace) } }
在上面的代码中,我们首先从指定路径读取了服务帐户的 Token 和 CA 证书文件,然后创建了一个包含这些数据的 rest.Config
结构。之后,使用这个配置创建了 kubernetes.Clientset
对象,用来执行对 Kubernetes API Server 的请求。
请确保将 tokenPath
、caPath
和 host
替换为我们实际环境中的正确路径和API服务器地址,并确保提供的 ServiceAccount Token 具有足够的权限执行我们希望进行的 API 调用。
最后,使用 clientset
对象来访问 Kubernetes API,例如列出集群中的 Pods。记得处理任何可能发生的错误。这种方式的访问更多用于开发和调试目的,因为它涉及到手动管理敏感的凭据。在生产环境中建议使用标准的服务帐户令牌,以确保安全。