亲宝软件园·资讯

展开

Kubernetes 权限管理认证鉴权详解

运维开发故事 人气:0

正文

Kubernetes 主要通过 API Server 对外提供服务,对于这样的系统来说,如果不加以安全限制,那么可能导致请求被滥用,甚至导致整个集群崩塌。

鉴于此,Kubernetes 对于访问 API 的用户提供了相应的安全控制:认证和授权。认证解决用户是谁的问题,授权解决用户能做什么的问题。只有通过合理的权限控制,才能够保证整个集群系统的安全可靠。

下图是 API 访问需要经过的三个步骤,它们分别是:认证、授权和准入,准入不在这章节讨论,它更多专注的是资源的管理。

认证

认证(Authentication)是指用户是否可以登录 Kubernetes,即是否可以向 API Server 发送请求。

在 Kubernetes 中,有两类用户:

认证用户

Normal Users

Normal Users 独立于 Kubernetes,不归其管理,这类用户可以是:

如果用户都不在 Kubernetes 中,都不归它管,那它是如何进行认证的呢?

对于一般的应用系统来说,用户提供用户名和密码,服务端收到过后会在数据库中进行检查是否存在并有效,如果有就表示鉴权成功,反之失败。

那对于 Kubernetes 来说,是如何实现的呢?

尽管无法通过 API 调用来添加普通用户,Kubernetes 巧妙的通过证书来进行用户认证。也就是说,不管任何用户,只要能提供有效的证书就能通过 Kubernetes 用户认证。

通过用户认证过后,Kubernetes 会把证书中的 CN 作为用户名(比如证书中”/CN=joker“,则用户名就是 Joker),把 Organization 作为用户组,然后由用户名和用户组绑定的 Role 来决定用户的权限。

Service Accounts

Service Accounts 由 Kubernetes 管理,它们被绑定到特定的 namespace,其可以通过 API Server 自己创建,也可以通过调用 API 来创建,比如使用 kubectl 客户端工具。

与 Normal Users 不同,Service Accounts 存在对应的 Kubernetes 对象,当创建 Service Accounts,会创建相应的 Secret,里面保存对应的密码信息(在 1.24.x 版本中,不会创建对应的 secret)。当创建的 Pod 指定了一个 Service Account,其 Secret 会被 Mount 到 Pod 中,Pod 中的进程就可以访问 Kubernetes API 了。

认证策略

Kubernetes 有以下几种鉴权方法:

当 HTTP 请求发送到 API Server 时,Kubernetes 会从 Request 中关联以下信息:

需要注意的是,当多种鉴权方法同时启用时,第一个鉴权方法鉴权成功即通过验证,其它方法不再鉴权,同时 api-server 也不保证鉴权方法的顺序。所有鉴权成功的用户都会加到 group “system:authenticated”中。

客户端证书

当我们使用客户端证书进进行认证时,需要向 Kubernetes 提供有效的证书,这个证书可以是外部证书,也可以是 Kubernetes 自己审批的证书。

如果是外部证书,就需要在 API Server 启动的时候用--client-ca-file=SOMEFILE参数引入外部证书的 CA 等信息,用来验证客户端证书的有效性。

当认证通过后,Kubernetes 会把证书的 Subject CN 作为用户名,Organization 作为用户组(Group)。

不记名令牌

当使用不记名令牌(Bearer token)来对某 HTTP 客户端执行身份认证时,API 服务器希望看到一个名为 Authorization 的 HTTP 头,其值格式为 Bearer。不记名令牌(Bearer token)必须是一个可以放入 HTTP 头部值字段的字符序列,至多可使用 HTTP 的编码和引用机制。例如:如果持有者令牌为 31ada4fd-adec-460c-809a-9e56ceb75269,则其出现在 HTTP 头部时如下所示:

Authorization: Bearer 31ada4fd-adec-460c-809a-9e56ceb75269

