9、权限管理RBAC

9、RBAC权限控制

更新:从1.24版本开始,创建sa不再自动生成secrets

image-20240629153657391

image-20240629153805104

在Kubernetes中,你可以定义不同的角色(Role),这些角色包含了对特定命名空间(Namespace)内的资源进行操作的一组权限。然后通过角色绑定(RoleBinding)将这些角色分配给用户、用户组或服务账户(ServiceAccount),从而允许他们执行相应的操作。

集群角色(ClusterRole)则是在整个集群范围内生效的角色,它拥有对所有命名空间或者某些特定全局资源的操作权限。通过集群角色绑定(ClusterRoleBinding),可以将这些集群范围内的权限赋予指定的用户、用户组或服务账户。

1. 开启RBAC

管理员可以通过 Kubernetes API 动态配置策略来启用RBAC,需要在 kube-apiserver 中添加参数--authorization-mode=RBAC,如果使用的 kubeadm 安装的集群那么是默认开启了 RBAC 的,可以通过查看 Master 节点上 apiserver 的静态 Pod 定义文件:

➜  ~ cat /etc/kubernetes/manifests/kube-apiserver.yaml
...
    - --authorization-mode=Node,RBAC
...

如果是二进制的方式搭建的集群,添加这个参数过后,记得要重启 kube-apiserver 服务。

2. API 对象

在学习 RBAC 之前,我们还需要再去理解下 Kubernetes 集群中的对象,我们知道,在 Kubernetes 集群中,Kubernetes 对象是我们持久化的实体,就是最终存入 etcd 中的数据,集群中通过这些实体来表示整个集群的状态。前面我们都直接编写的 YAML 文件,通过 kubectl 来提交的资源清单文件,然后创建的对应的资源对象,那么它究竟是如何将我们的 YAML 文件转换成集群中的一个 API 对象的呢?

这个就需要去了解下声明式 API的设计,Kubernetes API 是一个以 JSON 为主要序列化方式的 HTTP 服务,除此之外也支持 Protocol Buffers 序列化方式,主要用于集群内部组件间的通信。为了可扩展性,Kubernetes 在不同的 API 路径(比如/api/v1 或者 /apis/batch)下面支持了多个 API 版本,不同的 API 版本意味着不同级别的稳定性和支持:

  • Alpha 级别,例如 v1alpha1 默认情况下是被禁用的,可以随时删除对功能的支持,所以要慎用
  • Beta 级别,例如 v2beta1 默认情况下是启用的,表示代码已经经过了很好的测试,但是对象的语义可能会在随后的版本中以不兼容的方式更改
  • 稳定级别,比如 v1 表示已经是稳定版本了,也会出现在后续的很多版本中。

在 Kubernetes 集群中,一个 API 对象在 Etcd 里的完整资源路径,是由:Group(API 组)Version(API 版本)Resource(API 资源类型)三个部分组成的。通过这样的结构,整个 Kubernetes 里的所有 API 对象,实际上就可以用如下的树形结构表示出来:

apiserver tree

从上图中我们也可以看出 Kubernetes 的 API 对象的组织方式,在顶层,我们可以看到有一个核心组(由于历史原因,是 /api/v1 下的所有内容而不是在 /apis/core/v1 下面)和命名组(路径 /apis/$NAME/$VERSION)和系统范围内的实体,比如 /metrics。我们也可以用下面的命令来查看集群中的 API 组织形式:

➜  ~ kubectl get --raw /
{
  "paths": [
    "/api",
    "/api/v1",
    "/apis",
    "/apis/",
    ......
    "/version"
  ]
}

比如我们来查看批处理这个操作,在我们当前这个版本中存在 1 个版本的操作:/apis/batch/v1,暴露了可以查询和操作的不同实体集合,同样我们还是可以通过 kubectl 来查询对应对象下面的数据:

➜  ~ kubectl get --raw /apis/batch/v1 | python -m json.tool
{
    "apiVersion": "v1",
    "groupVersion": "batch/v1",
    "kind": "APIResourceList",
    "resources": [
        {
            "categories": [
                "all"
            ],
            "kind": "CronJob",
            "name": "cronjobs",
            "namespaced": true,
            "shortNames": [
                "cj"
            ],
            "singularName": "",
            "storageVersionHash": "sd5LIXh4Fjs=",
            "verbs": [
                "create",
                "delete",
                "deletecollection",
                "get",
                "list",
                "patch",
                "update",
                "watch"
            ]
        },
        {
            "kind": "CronJob",
            "name": "cronjobs/status",
            "namespaced": true,
            "singularName": "",
            "verbs": [
                "get",
                "patch",
                "update"
            ]
        },
        {
            "categories": [
                "all"
            ],
            "kind": "Job",
            "name": "jobs",
            "namespaced": true,
            "singularName": "",
            "storageVersionHash": "mudhfqk/qZY=",
            "verbs": [
                "create",
                "delete",
                "deletecollection",
                "get",
                "list",
                "patch",
                "update",
                "watch"
            ]
        },
        {
            "kind": "Job",
            "name": "jobs/status",
            "namespaced": true,
            "singularName": "",
            "verbs": [
                "get",
                "patch",
                "update"
            ]
        }
    ]
}

但是这个操作和我们平时操作 HTTP 服务的方式不太一样,这里我们可以通过 kubectl proxy 命令来开启对 apiserver 的访问:

➜  ~ kubectl proxy
Starting to serve on 127.0.0.1:8001

然后重新开启一个新的终端,我们可以通过如下方式来访问批处理的 API 服务:

