# 简介

K8S 中有两种用户 (User)—— 服务账号 (ServiceAccount) 和普通意义上的用户 (User)
ServiceAccount 是由 K8S 管理的,相当于系统账号。而 User 通常是在我们添加的,但 K8S 不存储用户列表 —— 也就是说,添加 / 编辑 / 删除用户都是在外部进行,无需与 K8S API 交互,虽然 K8S 并不管理用户,但是在 K8S 接收 API 请求时,是可以认知到发出请求的用户的。

而这里我主要介绍的是 User 普通用户。
通过以上介绍,我们知道了普通用户是不需要 api-server 存储的,那 api-server 是怎么判断用户是否为合法用户和它具有的权限的呢?我们带着这个问题往下看。

# api-service 用户验证(用户识别)

尽管 K8S 认知用户靠的只是用户的名字,但是只需要一个名字就能请求 K8S 的 API 显然是不合理的,所以依然需要验证此用户的身份

而 api-server 去验证用户主要通过以下几种情况:
这三种认证资料的生成都是和证书密不可分的。
・X509 客户端证书
客户端证书验证通过为 API Server 指定 --client-ca-file=xxx 选项启用,API Server 通过此 ca 文件来验证 API 请求携带的客户端证书的有效性,一旦验证成功,API Server 就会将客户端证书 Subject 里的 CN 属性作为此次请求的用户名
・静态 token 文件
通过指定 --token-auth-file=SOMEFILE 选项来启用 bearer token 验证方式,引用的文件是一个包含了 token, 用户名,用户 ID 的 csv 文件 请求时,带上 Authorization: Bearer 31ada4fd-adec-460c-809a-9e56ceb75269 头信息即可通过 bearer token 验证
・静态密码文件
通过指定 --basic-auth-file=SOMEFILE 选项启用密码验证,类似的,引用的文件时一个包含 密码,用户名,用户 ID 的 csv 文件 请求时需要将 Authorization 头设置为 Basic BASE64ENCODED (USER:PASSWORD)

这里我们已经解答了前面的 api-server 是怎么识别用户的,但是这里又诞生了一个问题,api-server 怎么通过证书或者说 token、密钥文件来识别出用户是合法用户而不是假冒的用户?毕竟 api-server 理论上没保存用户的列表。
这里就涉及到用户的生成方式了。

# 用户的创建

为了让 api-server 能够准确的识别到用户为合法用户而不是假冒的,用户的证书在创建时需要经过 api-server 的 ca 进行签发。也就是说 api-server 签发过的证书就是合法用户。
其实就是 api-server 是第三方证书颁发机构的同时也是服务端。

# 生成一个私钥(RSA 算法私钥,我们访问 k8s 的凭证)

openssl genrsa -out test.key 2048

image-20220303155034528

# 创建证书签名请求文件(预签发)

使用上述得到的私钥创建一个 csr (证书签名请求) 文件,其中我们需要在 subject 里带上用户信息 (CN 为用户名,O 为用户组)
这个用户名就是我们要创建的用户。用户组其实不重要,只是我们给这个证书分的组而已,在集群中没有任何意义。

openssl req -new -key test.key -out test.csr -subj "/CN=test/O=RUYUE" # 生成 crt,并使用我们的私钥进行签名

# 使用 ca 证书进行签发(给用户颁发证书)

这里主要就是使用 api-server 的 ca 证书去签发用户证书。
找到 api-server 的证书文件,在 api-server 的安装节点的 /etc/kubernetes/pki/ 路径下,会有两个文件,一个是 CA 证书 (ca.crt),一个是 CA 私钥 (ca.key)

image-20220303155042234

openssl x509 -req -in test.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out test.crt -days 365 #提取用户信息,并加上其他信息(比如颁发者等信息),用 CA 的私钥签发数字证书。

image-20220303155047446

# k8s 用户的权限控制 (RBAC) 介绍

至此,我们已经解决了最初的两个问题之一(api-server 是怎么识别用户的),还剩下一个权限问题。

RBAC(Role-Based Access Control,基于角色的访问控制)在 Kubernetes 的 1.5 版本中引入,在 1.6 版本时升级为 Beta 版本,在 1.8 版本时升级为 GA。作为 kubeadm 安装方式的默认选项,足见其重要程度。

# RBAC 的概念

RBAC 引入了 4 个新的顶级资源对象: Role, ClusterRole,RoleBinding 和 ClusterRoleBinding. 同其他 API 资源对象一样,用户可以使用 kubectl 或者 API 调用等方式操作这些资源对象.

• Role: 角色,它定义了一组规则,定义了一组对Kubernetes API对象的操作权限
• Subject: 被作用者,既可以是”人”,也可以是机器,当然也可以是我们Kubernetes中定义的用户
• RoleBinding: 定义了”被作用者”和”角色”的绑定关系

# 角色(Role)

其实就相当于我给这个资源定义了一个组(角色),然后后续可以通过用户绑定实现这个组内的用户(subject)可以对这个资源做什么操作(还需要绑定)。

一个角色就是一组权限的集合,这里的权限都是许可形式的,不存在拒绝的规则,在一个命名空间中,可以用角色来定义一个角色,如果是集群级别的,就需要使用 ClusterRole 了.

角色只能对命名空间内的资源进行授权,在下面例子中定义的角色具备读取 Pod 的权限:
实际上,Role 本身就是一个 kubernetes 的 API 对象,定义文件如下:

kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  namespace: test #这个Role作用的命名空间
  name: test-role 
rules:
- apiGroups: [""]  # ""空字符串,表示核心API群,支持的API组列表,例如"“apiVersion: batch/v1”“apiVersion: extensions:v1beta1”“apiVersion: apps/v1beta1”等”
  resources: ["pods"] #支持的资源对象列表,例如Pods,deployments,jobs等
  verbs: ["get", "watch", "list"]  #对资源对象的操作方法列表,例如get,watch,list,delete,replace,patch等

# 集群角色(ClusterRole)

本质上也是角色,只是它相当于更加广泛的角色,是集群级别的,不再是命名空间那么细了。
集群角色除了具有和角色一致的命名空间内资源的管理能力,因其集群级别的范围,还可以用于以下特殊元素的授权.
主要用来绑定以下资源:

  • 集群范围的资源,例如 Node.
  • 非资源型的路径,例如 "/ealthz"
  • 包含全部命名空间的资源,例如 Pods (用于 kubectl get pods --all-namespaces 这样的操作授权)
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  # ClusterRole 不受限于命名空间,所以无需设置namespace的名称
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "watch", "list"]

# 角色绑定(Rolebinding)和集群角色绑定(ClusterRolebinding)

其实就是将指定的角色给绑定到一个目标上,绑定目标可以是 User(用户)、Group(组)或者 Service Account,相当于给一个或一组对象进行赋予权限。

使用 RoleBinding 为某个命名空间授权,使用 ClusterRoleBinding 为集群范围内授权。
RoleBinding 可以引用 Role 进行授权。下面的例子中的 RoleBinding 将在 default 命名空间中把 pod-reader 角色授予用户 jane,这一操作可以让 jane 读取 default 命名空间中的 Pod:

kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: example-rolebinding
  namespace: default
subjects:
- kind: User
  name: youmen
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: example-role
  apiGroup: rbac.authorization.k8s.io

在 Rolebinding 中定义了一个 subject 字段,即” 被作用者”。它的类型是 User,即 Kubernetes 里的用户,名称为 youmen
在 kubernetes 里的 User,也就是用户,只是一个授权系统里的逻辑概念。它需要通过外部认证服务,比如 Keystone,来提供。或者直接给 APIServer 指定一个用户名、密码文件。那么 kubernetes 的授权系统就能够从这个文件里找到对象的用户.
Rolebinding 对象通过 roleRef 字段可以直接通过名字,来引用前面定义的 Role 对象 (example-role),从而定义了” 被作用者 (Subject)” 和” 角色 (Role)” 之间的绑定关系
Role 和 RoleBinding 他们的权限限制规则仅仅在他们自己的 namespace 内有效,roleRef 也只能引用当前 namespace 里的 Role 对象
“RoleBinding 也可以引用 ClusterRole,对属于同一命名空间内 ClusterRole 定义的资源主体进行授权。一种常见的做法是集群管理员为集群范围预先定义好一组 ClusterRole,然后在多个命名空间中重复使用这些 ClusterRole。

kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: read-secrets
  namespace: development
  # 集群角色中,只有development命名空间的权限才能赋予davesubjects
subjects:
- kind: User
  name: dave
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: secret-reader
  apiGroup: rbac.authorization.k8s.io

# 给 test 用户授权(绑定资源组)

前面我们已经学习了 k8s 的权限控制,这里我们可以实践下,这里我们目标是让我们新建的 test 用户有 php 这个命名空间的所有权限。构造如下 role 和 rolebinding 即可。

# 赋予指定命名空间权限

创建如下 role 和 RoleBinding,则赋予了 test 这个账号对 php 这个命名空间的管理权限。

kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  namespace: php
  name: test_role
rules:
- apiGroups: [""]
  resources: ["*"]
  verbs: ["*"]
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: test_rolebinding
  namespace: php
subjects:
- kind: User
  name: test
roleRef:
  kind: Role
  name: test_role
  apiGroup: rbac.authorization.k8s.io

# 赋予所有命名空间权限(相当于让 test 变成超管)

k8s 中存在很多默认的 role,cluster-admin 就是其中一个。

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: eviltest
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: User
  name: test
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin

image-20220303155729464

# 使用 test 用户访问指定资源

前面我们创建了一个用户,并为该用户分配了权限,接着我们就能使用这个用户去访问指定资源了。这里我们将这个用户绑定到 kubectl 配置文件中。

# 创建用户配置

告诉 kubectl test 用户的证书,此时。kube/config 文件内容如下

kubectl config set-credentials test --client-certificate=/home/momo/桌面/unhiden/test.crt --client-key=/home/momo/桌面/unhiden/test.key

image-20220303155823937

# 创建 api-server 配置

kubectl config set-cluster kubernetes --server=https://192.168.5.174:6443 --certificate-authority=/home/momo/桌面/unhiden/ca.crt

此时,kube/config 文件内容如下

image-20220303155933323

# 设置环境项中默认上下文

kubectl config  set-context default --cluster=kubernetes --user=test

设定使用 kubernetes 集群,使用用户 test 进行验证

image-20220303155938489

# 设置默认环境项为 default

kubectl config use-context default

image-20220303155948297

# 访问指定资源

可以看到,由于我们前面设置的 rbac 规则,我们只能管控 php 的资源。

image-20220303155952545