在 Kubernetes 中,主要有以下几种使用不记名令牌(Bearer token)的方法:

Static Token File

当使用静态令牌的时候,API Server 会通过--token-auth-file=SOMEFILE从外部引入一个 CSV 文件,API Server 会从这个文件中读取对应的用户名和用户组。

当客户端进行请求时,API Server 把请求 Header 中的 Bearer tokens 和文件中的 token 进行比较,然后判断 Token 是否有效。

Service Account Tokens

当手动或者自动创建 Service Account 的时候,Kubernetes 会自动创建一个 Service Account Token,在 v1.24 版本之前,会对应创建一个 secret 保存到对应的 namespace 中,在 v1.24 版本之后不会再单独创建 secret,而是在启动 Pod 的时候,由 kubelet 通过 TokenRequest API 生成一个 Token 挂载到 Pod 中。

上文提到,Service Account 主要是为 Pods 提供访问 API Server 的功能,当 Pod 创建过后,Service Account Token 就会被 Mount 到 Pod 中,此时 Pod 就拥有访问 API Server 的能力。

当然,Service Account Token 除了用在 Pod 上,在外部也可以使用,在《Kubernetes 集群管理》中的集群安装章节,有介绍使用 Token 访问 Kubernetes Dashboard,这时候使用的也是 Service Account Token。

OpenID Connect Tokens

OpenID Connect 是一种 OAuth3 认证方式,Kubernetes 可以利用 OAuth3 Token Response 中的 ID Token 作为 Bearer Token 进行认证访问。

其流程如下:

不管用户通过哪种方式进行认证,认证通过并不代表就有操作权限,仅仅只是通过第一条防线而已,下一步就要进行鉴权,用来决定用户是否有具体的操作权限。

鉴权

鉴权流程

当请求通过认证(Authentication)过后,就会进入鉴权(Authorization)阶段。在这个阶段 Kubernetes 会检查请求是否有权限访问需要的资源,如果有权限则开始处理请求,反之则返回权限不足。

API Server 会检查所有 Policy 来检查是否存在 Policy 允许请求中的动作,存在则允许请求执行,否则会拒绝执行并返回 403 错误。当配置了多个授权模块的时候,请求会按顺序校验每一个模板,如果其中任一模块校验不通过,则请求会被拒绝,不再进行后续的校验。

Kubernetes 在做鉴权时,主要检查以下信息:

鉴权模块

Kubernetes 提供了以下 4 种鉴权模式:

这里只会介绍 RBAC——基于角色的访问控制。在实际中,这种模式使用的场景比较多。

RBAC

RBAC 是 Kubernetes 中常用的鉴权模式,其基本概念如下:

Role 和 ClusterRole

Role 和 ClusterRole 中定义一组相关权限的规则,这些权限是累加的(不存在拒绝某操作的规则)。其中 Role 是 Namespace 级别的,而 ClusterRole 是集群级别的。

Role 的定义如下:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: role-demo
  namespace: devops
rules:
- apiGroups: [""]
  resources: ["pods", "deployment"]
  verbs: ["creat", "delete", "watch", "list", "get"]

其含义是:它允许被作用者在 devops 的 namespace 中对 pod 和 deployment 有 creat,delete,watch,list,get 操作。由于 Role 是 Namespace 级别,所以上面的规则只对 devops 的 namespace 有效。

ClusterRole 的定义如下:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: cluster-role-demo
rules:
- apiGroups: [""]
  resources: [""]
  verbs: ["watch", "list", "get"]

其含义是对所有资源都有 watch、list、get 的操作权限。

如果要定义所有权限,可以将 verbs 字段定义如下:

verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]

RoleBinding 和 ClusterRoleBinding

角色绑定将一个角色中定义的各种权限授予一个或者一组用户。角色绑定包含了一组相关主体(即 subject, 包括用户——User、用户组——Group、或者服务账户——Service Account)以及对被授予角色的引用。在命名空间中可以通过 RoleBinding 对象授予权限,而集群范围的权限授予则通过 ClusterRoleBinding 对象完成。