➜  ~ curl http://127.0.0.1:8001/apis/batch/v1
{
  "kind": "APIResourceList",
  "apiVersion": "v1",
  "groupVersion": "batch/v1",
  "resources": [
    {
      "name": "jobs",
      "singularName": "",
      "namespaced": true,
      "kind": "Job",
      "verbs": [
        "create",
        "delete",
        "deletecollection",
        "get",
        "list",
        "patch",
        "update",
        "watch"
      ],
      "categories": [
        "all"
      ],
      "storageVersionHash": "mudhfqk/qZY="
    },
    {
      "name": "jobs/status",
      "singularName": "",
      "namespaced": true,
      "kind": "Job",
      "verbs": [
        "get",
        "patch",
        "update"
      ]
    }
  ]
}

通常,Kubernetes API 支持通过标准 HTTP POSTPUTDELETEGET 在指定 PATH 路径上创建、更新、删除和检索操作,并使用 JSON 作为默认的数据交互格式。

比如现在我们要创建一个 Deployment 对象,那么我们的 YAML 文件的声明就需要怎么写:

apiVersion: apps/v1
kind: Deployment

其中 Deployment 就是这个 API 对象的资源类型(Resource),apps 就是它的组(Group),v1 就是它的版本(Version)。API Group、Version 和 资源就唯一定义了一个 HTTP 路径,然后在 kube-apiserver 端对这个 url 进行了监听,然后把对应的请求传递给了对应的控制器进行处理而已,当然在 Kuberentes 中的实现过程是非常复杂的。

3. RBAC

RBAC API 声明了四种 Kubernetes 对象:RoleClusterRoleRoleBindingClusterRoleBinding。你可以像使用其他 Kubernetes 对象一样, 通过类似 kubectl 这类工具描述或修补 RBAC 对象

3.1 Role 和 ClusterRole

RBAC 的 RoleClusterRole 中包含一组代表相关权限的规则。 这些权限是纯粹累加的(不存在拒绝某操作的规则)。

Role 总是用来在某个命名空间内设置访问权限; 在你创建 Role 时,你必须指定该 Role 所属的命名空间。

与之相对,ClusterRole 则是一个集群作用域的资源。这两种资源的名字不同(Role 和 ClusterRole) 是因为 Kubernetes 对象要么是命名空间作用域的,要么是集群作用域的,不可两者兼具。

ClusterRole 有若干用法。你可以用它来:

  1. 定义对某命名空间域对象的访问权限,并将在个别命名空间内被授予访问权限;
  2. 为命名空间作用域的对象设置访问权限,并被授予跨所有命名空间的访问权限;
  3. 为集群作用域的资源定义访问权限。

如果你希望在命名空间内定义角色,应该使用 Role; 如果你希望定义集群范围的角色,应该使用 ClusterRole。

3.1.1 Role 示例

下面是一个位于 "default" 命名空间的 Role 的示例,可用来授予对 Pod 的读访问权限:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default
  name: pod-reader
rules:
- apiGroups: [""] # "" 标明 core API 组
  resources: ["pods"]
  verbs: ["get", "watch", "list"]

3.1.2 ClusterRole 示例

