一个计算机技术爱好者与学习者

0%

好好学K8S:K8S中服务账户的RBAC鉴权

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 main

import (
"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

// load token and ca from path /var/run/secrets/kubernetes.io/serviceaccount
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 main

import (
"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" // 指定Token文件的路径
caPath := "/Users/vk/tmp/serviceaccount/ca.crt" // 指定CA证书文件的路径
host := "https://192.168.50.133:6443" // Kubernetes API Server 地址

// 读取ServiceAccount的Token
token, err := os.ReadFile(tokenPath)
if err != nil {
panic(err.Error())
}

// 读取CA证书文件
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 的请求。

请确保将 tokenPathcaPathhost 替换为我们实际环境中的正确路径和API服务器地址,并确保提供的 ServiceAccount Token 具有足够的权限执行我们希望进行的 API 调用。

最后,使用 clientset 对象来访问 Kubernetes API,例如列出集群中的 Pods。记得处理任何可能发生的错误。这种方式的访问更多用于开发和调试目的,因为它涉及到手动管理敏感的凭据。在生产环境中建议使用标准的服务帐户令牌,以确保安全。

  • 本文作者: 好好学习的郝
  • 原文链接: https://www.voidking.com/dev-k8s-sa-rbac-auth/
  • 版权声明: 本文采用 BY-NC-SA 许可协议,转载请注明出处!源站会即时更新知识点并修正错误,欢迎访问~
  • 微信公众号同步更新,欢迎关注~