RoleBinding 可以引用在同一命名空间内定义的 Role 对象。如下:

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: joker-rolebinding
  namespace: devops
subjects:
  - kind: User
    name: joker
    apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: role-demo
  apiGroup: rbac.authorization.k8s.io

这就给 joker 用户和 role-demo 的 Role 建立了绑定关系,其中 joker 这个 User 只是一个授权系统中的逻辑概念,它需要通过外部认证服务,比如 Keystone,或者直接给 API Server 指定一个用户名,密码文件。

上面的 YAML 文件中其中一个重要的字段是 Subjects 字段,它定义"被作用者",其中的 kind 表示被作用者的类型,其有以下三种类型:

另外一个重要字段是 roleRef,它定义 RoleBing 对象可以直接通过 Role 的名字来引用我们定义的 Role 对象,从而定义被作业者和角色之间的绑定关系。

RoleBinding 是 namespace 级别的,如果是集群级别则用 ClusterRoleBinding,如下:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: joker-clusterrolebinding
subjects:
  - kind: User
    name: joker
    apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: cluster-role-demo
  apiGroup: rbac.authorization.k8s.io

上面定义的就是 joker 这个用户对所有 namespace 里的资源都有 watch,list,get 的操作权限。

Service Account

Service Account 也是一种账号,但它并不是给 Kubernetes 集群的用户(系统管理员、运维人员、租户用户等)用的,而是给运行在 Pod 里的进程用的,它为 Pod 里的进程提供了必要的SFZ明。

我们通过一个例子来了解 ServiceAccount 的授权过程。

1、首先定义一个 ServiceAccount:

apiVersion: v1
kind: ServiceAccount
metadata:
  namespace: devops
  name: sa-demo

一个简单的 ServiceAccount 只需要简单的 namespace 和 name 即可。

2、编写 RoleBinding 的 YAML 文件来为这个 ServiceAccount 分配权限:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: role-demo
  namespace: devops
rules:
  - apiGroups: [""]
    resources: ["pods", "deployment"]
    verbs: ["creat", "delete", "watch", "list", "get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: sa-rolebinding
  namespace: devops
subjects:
  - kind: ServiceAccount
    name: sa-demo
    namespace: devops
roleRef:
  kind: Role
  name: role-demo
  apiGroup: rbac.authorization.k8s.io

然后我们创建上面定义的 YAML 文件,查看创建完成后的信息:

$ kubectl get sa -n devops
NAME      SECRETS   AGE
default   0         6m25s
sa-demo   0         6m25s
$ kubectl get role -n devops
NAME        CREATED AT
role-demo   2022-07-06T04:27:02Z
$ kubectl get rolebinding -n devops
NAME             ROLE             AGE
sa-rolebinding   Role/role-demo   6m50s

现在创建一个 Pod 并使用 sa-demo Service Account。

apiVersion: v1
kind: Pod
metadata:
  name: pod-sa-demo
  namespace: devops
spec:
  serviceAccountName: sa-demo
  containers:
  - name: pod-sa-demo
    image: nginx
    imagePullPolicy: IfNotPresent

查看 Pod 信息如下:

$ kubectl get po -n devops pod-sa-demo -oyaml
apiVersion: v1
kind: Pod
metadata:
  annotations:
    cni.projectcalico.org/containerID: c0820de4319bb6915602c84132ff83a63f62abaa1e9c706bad04e64661455d30
    cni.projectcalico.org/podIP: 172.16.51.225/32
    cni.projectcalico.org/podIPs: 172.16.51.225/32
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"v1","kind":"Pod","metadata":{"annotations":{},"name":"pod-sa-demo","namespace":"devops"},"spec":{"containers":[{"image":"nginx","imagePullPolicy":"IfNotPresent","name":"pod-sa-demo"}],"serviceAccountName":"sa-demo"}}
  creationTimestamp: "2022-07-06T04:30:13Z"
  name: pod-sa-demo
  namespace: devops
  resourceVersion: "192831"
  uid: 4f4c7c5a-53ca-45f7-94ad-63e546cfcc62
