# 玩转 K8s 权限控制：RBAC + kubeconfig 搞定 kubectl 权限管理那些事


## 1. 先抛需求

当一个 K8s 集群需要被多个租户共享时，就涉及到了权限问题，比如你是管理员，这时候你会面临着“给每个用户分配一个 Namespace”类似的需求。

更进一步，可能你需要限制特定用户只能够对集群进行特定的操作，比如不能让“张三”有机会执行 `kubectl delete node <NODE_NAME>` 这种 sao 命令。

在上一篇文章[《通过 VS Code 优雅地编辑 Pod 内的代码（非 NodePort）》](https://www.danielhu.cn/attach-a-pod-via-vscode/)中我们讲到**怎样在不暴露一堆 NodePort 的前提下，通过 VS Code 更新 Pod 内的代码。**VS Code 里去 Attach Pod 其实也是用的本地 kubeconfig 来和 apiserver 通信，进而操作到 Pod。换言之，要实现“多租户通过 VS Code 连接自己的 Pod”这个需求，也涉及到给不同租户创建合适的“最小权限 kubeconfig”。行，今天就来解决这个问题。

## 2. RBAC 配置

整体就两步，先倒腾 RBAC，然后倒腾 kubeconfig。我们先来看 RBAC吧。

### 2.1. K8s 里的 RBAC 机制介绍

先复习下 K8s 里的 RBAC 机制。

(这时候需要用到 GPT 了。)

- **我**：Hello ChatGPT，用中文总结下 K8s 里的 RBAC 是个啥。

- **ChatGPT**:

> 在 Kubernetes 中，RBAC 是一种强大的访问控制机制，用于管理对集群资源的访问权限。RBAC 可以帮助管理员精确地控制用户、ServiceAccount 或其他实体对 Kubernetes API 中资源的操作权限。RBAC 基于角色的授权模型使得管理员可以定义角色和角色绑定，从而实现对不同用户或实体的访问权限控制。
> 
> RBAC 由四个基本概念组成：
> 
> - 角色（Role）：角色定义了一组操作权限，例如对某个命名空间下资源的读取、创建或删除等操作。
> - 角色绑定（RoleBinding）：角色绑定将特定的角色授予 User、Group 或者 ServiceAccount，从而赋予它们相应的权限。
> - 集群角色（ClusterRole）：类似于角色，但作用范围更广，可以授权对整个集群中资源的操作权限。
> - 集群角色绑定（ClusterRoleBinding）：将集群角色绑定给 User、Group 或者 ServiceAccount，授予它们在整个集群范围内的权限。
> 
> 通过 RBAC，管理员可以根据需求精确控制不同用户或实体在集群中的权限范围，避免了不必要的权限泄露或误操作，提高了集群的安全性和管理灵活性。

行，就总结到这里，接着我们要实操了。

### 2.2 创建 ServiceAccount、Role、RoleBinding 和 Secret

1. **ServiceAccount：以用户 user1 为例，来一个 user1-account**

```yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: user1-account
  namespace: default
```

2. **Role：继续来一个 user1-role**

```yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: user1-role
  namespace: default
rules:
- apiGroups: [""]
  resources: ["pods", "pods/exec"]
  verbs: ["get", "list", "watch", "create", "delete", "update", "patch"]
```

这里就可以在 rules 下精细地控制这个角色所能够访问的资源、执行的操作了。如果需要集群维度资源的控制，比如 Node，那就得用到 ClusterRole 了，玩法类似，不赘述。

3. **RoleBinding：把用户和角色绑起来**

```yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: user1-access-default-namespace
  namespace: default
subjects:
- kind: ServiceAccount
  name: user1-account
  namespace: default
roleRef:
  kind: Role
  name: user1-role
  apiGroup: rbac.authorization.k8s.io
```

4. **Secret：在 K8s 1.24 版本之后，ServiceAccount 对应的 Secret 就不会自动创建了。哎，手动搞吧**

```yaml
apiVersion: v1
kind: Secret
metadata:
  name: user1-account-secret
  annotations:
    kubernetes.io/service-account.name: "user1-account"
type: kubernetes.io/service-account-token
```

这个 Secret 创建出来之后，K8s 会自动将 ServiceAccount 对应的 token 写进这个 Secret。

一顿 apply 操作之后，RBAC 相关的几个资源就都有了。前几个没啥好说的，Secret 的关键字段贴一下，大家感受下 K8s 在背地里偷偷干了些啥：

- `kgsec user1-account-secret -o yaml`

```bash
apiVersion: v1
data:
  ca.crt: LS0t……==
  namespace: ZGVmYXVsdA==
  token: ZXlKa……=
kind: Secret
metadata:
  annotations:
    kubernetes.io/service-account.name: user1-account
  name: user1-account-secret
  namespace: default
type: kubernetes.io/service-account-token
```

data 部分，发现了吗，`ca.crt` 和 `token` 信息被填进去了。这两段配置在后面要用到。

## 3. 配置 kubeconfig 文件

user1 的权限配置已经 OK 了，下一步就是将 token 搞到 kubeconfig 里。

### 3.1 kubeconfig 文件介绍

呵呵，老规矩，我这么懒的人，怎么可能自己总结。

- **ChatGPT：**

> kubeconfig 是 Kubernetes 的一个配置文件，它包含了访问 Kubernetes 集群所需的所有信息。这些信息包括集群的地址、用户证书、用户名和命名空间等。kubeconfig 文件使得用户和 CI/CD 工具可以方便地与 Kubernetes 集群进行交互。
>
> kubeconfig 文件通常位于用户的主目录下的 `.kube` 目录中，文件名为 `config`。然而，用户也可以通过设置 `KUBECONFIG` 环境变量来指定其他位置的 kubeconfig 文件。
> 
> kubeconfig 文件主要由以下三部分组成：
> 
> - **集群（clusters）**：定义了要连接的 Kubernetes 集群的信息，包括集群的名称、服务器的地址和 CA 证书。
> 
> - **用户（users）**：定义了用户的认证信息，包括用户的名称、客户端证书和密钥。
> 
> - **上下文（contexts）**：定义了用户和集群之间的关系。一个上下文包含了一个用户和一个集群，以及用户在该集群中的默认命名空间。
> 
> 通过在 kubeconfig 文件中定义不同的上下文，用户可以轻松地在不同的 Kubernetes 集群和命名空间之间切换。

稍显稚嫩，不过将就吧。关于 kubeconfig 是个啥的资料太多了，我就不过多啰嗦了。

贴一个 minikube 默认创建的 kubeconfig 文件：

```yaml
apiVersion: v1
clusters:
- cluster:
    certificate-authority: /Users/danielhu/.minikube/ca.crt
    extensions:
    - extension:
        last-update: Wed, 13 Dec 2023 15:28:46 CST
        provider: minikube.sigs.k8s.io
        version: v1.32.0
      name: cluster_info
    server: https://127.0.0.1:60719
  name: minikube
contexts:
- context:
    cluster: minikube
    extensions:
    - extension:
        last-update: Wed, 13 Dec 2023 15:28:46 CST
        provider: minikube.sigs.k8s.io
        version: v1.32.0
      name: context_info
    namespace: default
    user: minikube
  name: minikube
current-context: minikube
kind: Config
preferences: {}
users:
- name: minikube
  user:
    client-certificate: /Users/danielhu/.minikube/profiles/minikube/client.crt
    client-key: /Users/danielhu/.minikube/profiles/minikube/client.key
```

总之就是 `clusters` 里定义一堆可用的集群信息，`users` 里定义一堆用户信息，然后在 `contexts` 里将 user 和 cluster 关联起来；最后通过切换 `context` 就可以实现切换“用户/集群”了。

### 3.2 具体的 kubeconfig 配置

前面创建的 Secret 里有一个 `token`，将其复制出来备用。（这里要注意，别拿 base64 转码后的结果。`kubectl get -o yaml` 拿到的是 base64 编码的；`kubectl describe` 拿到的是原始的，你可以直接拿原始的，也可以拿编码后的自己 `echo "xxx" | base64 -d` 解码一下。

此外 Secret 里有一个 `ca.crt`，这个 ca 的内容是集群维度统一的，你在其他地方复制也行，不过这里有，顺手拿一个也不打紧。这里需要是 base64 编码后的内容，注意这个细节。

然后准备这样一个 kubeconfig 文件：

```yaml
apiVersion: v1
kind: Config
clusters:
- name: minikube1
  cluster:
    server: https://127.0.0.1:60719
    certificate-authority-data: <ca.crt, base64>

users:
- name: user1-account
  user:
    token: <token>

contexts:
- name: user1-context
  context:
    cluster: minikube1
    namespace: default
    user: user1-account
```

注意几个细节：

1. clusters 下面的 name 记得不要和你系统内默认 config 中的 clusters 重名；
2. server 地址可以抄你的默认 config 里的配置；
3. ca 配置其实也是一样的，你的系统内有，复用配置也行；我这里选择贴一个 base64 版本的内容，为了不引入“外部文件”，方便分发；
4. token 填写前面准备的 token，非 base64。

### 3.3 kubeconfig 切换测试

最简单粗暴的办法，你可以直接备份 $HOME/.kube/config，然后将这个新文件替换进去，这样就能测试新的 kubeconfig 是不是正常工作了。

或者，你可以配置上 KUBECONFIG 环境变量，将新的 kubeconfig 保存路径追加进去。比如 mac 可以这样：

```bash
export KUBECONFIG=/Users/danielhu/.kube/config:/Users/danielhu/Work/test/kubeconfig
```

Windows 系统得用分号（；）。

这时候你再执行 `kubectl config view` 就可以看到一个 merge 之后的 kubeconfig 配置，也就是新的旧的 kubeconfig 都在一起了。

下一步，我推荐你用 [`kubectx`](https://github.com/ahmetb/kubectx)来切换 `context`：

- `kubectx`

```bash
minikube
user1-context
```

- `kubectx user1-context`

OK，这时候就能切换到新的 context 了。接着可以试下是不是只有 pods 能被操作到：

![](1.png)

## 4. 总结

打完收工。没啥好总结的。See you next day.