ClusterRole 同样可以用于授予 Role 能够授予的权限。 因为 ClusterRole 属于集群范围,所以它也可以为以下资源授予访问权限:

  • 集群范围资源(比如节点(Node))

  • 非资源端点(比如 /healthz

  • 跨命名空间访问的命名空间作用域的资源(如 Pod)

    比如,你可以使用 ClusterRole 来允许某特定用户执行 kubectl get pods --all-namespaces

下面是一个 ClusterRole 的示例,可用来为任一特定命名空间中的 Secret 授予读访问权限, 或者跨命名空间的访问权限(取决于该角色是如何绑定的):

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  # "namespace" 被忽略,因为 ClusterRoles 不受命名空间限制
  name: secret-reader
rules:
- apiGroups: [""]
  # 在 HTTP 层面,用来访问 Secret 资源的名称为 "secrets"
  resources: ["secrets"]
  verbs: ["get", "watch", "list"]

Role 或 ClusterRole 对象的名称必须是合法的路径分段名称。

3.2 RoleBinding 和 ClusterRoleBinding

角色绑定(Role Binding)是将角色中定义的权限赋予一个或者一组用户。 它包含若干主体(Subject)(用户、组或服务账户)的列表和对这些主体所获得的角色的引用。 RoleBinding 在指定的命名空间中执行授权,而 ClusterRoleBinding 在集群范围执行授权。

一个 RoleBinding 可以引用同一的命名空间中的任何 Role。 或者,一个 RoleBinding 可以引用某 ClusterRole 并将该 ClusterRole 绑定到 RoleBinding 所在的命名空间。 如果你希望将某 ClusterRole 绑定到集群中所有命名空间,你要使用 ClusterRoleBinding。

RoleBinding 或 ClusterRoleBinding 对象的名称必须是合法的路径分段名称

3.2.1 RoleBinding 示例

下面的例子中的 RoleBinding 将 "pod-reader" Role 授予在 "default" 命名空间中的用户 "jane"。 这样,用户 "jane" 就具有了读取 "default" 命名空间中所有 Pod 的权限。

apiVersion: rbac.authorization.k8s.io/v1
# 此角色绑定允许 "jane" 读取 "default" 命名空间中的 Pod
# 你需要在该命名空间中有一个名为 “pod-reader” 的 Role
kind: RoleBinding
metadata:
  name: read-pods
  namespace: default
subjects:
# 你可以指定不止一个“subject(主体)”
- kind: User
  name: jane # "name" 是区分大小写的
  apiGroup: rbac.authorization.k8s.io
roleRef:
  # "roleRef" 指定与某 Role 或 ClusterRole 的绑定关系
  kind: Role        # 此字段必须是 Role 或 ClusterRole
  name: pod-reader  # 此字段必须与你要绑定的 Role 或 ClusterRole 的名称匹配
  apiGroup: rbac.authorization.k8s.io

RoleBinding 也可以引用 ClusterRole,以将对应 ClusterRole 中定义的访问权限授予 RoleBinding 所在命名空间的资源。这种引用使得你可以跨整个集群定义一组通用的角色, 之后在多个命名空间中复用。

例如,尽管下面的 RoleBinding 引用的是一个 ClusterRole,"dave"(这里的主体, 区分大小写)只能访问 "development" 命名空间中的 Secret 对象,因为 RoleBinding 所在的命名空间(由其 metadata 决定)是 "development"。

apiVersion: rbac.authorization.k8s.io/v1
# 此角色绑定使得用户 "dave" 能够读取 "development" 命名空间中的 Secret
# 你需要一个名为 "secret-reader" 的 ClusterRole
kind: RoleBinding
metadata:
  name: read-secrets
  # RoleBinding 的命名空间决定了访问权限的授予范围。
  # 这里隐含授权仅在 "development" 命名空间内的访问权限。
  namespace: development
subjects:
- kind: User
  name: dave # 'name' 是区分大小写的
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: secret-reader
  apiGroup: rbac.authorization.k8s.io

3.2.2 ClusterRoleBinding 示例

要跨整个集群完成访问权限的授予,你可以使用一个 ClusterRoleBinding。 下面的 ClusterRoleBinding 允许 "manager" 组内的所有用户访问任何命名空间中的 Secret。

apiVersion: rbac.authorization.k8s.io/v1
# 此集群角色绑定允许 “manager” 组中的任何人访问任何命名空间中的 Secret 资源
kind: ClusterRoleBinding
metadata:
  name: read-secrets-global
subjects:
- kind: Group
  name: manager      # 'name' 是区分大小写的
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: secret-reader
  apiGroup: rbac.authorization.k8s.io

创建了绑定之后,你不能再修改绑定对象所引用的 Role 或 ClusterRole。 试图改变绑定对象的 roleRef 将导致合法性检查错误。 如果你想要改变现有绑定对象中 roleRef 字段的内容,必须删除重新创建绑定对象。

这种限制有两个主要原因:

  1. roleRef 设置为不可以改变,这使得可以为用户授予对现有绑定对象的 update 权限, 这样可以让他们管理主体列表,同时不能更改被授予这些主体的角色。

  2. 针对不同角色的绑定是完全不一样的绑定。要求通过删除/重建绑定来更改 roleRef, 这样可以确保要赋予绑定的所有主体会被授予新的角色(而不是在允许或者不小心修改了 roleRef 的情况下导致所有现有主体未经验证即被授予新角色对应的权限)。

命令 kubectl auth reconcile 可以创建或者更新包含 RBAC 对象的清单文件, 并且在必要的情况下删除和重新创建绑定对象,以改变所引用的角色。 更多相关信息请参照命令用法和示例

3.3 对资源的引用

在 Kubernetes API 中,大多数资源都是使用对象名称的字符串表示来呈现与访问的。 例如,对于 Pod 应使用 "pods"。 RBAC 使用对应 API 端点的 URL 中呈现的名字来引用资源。 有一些 Kubernetes API 涉及子资源(subresource),例如 Pod 的日志。 对 Pod 日志的请求看起来像这样:

GET /api/v1/namespaces/{namespace}/pods/{name}/log

在这里,pods 对应命名空间作用域的 Pod 资源,而 logpods 的子资源。 在 RBAC 角色表达子资源时,使用斜线(/)来分隔资源和子资源。 要允许某主体读取 pods 同时访问这些 Pod 的 log 子资源,你可以这样写:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default
  name: pod-and-pod-logs-reader
rules:
- apiGroups: [""]
  resources: ["pods", "pods/log"]
  verbs: ["get", "list"]

对于某些请求,也可以通过 resourceNames 列表按名称引用资源。 在指定时,可以将请求限定为资源的单个实例。 下面的例子中限制可以 getupdate 一个名为 my-configmapConfigMap

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default
  name: configmap-updater
rules:
- apiGroups: [""]
  # 在 HTTP 层面,用来访问 ConfigMap 资源的名称为 "configmaps"
  resources: ["configmaps"]
  resourceNames: ["my-configmap"]
  verbs: ["update", "get"]

说明:

你不能使用资源名字来限制 create 或者 deletecollection 请求。 对于 create 请求而言,这是因为在鉴权时可能还不知道新对象的名字。 如果你使用 resourceName 来限制 list 或者 watch 请求, 客户端必须在它们的 list 或者 watch 请求里包含一个与指定的 resourceName 匹配的 metadata.name 字段选择器。 例如,kubectl get configmaps --field-selector=metadata.name=my-configmap

你可以使用通配符 * 批量引用所有的 resourcesapiGroupsverbs 对象,无需逐一引用。 对于 nonResourceURLs,你可以将通配符 * 作为后缀实现全局通配, 对于 resourceNames,空集表示没有任何限制。 下面的示例对 example.com API 组中所有当前和未来资源执行所有动作。 这类似于内置的 cluster-admin

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default
  name: example.com-superuser # 此角色仅作示范,请勿使用
rules:
- apiGroups: ["example.com"]
  resources: ["*"]
  verbs: ["*"]

注意:

在 resources 和 verbs 条目中使用通配符会为敏感资源授予过多的访问权限。 例如,如果添加了新的资源类型、新的子资源或新的自定义动词, 通配符条目会自动授予访问权限,用户可能不希望出现这种情况。 应该执行最小特权原则, 使用具体的 resources 和 verbs 确保仅赋予工作负载正常运行所需的权限。

3.4 对主体的引用

RoleBinding 或者 ClusterRoleBinding 可绑定角色到某主体(Subject) 上。 主体可以是组,用户或者服务账户

Kubernetes 用字符串来表示用户名。 用户名可以是普通的用户名,像 "alice";或者是邮件风格的名称,如 "bob@example.com", 或者是以字符串形式表达的数字 ID。你作为 Kubernetes 管理员负责配置身份认证模块, 以便后者能够生成你所期望的格式的用户名。

注意:

前缀 system: 是 Kubernetes 系统保留的,所以你要确保所配置的用户名或者组名不能出现上述 system: 前缀。除了对前缀的限制之外,RBAC 鉴权系统不对用户名格式作任何要求。

在 Kubernetes 中,身份认证(Authenticator)模块提供用户组信息。 与用户名一样,用户组名也用字符串来表示,而且对该字符串没有格式要求, 只是不能使用保留的前缀 system:

服务账户(ServiceAccount) 的用户名前缀为 system:serviceaccount:,属于前缀为 system:serviceaccounts: 的用户组。

说明:

  • system:serviceaccount: (单数)是用于服务账户用户名的前缀;
  • system:serviceaccounts: (复数)是用于服务账户组名的前缀。

RoleBinding 示例

下面示例是 RoleBinding 中的片段,仅展示其 subjects 的部分。

对于名称为 alice@example.com 的用户:

subjects:
- kind: User
  name: "alice@example.com"
  apiGroup: rbac.authorization.k8s.io

对于名称为 frontend-admins 的用户组:

subjects:
- kind: Group
  name: "frontend-admins"
  apiGroup: rbac.authorization.k8s.io

对于 kube-system 命名空间中的默认服务账户:

subjects:
- kind: ServiceAccount
  name: default
  namespace: kube-system

对于 "qa" 名称空间中的所有服务账户:

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

对于在任何命名空间中的服务账户:

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

对于所有已经过身份认证的用户:

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

对于所有未通过身份认证的用户:

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

对于所有用户:

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

4. 聚合的 ClusterRole

你可以将若干 ClusterRole 聚合(Aggregate) 起来,形成一个复合的 ClusterRole。 作为集群控制面的一部分,控制器会监视带有 aggregationRule 的 ClusterRole 对象集合。aggregationRule 为控制器定义一个标签选择算符供后者匹配应该组合到当前 ClusterRole 的 roles 字段中的 ClusterRole 对象。

注意:

控制平面会覆盖你在聚合 ClusterRole 的 rules 字段中手动指定的所有值。 如果你想更改或添加规则,请在被 aggregationRule 所选中的 ClusterRole 对象上执行变更。

4.1 ClusterRole 示例

下面是一个聚合 ClusterRole 的示例:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: monitoring
aggregationRule:
  clusterRoleSelectors:
  - matchLabels:
      rbac.example.com/aggregate-to-monitoring: "true"
rules: [] # 控制面自动填充这里的规则

如果你创建一个与某个已存在的聚合 ClusterRole 的标签选择算符匹配的 ClusterRole, 这一变化会触发新的规则被添加到聚合 ClusterRole 的操作。 下面的例子中,通过创建一个标签同样为 rbac.example.com/aggregate-to-monitoring: true 的 ClusterRole,新的规则可被添加到 "monitoring" ClusterRole 中。

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: monitoring-endpoints
  labels:
    rbac.example.com/aggregate-to-monitoring: "true"
# 当你创建 "monitoring-endpoints" ClusterRole 时,
# 下面的规则会被添加到 "monitoring" ClusterRole 中
rules:
- apiGroups: [""]
  resources: ["services", "endpointslices", "pods"]
  verbs: ["get", "list", "watch"]

默认的面向用户的角色使用 ClusterRole 聚合。 这使得作为集群管理员的你可以为扩展默认规则,包括为定制资源设置规则, 比如通过 CustomResourceDefinitions 或聚合 API 服务器提供的定制资源。

例如,下面的 ClusterRoles 让默认角色 "admin" 和 "edit" 拥有管理自定义资源 "CronTabs" 的权限, "view" 角色对 CronTab 资源拥有读操作权限。 你可以假定 CronTab 对象在 API 服务器所看到的 URL 中被命名为 "crontabs"

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: aggregate-cron-tabs-edit
  labels:
    # 添加以下权限到默认角色 "admin" 和 "edit" 中
    rbac.authorization.k8s.io/aggregate-to-admin: "true"
    rbac.authorization.k8s.io/aggregate-to-edit: "true"
rules:
- apiGroups: ["stable.example.com"]
  resources: ["crontabs"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: aggregate-cron-tabs-view
  labels:
    # 添加以下权限到 "view" 默认角色中
    rbac.authorization.k8s.io/aggregate-to-view: "true"
rules:
- apiGroups: ["stable.example.com"]
  resources: ["crontabs"]
  verbs: ["get", "list", "watch"]

4.2 Role 示例

以下示例均为从 Role 或 ClusterRole 对象中截取出来,我们仅展示其 rules 部分。

允许读取在核心 API组 下的 "pods"

rules:
- apiGroups: [""]
  # 在 HTTP 层面,用来访问 Pod 资源的名称为 "pods"
  resources: ["pods"]
  verbs: ["get", "list", "watch"]

允许在 "apps" API 组中读/写 Deployment(在 HTTP 层面,对应 URL 中资源部分为 "deployments"):

rules:
- apiGroups: ["apps"]
  #
  # 在 HTTP 层面,用来访问 Deployment 资源的名称为 "deployments"
  resources: ["deployments"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]

允许读取核心 API 组中的 Pod 和读/写 "batch" API 组中的 Job 资源:

rules:
- apiGroups: [""]
  # 在 HTTP 层面,用来访问 Pod 资源的名称为 "pods"
  resources: ["pods"]
  verbs: ["get", "list", "watch"]
- apiGroups: ["batch"]
  # 在 HTTP 层面,用来访问 Job 资源的名称为 "jobs"
  resources: ["jobs"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]

允许读取名称为 "my-config" 的 ConfigMap(需要通过 RoleBinding 绑定以限制为某命名空间中特定的 ConfigMap):

rules:
- apiGroups: [""]
  # 在 HTTP 层面,用来访问 ConfigMap 资源的名称为 "configmaps"
  resources: ["configmaps"]
  resourceNames: ["my-config"]
  verbs: ["get"]

允许读取在核心组中的 "nodes" 资源(因为 Node 是集群作用域的,所以需要 ClusterRole 绑定到 ClusterRoleBinding 才生效):

rules:
- apiGroups: [""]
  # 在 HTTP 层面,用来访问 Node 资源的名称为 "nodes"
  resources: ["nodes"]
  verbs: ["get", "list", "watch"]

允许针对非资源端点 /healthz 和其子路径上发起 GET 和 POST 请求 (必须在 ClusterRole 绑定 ClusterRoleBinding 才生效):

rules:
- nonResourceURLs: ["/healthz", "/healthz/*"] # nonResourceURL 中的 '*' 是一个全局通配符
  verbs: ["get", "post"]

5. 一些命令行工具

可使用--dry-run=client -o yaml导出yaml文件

5.1 kubectl create role

创建 Role 对象,定义在某一命名空间中的权限。例如:

  • 创建名称为 “pod-reader” 的 Role 对象,允许用户对 Pods 执行 getwatchlist 操作:

    kubectl create role pod-reader --verb=get --verb=list --verb=watch --resource=pods
    
  • 创建名称为 “pod-reader” 的 Role 对象并指定 resourceNames

    kubectl create role pod-reader --verb=get --resource=pods --resource-name=readablepod --resource-name=anotherpod
    
  • 创建名为 “foo” 的 Role 对象并指定 apiGroups

    kubectl create role foo --verb=get,list,watch --resource=replicasets.apps
    
  • 创建名为 “foo” 的 Role 对象并指定子资源权限:

    kubectl create role foo --verb=get,list,watch --resource=pods,pods/status
    
  • 创建名为 “my-component-lease-holder” 的 Role 对象,使其具有对特定名称的资源执行 get/update 的权限:

    kubectl create role my-component-lease-holder --verb=get,list,watch,update --resource=lease --resource-name=my-component
    

5.2 kubectl create clusterrole

创建 ClusterRole 对象。例如:

  • 创建名称为 “pod-reader” 的 ClusterRole 对象,允许用户对 Pods 对象执行 getwatchlist 操作:

    kubectl create clusterrole pod-reader --verb=get,list,watch --resource=pods
    
  • 创建名为 “pod-reader” 的 ClusterRole 对象并指定 resourceNames

    kubectl create clusterrole pod-reader --verb=get --resource=pods --resource-name=readablepod --resource-name=anotherpod
    
  • 创建名为 “foo” 的 ClusterRole 对象并指定 apiGroups

    kubectl create clusterrole foo --verb=get,list,watch --resource=replicasets.apps
    
  • 创建名为 “foo” 的 ClusterRole 对象并指定子资源:

    kubectl create clusterrole foo --verb=get,list,watch --resource=pods,pods/status
    
  • 创建名为 “foo” 的 ClusterRole 对象并指定 nonResourceURL

    kubectl create clusterrole "foo" --verb=get --non-resource-url=/logs/*
    
  • 创建名为 “monitoring” 的 ClusterRole 对象并指定 aggregationRule

    kubectl create clusterrole monitoring --aggregation-rule="rbac.example.com/aggregate-to-monitoring=true"
    

5.3 kubectl create rolebinding

在特定的命名空间中对 RoleClusterRole 授权。例如:

  • 在命名空间 “acme” 中,将名为 admin 的 ClusterRole 中的权限授予名称 “bob” 的用户:

    kubectl create rolebinding bob-admin-binding --clusterrole=admin --user=bob --namespace=acme
    
  • 在命名空间 “acme” 中,将名为 view 的 ClusterRole 中的权限授予命名空间 “acme” 中名为 myapp 的服务账户:

    kubectl create rolebinding myapp-view-binding --clusterrole=view --serviceaccount=acme:myapp --namespace=acme
    
  • 在命名空间 “acme” 中,将名为 view 的 ClusterRole 对象中的权限授予命名空间 “myappnamespace” 中名称为 myapp 的服务账户:

    kubectl create rolebinding myappnamespace-myapp-view-binding --clusterrole=view --serviceaccount=myappnamespace:myapp --namespace=acme
    

5.4 kubectl create clusterrolebinding

在整个集群(所有命名空间)中用 ClusterRole 授权。例如:

  • 在整个集群范围,将名为 cluster-admin 的 ClusterRole 中定义的权限授予名为 “root” 用户:

    kubectl create clusterrolebinding root-cluster-admin-binding --clusterrole=cluster-admin --user=root
    
  • 在整个集群范围内,将名为 system:node-proxier 的 ClusterRole 的权限授予名为 “system:kube-proxy” 的用户:

    kubectl create clusterrolebinding kube-proxy-binding --clusterrole=system:node-proxier --user=system:kube-proxy
    
  • 在整个集群范围内,将名为 view 的 ClusterRole 中定义的权限授予 “acme” 命名空间中名为 “myapp” 的服务账户:

    kubectl create clusterrolebinding myapp-view-binding --clusterrole=view --serviceaccount=acme:myapp
    

5.5 kubectl auth reconcile

使用清单文件来创建或者更新 rbac.authorization.k8s.io/v1 API 对象。 #can-i可以测试是否创建成功

尚不存在的对象会被创建,如果对应的命名空间也不存在,必要的话也会被创建。 已经存在的角色会被更新,使之包含输入对象中所给的权限。如果指定了 --remove-extra-permissions,可以删除额外的权限。

已经存在的绑定也会被更新,使之包含输入对象中所给的主体。如果指定了 --remove-extra-permissions,则可以删除多余的主体。

例如:

  • 测试应用 RBAC 对象的清单文件,显示将要进行的更改:

    kubectl auth reconcile -f my-rbac-rules.yaml --dry-run=client
    
  • 应用 RBAC 对象的清单文件,保留角色(roles)中的额外权限和绑定(bindings)中的其他主体:

    kubectl auth reconcile -f my-rbac-rules.yaml
    
  • 应用 RBAC 对象的清单文件,删除角色(roles)中的额外权限和绑定中的其他主体:

    kubectl auth reconcile -f my-rbac-rules.yaml --remove-extra-subjects --remove-extra-permissions
    

6. 面向用户的角色

一些默认的 ClusterRole 不是以前缀 system: 开头的。这些是面向用户的角色。 它们包括超级用户(Super-User)角色(cluster-admin)、 使用 ClusterRoleBinding 在集群范围内完成授权的角色(cluster-status)、 以及使用 RoleBinding 在特定命名空间中授予的角色(admineditview)。

面向用户的 ClusterRole 使用 ClusterRole 聚合以允许管理员在这些 ClusterRole 上添加用于定制资源的规则。如果想要添加规则到 adminedit 或者 view, 可以创建带有以下一个或多个标签的 ClusterRole:

metadata:
  labels:
    rbac.authorization.k8s.io/aggregate-to-admin: "true"
    rbac.authorization.k8s.io/aggregate-to-edit: "true"
    rbac.authorization.k8s.io/aggregate-to-view: "true"
默认 ClusterRole 默认 ClusterRoleBinding 描述
cluster-admin system:masters 允许超级用户在平台上的任何资源上执行所有操作。 当在 ClusterRoleBinding 中使用时,可以授权对集群中以及所有命名空间中的全部资源进行完全控制。 当在 RoleBinding 中使用时,可以授权控制角色绑定所在命名空间中的所有资源,包括命名空间本身。
admin 允许管理员访问权限,旨在使用 RoleBinding 在命名空间内执行授权。如果在 RoleBinding 中使用,则可授予对命名空间中的大多数资源的读/写权限, 包括创建角色和角色绑定的能力。 此角色不允许对资源配额或者命名空间本身进行写操作。 此角色也不允许对 Kubernetes v1.22+ 创建的 EndpointSlices(或 Endpoints)进行写操作。 更多信息参阅 “EndpointSlices 和 Endpoints 写权限”小节
edit 允许对命名空间的大多数对象进行读/写操作。此角色不允许查看或者修改角色或者角色绑定。 不过,此角色可以访问 Secret,以命名空间中任何 ServiceAccount 的身份运行 Pod, 所以可以用来了解命名空间内所有服务账户的 API 访问级别。 此角色也不允许对 Kubernetes v1.22+ 创建的 EndpointSlices(或 Endpoints)进行写操作。 更多信息参阅 “EndpointSlices 和 Endpoints 写操作”小节
view 允许对命名空间的大多数对象有只读权限。 它不允许查看角色或角色绑定。此角色不允许查看 Secret,因为读取 Secret 的内容意味着可以访问命名空间中 ServiceAccount 的凭据信息,进而允许利用命名空间中任何 ServiceAccount 的身份访问 API(这是一种特权提升)。

7. RBAC 实践

7.1 需求

  1. 创建一个名为 deployment-clusterrole 的 clusterrole

a)该 clusterrole,只允许创建 Deployment、Daemonset、Statefulset 的 create 操作

  1. 在名字为 app-team1 的 namespace 下创建一个名为 cicd-token 的 serviceAccount,并且将上一步创建 clusterrole的权限绑定到该 serviceAccount

7.2 创建 clusterrole

[root@k8s-master01]# vim dp-clusterrole.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  # "namespace" 被忽略,因为 ClusterRoles 不受命名空间限制
  name: deployment-clusterrole
rules:
- apiGroups: ["apps"]
  # 在 HTTP 层面,用来访问 Secret 资源的名称为 "secrets"
  resources: ["deployments","daemonsets","statefulsets"]
  verbs: ["create"]
[root@k8s-master01]# kubectl apply -f dp-clusterrole.yaml
clusterrole.rbac.authorization.k8s.io/deployment-clusterrole created

7.3 创建 namespace 和 serviceAccount

[root@k8s-master01]# kubectl create ns app-team1
namespace/app-team1 created
[root@k8s-master01]# kubectl create sa cicd-token -n app-team1
serviceaccount/cicd-token created
[root@k8s-master01]#kubectl create rolebinding deployment-rolebinding --clusterrole=deployment-clusterrole --serviceaccount=app-team1:cicd-token --namespace=app-team1
rolebinding.rbac.authorization.k8s.io/deployment-rolebinding created
[root@k8s-master01]# kubectl get rolebinding deployment-rolebinding -n app-team1 -oyaml

7.4 验证

#可查看secret的token
[root@k8s-master01 0629]# kubectl get sa -n app-team1
NAME         SECRETS   AGE
cicd-token   1         102s
default      1         43m
[root@k8s-master01 0629]# kubectl get secrets -n app-team1
NAME                     TYPE                                  DATA   AGE
cicd-token-token-7frxm   kubernetes.io/service-account-token   3      114s
default-token-9kr4v      kubernetes.io/service-account-token   3      43m
[root@k8s-master01 0629]# kubectl describe secrets cicd-token-token-7frxm -n app-team1

使用token登录dashboard

image-20240629215844901

image-20240629221731198

在dashboard创建后没有权限查看,可使用kuboard或使用kubectl get deployment -n app-team1查看。

image-20240629221941342

image-20240629221814967

image-20240629222210870

8. 企业级实践 RBAC

8.1 概念

image-20240629223839975

  • 项目组A:展示了两个不同的需求。第一个需求是“我想对 NamespaceA 下的所有 Pod 执行命令和查看日志的权限”,第二个需求是“我想对 NamespaceA 下的所有资源有编辑权限和Pod的删除权限”。这两个需求分别对应了不同级别的权限。
  • 项目组B:同样有两个需求。第一个需求是“我想对NamespaceB和 NamespaceC 下的所有Pod有执行命令和查看日志的权限”,第二个需求是“我想对 NamespaceA 和 NamespaceC 的所有资源有编辑权限和 Pod 的删除权限”。
  • 通用权限:将上述的需求进行归纳总结,列出了以下几种权限:
    • Namespace 列表查看权限
    • 日志查看权限
    • 执行命令权限
    • Pod删除权限
    • 资源编辑权限
    • 其他权限
  • ClusterRole:这是 Kubernetes 中的一种对象类型,它定义了一组可以在整个集群范围内应用的权限规则。这些规则可以被多个用户、服务账户或者组共享使用。

通过RBAC,管理员可以根据实际需要为不同的用户或组分配相应的权限,从而实现精细的访问控制。例如,在这个例子中,管理员可以通过创建一个名为"project-A-role"的 ClusterRole 来满足项目组A的需求,并将其绑定到对应的用户或服务账户上。类似地,也可以创建另一个名为"project-B-role"的 ClusterRole 以满足项目组B的需求。这样就可以确保每个用户只能访问他们被授予权限的资源,而不能越权操作其他资源。

image-20240629223904666

左边的部分描述了用户管理的基本流程:

  1. User表:存储用户的详细信息,包括用户名、密码等。
  2. 权限表:记录各个用户所拥有的权限,比如查看、编辑、删除等。
  3. 授权表:是用来记录用户或用户组的权限信息的表格。
  4. 数据库:保存了User表和权限表的数据。

右边的部分则具体说明了如何在Kubernetes中实现用户管理:

  1. ClusterRole:定义全局的角色与权限,比如Namespace List ClusterRole表示列出命名空间的权限,Pod Delete ClusterRole表示删除Pod的权限,Pod Exec ClusterRole表示执行 Pod 内命令的权限,以及其他一些权限。
  2. ClusterRoleBinding:将 ClusterRole 与具体的用户或用户组关联起来,使得这些用户能够获得相应的权限。在这个例子中,kube-users是一个用户组,通过ClusterRoleBinding获得了上述的几个权限。
  3. RoleBinding:在特定的命名空间(namespace)里,将权限赋予给用户或用户组。这里可以看到有两个 RoleBinding,一个是Project-A,另一个是Project-B。它们分别将权限授予了不同的用户或用户组。
  4. User1、User2、UserX:代表不同的用户。这些用户可能是通过认证系统(如OAuth、OpenID Connect等)登录系统的用户。
  5. Project-A、Project-B:指不同的项目或业务领域。每个项目有自己的RoleBinding,用来控制该项目内的资源访问权限。

通过这样的方式,管理员可以灵活地管理用户及其权限,确保每个用户只能访问和操作他们被允许的资源,同时也能根据项目的需要调整权限策略。

8.2 实验:不同用户不同权限

8.2.1 需求:

  1. 用户zzb可以查看default、kube-system下Pod的日志

  2. 用户ggb可以在default下的Pod中执行命令,并且可以删除Pod

8.2.2 通用权限管理

8.2.2.1 创建namespace-list
[root@k8s-master01 RBAC]# vim namespace-list.yaml
apiVersion: rbac.authorization.k8s.io/v1  
kind: ClusterRole  
metadata:  
  name: namespace-readonly  
rules:  
- apiGroups: [""]  
  resources: ["namespaces"]  
  verbs: ["get", "list", "watch"]  
- apiGroups: ["metrics.k8s.io"]  
  resources: ["pods"]  
  verbs: ["get", "list", "watch"]
[root@k8s-master01 RBAC]# kubectl apply -f namespace-list.yaml
clusterrole.rbac.authorization.k8s.io/namespace-readonly created  
8.2.2.2 创建Pod-delete
[root@k8s-master01 RBAC]# vim pod-delete.yaml
apiVersion: rbac.authorization.k8s.io/v1  
kind: ClusterRole  
metadata:  
  name: pod-delete  
rules:  
- apiGroups: [""]  
  resources: ["pods"]  
  verbs: ["get", "list", "delete"]
[root@k8s-master01 RBAC]# kubectl apply -f pod-delete.yaml
clusterrole.rbac.authorization.k8s.io/pod-delete created
8.2.2.3 创建pod-exec
[root@k8s-master01 RBAC]# vim pod-exec.yaml
apiVersion: rbac.authorization.k8s.io/v1  
kind: ClusterRole  
metadata:  
  name: pod-exec  
rules:  
- apiGroups: [""]  
  resources: ["pods"]  
  verbs: ["get", "list"]  
- apiGroups: [""]  
  resources: ["pods/exec"]  
  verbs: ["create"]
[root@k8s-master01 RBAC]# kubectl apply -f pod-exec.yaml
clusterrole.rbac.authorization.k8s.io/pod-exec created
8.2.2.4 创建pod-log
[root@k8s-master01 RBAC]# vim pod-log.yaml
apiVersion: rbac.authorization.k8s.io/v1  
kind: ClusterRole  
metadata:  
  name: pod-log  
rules:  
- apiGroups: [""]  
  resources: ["pods", "pods/log"]  
  verbs: ["get", "list", "watch"] # 对于 pods 使用 get, list, watch; 对于 pods/log 只使用 get
[root@k8s-master01 RBAC]# kubectl apply -f pod-log.yaml
clusterrole.rbac.authorization.k8s.io/pod-log created

8.2.3 用户管理

8.2.3.1 创建用户管理命名空间和用户
[root@k8s-master01 RBAC]# kubectl create ns kube-users #创建用户管理命名空间
[root@k8s-master01 RBAC]# kubectl create sa zzb -n kube-users;kubectl create sa ggb -n kube-users #创建用户
8.2.3.2 绑定全局命名空间查看权限
[root@k8s-master01 RBAC]# vim namespace-readonly.yaml
apiVersion: rbac.authorization.k8s.io/v1  
kind: ClusterRoleBinding  
metadata:  
  name: namespace-readonly-sa  
roleRef:  
  apiGroup: rbac.authorization.k8s.io  
  kind: ClusterRole  
  name: namespace-readonly  
subjects:  
- apiGroup: rbac.authorization.k8s.io 
  kind: Group  
  name: system:serviceaccounts:kube-users
[root@k8s-master01 RBAC]# kubectl apply -f namespace-readonly.yaml  
clusterrolebinding.rbac.authorization.k8s.io/namespace-readonly-sa created
8.2.3.3 绑定权限
kubectl create rolebinding zzb-pod-log \
--clusterrole=pod-log --serviceaccount=kube-users:zzb --namespace=kube-system

kubectl create rolebinding zzb-pod-log \
--clusterrole=pod-log --serviceaccount=kube-users:zzb --namespace=default

kubectl create rolebinding ggb-pod-exec \
--clusterrole=pod-exec --serviceaccount=kube-users:ggb --namespace=default

kubectl create rolebinding ggb-pod-delete \
--clusterrole=pod-delete --serviceaccount=kube-users:ggb --namespace=default

8.2.4 测试

使用 kubectl describe secrets -n kube-users zzb-token-jnvcm 查看 token 后用 Dashboard 登录,用户zzb可以看到能够查看到 default 的 pod 资源,以及查看日志,但使用执行(exec)则表示没有权限。

image-20240630001827450

image-20240630002132357

image-20240630002204181

使用 kubectl describe secrets -n kube-users ggb-token-f5gnh 看 token 后用 Dashboard 登录,用户 ggb 可以在 default 下的 Pod 中执行命令,并且可以删除 Pod。

image-20240630003147868

image-20240630003223368

image-20240630003349085

8.2.5 总结

  1. 首先创建通用的权限(ClusterRole)。
  2. 创建了一个 kube-users, 用来管理我们的这个用户(方便看到集群中有哪些用户 kubectl get sa -n kube-users)。
  3. 在命名空间下创建了两个用户。
  4. 两个用户都有一个查看命名空间这个列表的这个权限。
  5. 把不同的权限,不同的 ClusterRole 绑定到不同的 SA 上面去(这个SA就有了不同的权限),创建在哪个命名空间下就有哪个命名空间的权限。

8.2.6 集群外机器使用token(命令行)登录

  1. 创建kubeconfig文件:首先,你需要在你的外部机器上创建或修改kubeconfig文件。如果之前没有创建过,你可以从集群管理员那里获取一个,或者使用以下命令生成一个默认的kubeconfig文件(但请注意,这可能需要管理员权限):

    kubectl config set-cluster kubernetes --server=https://YOUR-API-SERVER --certificate-authority=/path/to/ca.crt
    kubectl config set-context default-context --cluster=kubernetes --user=default-user
    kubectl config set-credentials default-user --token=YOUR-TOKEN
    kubectl config use-context default-context
    

    其中YOUR-API-SERVER是你的API Server的URL,/path/to/ca.crt是你集群的CA证书路径,YOUR-TOKEN是你的token。(可使用kubectl config view查看API Server地址)

  2. 限制命名空间:然后,需要在你的kubeconfig文件中添加一个context,用于限制你只能访问test命名空间。这可以通过在执行kubectl命令时指定--namespace=xxx参数来实现。

  3. 测试连接:完成上述配置后,你可以使用以下命令来测试你的kubectl是否能正确地连接到集群并使用token:

    kubectl get pods --namespace=test
    
  4. 执行操作:一旦验证成功,你就可以使用kubectl命令来对特定命名空间内的资源进行各种操作,如部署应用、查看日志、更新配置等。

请注意,使用token直接在kubectl命令中进行身份验证可能不如使用服务账户或用户账户安全,因为如果token被泄露,它可能会被恶意使用。因此,确保token的安全性非常重要,比如定期更换token、使用最小权限原则等。

注:本篇学习笔记内容参考杜宽的《云原生Kubernetes全栈架构师》,视频、资料文档等,大家可以多多支持!还有YinJayChen语雀k8s训练营“我为什么这么菜”知乎博主等资料文档,感谢无私奉献!

posted @ 2024-07-01 02:32  zzbao  阅读(188)  评论(0)    收藏  举报