spec:
  containers:
  - image: nginx
    imagePullPolicy: IfNotPresent
    name: pod-sa-demo
    resources: {}
    terminationMessagePath: /dev/termination-log
    terminationMessagePolicy: File
    volumeMounts:
    - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
      name: kube-api-access-vxrcd
      readOnly: true
  dnsPolicy: ClusterFirst
  enableServiceLinks: true
  nodeName: kk-node01
  preemptionPolicy: PreemptLowerPriority
  priority: 0
  restartPolicy: Always
  schedulerName: default-scheduler
  securityContext: {}
  serviceAccount: sa-demo
  serviceAccountName: sa-demo
  terminationGracePeriodSeconds: 30
  tolerations:
  - effect: NoExecute
    key: node.kubernetes.io/not-ready
    operator: Exists
    tolerationSeconds: 300
  - effect: NoExecute
    key: node.kubernetes.io/unreachable
    operator: Exists
    tolerationSeconds: 300
  volumes:
  - name: kube-api-access-vxrcd
    projected:
      defaultMode: 420
      sources:
      - serviceAccountToken:
          expirationSeconds: 3607
          path: token
      - configMap:
          items:
          - key: ca.crt
            path: ca.crt
          name: kube-root-ca.crt
      - downwardAPI:
          items:
          - fieldRef:
              apiVersion: v1
              fieldPath: metadata.namespace
            path: namespace

从上面可以看到 Service Account Token 被挂载到 Pod 中了。

$ kubectl exec -it -n devops pod-sa-demo -- /bin/sh
# ls /var/run/secrets/kubernetes.io/serviceaccount
ca.crt  namespace  token

其中 ca,crt 就是用来访问 API Server 的。

如果一个 Pod 在定义时没有指定 spec.serviceAccountName 属性,则系统会自动为其赋值为 default,即大家都使用同一个 Namespace 下的默认 Service Account。

Subjects 的 kind 类型除了 User,ServiceAccount 之外,还有一个 Group,就是一组用户的意思。如果你为 Kubernetes 配置了外部认证服务的话,这个用户组就由外部认证服务提供。而对于 Kubernetes 内置用户 ServiceAccount 来说,其也有用户和用户组的概念,其中对于一个 ServiceAccount,其在 Kubernetes 中对应的用户是:

system:serviceaccount:<ServiceAccount名字>

而对于其用户组是:

system:serviceaccounts:<Namespace名字>

比如我们定义下面这个 RoleBinding:

subjects:
  - kind: Group
    name: system:serviceaccounts:devops
    apiGroup: rbac.authorization.k8s.io

这就意味着 Role 这个角色的权限规则作用与 devops 的 namespace 中的所有 ServiceAccount。

再比如:

subjects:
  - kind: Group
    name: system:serviceaccounts
    apiGroup: rbac.authorization.k8s.io

这就意味着 Role 这个角色规则作用与整个集群的所有 ServiceAccount。

kubernetes 已经内置了许多 ClusterRole,以 system:开头,可以用 kubectl get clusterrole 查看。

另外,Kubernetes 还提供了四个预先定义好的 ClusterRole 来供用户直接使用,它们是:

我们在定义 RoleBinding 或 ClusterRolebinding 的时候可以直接使用。

最后

Kubernetes 的权限管理就介绍到这里,本章节主要介绍了认证、授权的大概流程以及在 Kubernetes 中是如何实现认证、授权的。学完本章,你可以掌握认证用户有哪些,有哪些认证策略,以及如何使用 RBAC 实现鉴权。

加载全部内容

相关教程
猜你喜欢
用户评论