k8s 基于角色的访问控制 RBAC

发布时间 2023-08-03 18:26:13作者: 小吉猫

RBAC介绍

DAC(自主访问控制)、MAC(强制访问控制)、RBAC(基于角色的访问控制)和ABAC(基于属性的访问控制)这4种主流的权限管理模型中,Kubernetes支持使用后两种完成普通账户和服务账户的权限管理,另外支持的权限管理模型还有Node和Webhook两种。
RBAC是一种新型、灵活且使用广泛的访问控制机制,它将权限授予角色,通过让“用户”扮演一到多个“角色”完成灵活的权限管理,这有别于传统访问控制机制中将权限直接赋予使用者的方式。相对于Kubernetes支持的ABAC和Webhook等授权机制,RBAC具有如下优势。
1. 对集群中的资源和非资源型URL的权限实现了完整覆盖。
2. 整个RBAC完全由少数几个API对象实现,而且与其他API对象一样可以用kubectl或API调用进行操作。
3. 支持权限的运行时调整,无须重新启动API Server。

RBAC授权模型

RBAC是一种特定的权限管理模型,它把可以施加在“资源对象”上的“动作”称为“许可权限”,这些许可权限能够按需组合在一起构建出“角色”及其职能,并通过为“用户账户或组账户”分配一到多个角色完成权限委派。这些能够发出动作的用户在RBAC中也称为“主体”。

RBAC访问控制模型中,授权操作只能通过角色完成,主体只有在分配到角色之后才能行使权限,且仅限于从其绑定的各角色之上继承而来的权限。换句话说,用户的权限仅能够通过角色分配获得,未能得到显式角色委派的用户则不具有任何权限。
简单来说,RBAC就是一种访问控制模型,它以角色为中心界定“谁”(subject)能够“操作”(verb)哪个或哪类“对象”(object)。动作的发出者即“主体”,通常以“账号”为载体,在Kubernetes系统上,它可以是普通账户,也可以是服务账户。“动作”用于表明要执行的具体操作,包括创建、删除、修改和查看等行为,对于API Server来说,即PUT、POST、DELETE和GET等请求方法。而“对象”则是指管理操作能够施加的目标实体,对Kubernetes API来说主要指各类资源对象以及非资源型URL。
API Server是RESTful风格的API,各类客户端由认证插件完成身份验证,而后通过HTTP协议的请求方法指定对目标对象的操作请求,并由授权插件进行授权检查,而操作的对象则是URL路径指定的REST资源。

RBAC API对象

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

Role 和 ClusterRole

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

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

与之相对,ClusterRole 则是一个集群作用域的资源。这两种资源的名字不同(Role 和 ClusterRole) 是因为 Kubernetes 对象要么是名称空间作用域的,要么是集群作用域的,不可两者兼具。
ClusterRole 有若干用法。你可以用它来:

1. 定义对某名称空间域对象的访问权限,并将在个别名称空间内被授予访问权限;
2. 为名称空间作用域的对象设置访问权限,并被授予跨所有名称空间的访问权限;
3. 为集群作用域的资源定义访问权限。
如果你希望在名称空间内定义角色,应该使用 Role; 如果你希望定义集群范围的角色,应该使用 ClusterRole。
Role或ClusterRole对象本身并不能作为动作的执行主体,它们需要“绑定”到主体(例如User、Group或Service Account)之上完成赋权,而后由相应主体执行资源操作。

Role和ClusterRole的资源规范

Role和ClusterRole是API Server内置的两种资源类型,它们在本质上都只是一组许可权限的集合。Role和ClusterRole的资源规范完全相同,该规范没有使用spec字段,而是直接使用rules字段嵌套授权规则列表。规则的基本要素是动作(verb)和相关的目标资源,后者支持指定一个或多个资源类型、特定资源类型下的单个或多个具体的资源,以及非资源类型的URL等。在Role和ClusterRole资源上定义的rules也称为PolicyRule,即策略规则,它可以内嵌的字段有如下几个。
apiVersion:  <string>           # API群组及版本
kind: <string>                  # 资源类型标识
metadata:
  namespace:  <string>          # 名称空间级别的资源,ClusterRole 不需要namespace。
  name: <string>                # 资源名称
rules:  <[]Object>
- apiGroups: <[]string>         # "" 表示核心群组
  resources: <[]string>         # 规则应用的目标资源类型,例如pods、services、deployments和daemonsets等,未同时使用resourceNames字段时,表示指定类型下的所有资源。ResourceAll表示所有资源。
  verbs: <[]string> -required-  # 可应用在此规则匹配到的所有资源类型的操作列表,可用选项有get、list、create、update、patch、watch、proxy、redirect、delete和deletecollection;此为必选字段。
  nonResourceURLs: <[]string>   # 用于定义用户有权限访问的网址列表,它并非名称空间级别的资源,因此只能应用于ClusterRole,Role支持此字段仅是为了格式上的兼容;该字段在一条规则中与resources和resourceNames互斥。
  resourceNames: <[]string>     # 可选字段,指定操作适用的具体目标资源名称。

Role 示例

下面是一个位于 "demoapp" 名字空间的 Role 的示例,可用来授予对 Pod 的读访问权限:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: demoapp
  name: pod-reader
rules:
- apiGroups: [""] # "" 表示核心群组
  resources: ["pods", "services", "pods/log", "pods/exec"]
  verbs: ["get", "watch", "list"]

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"]     # ["nodes", "persistentvolumeclaims", "persistentvolumes"]
  verbs: ["get", "watch", "list"]

RoleBinding 和 ClusterRoleBinding

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

一个 RoleBinding 可以引用同一的名称空间中的任何 Role。 或者,一个 RoleBinding 可以引用某 ClusterRole 并将该 ClusterRole 绑定到 RoleBinding 所在的名称空间。 如果你希望将某 ClusterRole 绑定到集群中所有名称空间,你要使用 ClusterRoleBinding。
创建绑定后,您无法更改它引用的 Role 或 ClusterRole。如果您尝试更改绑定的roleRef,您会收到验证错误。如果您确实想要更改roleRef绑定,则需要删除绑定对象并创建一个替换对象。
这种限制有两个原因:

1. 将 roleRef 设置为不可以改变,这使得可以为用户授予对现有绑定对象的 update 权限, 这样可以让他们管理主体列表,同时不能更改被授予这些主体的角色。
2. 针对不同角色的绑定是完全不一样的绑定。要求通过删除/重建绑定来更改 roleRef, 这样可以确保要赋予绑定的所有主体会被授予新的角色(而不是在允许或者不小心修改了 roleRef 的情况下导致所有现有主体未经验证即被授予新角色对应的权限)。

RoleBinding 和 ClusterRoleBinding资源清单

RoleBinding负责在名称空间级别向普通账户、服务账户或组分配Role或ClusterRole,而ClusterRoleBinding则只能用于在集群级别分配ClusterRole。但二者的配置规范格式完全相同,它们没有spec字段,直接使用subjects和roleRef两个嵌套的字段。其中,subjects的值是一个对象列表,用于给出要绑定的主体,而roleRef的值是单个对象,用于指定要绑定的Role或ClusterRole资源。
apiVersion: <string>            # API群组及版本
kind: <string>                  # 资源类型标识
metadata:
  name: <string>                # 资源名称
  namespace: <string>           # 名称空间级别的资源,ClusterRoleBinding 不需要namespace。
subjects:
- kind: <string>                # 要引用的资源对象(主体)所属的类别,可用值为User、Group和ServiceAccount,必选字段。
  name: <string>                # 引用的主体的名称,必选字段。
  apiGroup: <string>            # 要引用的主体所属的API群组,对于ServiceAccount类的主体来说默认为"",而User和Group类主体的默认值为"rbac.authorization.k8s.io"。
  namespace:<string>           # 引用的主体所属的名称空间,对于非名称空间类型的主体,例如User和Group,其值必须为空,否则授权插件将返回错误信息。
roleRef:
  kind: <string>                # 引用的资源所属的类别,可用值为Role或ClusterRole,必选字段。      
  name: <string>                # 引用的资源(Role或ClusterRole)的名称。
  apiGroup: <string>            # 引用的资源(Role或ClusterRole)所属的API群组,必选字段。

RoleBinding 示例

下面的例子中的 RoleBinding 将 "pod-reader" Role 授予在 "demoapp" 名称空间中的用户 "jane"。 这样,用户 "jane" 就具有了读取 "demoapp" 名称空间中所有 Pod 的权限。
apiVersion: rbac.authorization.k8s.io/v1
# 此角色绑定允许 "jane" 读取 "default" 名字空间中的 Pod
# 你需要在该名字空间中有一个名为 “pod-reader” 的 Role
kind: RoleBinding
metadata:
  name: read-pods
  namespace: demoapp
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

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

对资源的引用

在 Kubernetes API 中,大多数资源都是使用对象名称的字符串表示来呈现与访问的。 例如,对于 Pod 应使用 "pods"。 RBAC 使用对应 API 端点的 URL 中呈现的名字来引用资源。 有一些 Kubernetes API 涉及子资源(subresource),例如 Pod 的日志。 对 Pod 日志的请求看起来像这样:
GET /api/v1/namespaces/{namespace}/pods/{name}/log
在这里,pods 对应名字空间作用域的 Pod 资源,而 log 是 pods 的子资源。 在 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 列表按名称引用资源。 在指定时,可以将请求限定为资源的单个实例。 下面的例子中限制可以 get 和 update 一个名为 my-configmap 的 ConfigMap:
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
你可以使用通配符 * 可以批量引用所有的 resources、apiGroups 和 verbs 对象, 无需逐一引用。 对于 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 确保仅赋予工作负载正常运行所需的权限。

聚合型 ClusterRole

你可以将若干 ClusterRole 聚合(Aggregate) 起来,形成一个复合的 ClusterRole。 作为集群控制面的一部分,控制器会监视带有 aggregationRule 的 ClusterRole 对象集合。aggregationRule 为控制器定义一个标签选择算符供后者匹配应该组合到当前 ClusterRole 的 roles 字段中的 ClusterRole 对象。
注意:
控制平面会覆盖你在聚合 ClusterRole 的 rules 字段中手动指定的所有值。 如果你想更改或添加规则,请在被 aggregationRule 所选中的 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"]

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"]

对主体的引用

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

默认 Roles 和 Role Bindings

API 服务器创建一组默认的 ClusterRole 和 ClusterRoleBinding 对象。 这其中许多是以 system: 为前缀的,用以标识对应资源是直接由集群控制面管理的。 所有的默认 ClusterRole 和 ClusterRoleBinding 都有 kubernetes.io/bootstrapping=rbac-defaults 标签。
注意:
在修改名称包含 system: 前缀的 ClusterRole 和 ClusterRoleBinding 时要格外小心。 对这些资源的更改可能导致集群无法正常运作。

自动协商

在每次启动时,API 服务器都会更新默认 ClusterRole 以添加缺失的各种权限, 并更新默认的 ClusterRoleBinding 以增加缺失的各类主体。 这种自动协商机制允许集群去修复一些不小心发生的修改, 并且有助于保证角色和角色绑定在新的发行版本中有权限或主体变更时仍然保持最新。

如果要禁止此功能,请将默认 ClusterRole 以及 ClusterRoleBinding 的 rbac.authorization.kubernetes.io/autoupdate 注解设置成 false。 注意,缺少默认权限和角色绑定主体可能会导致集群无法正常工作。

如果基于 RBAC 的鉴权机制被启用,则自动协商功能默认是被启用的。

API 发现角色

无论是经过身份验证的还是未经过身份验证的用户, 默认的角色绑定都授权他们读取被认为是可安全地公开访问的 API(包括 CustomResourceDefinitions)。 如果要禁用匿名的未经过身份验证的用户访问,请在 API 服务器配置中中添加 --anonymous-auth=false 的配置选项。

通过运行命令 kubectl 可以查看这些角色的配置信息:
kubectl get clusterroles system:discovery -o yaml
说明:
如果你编辑该 ClusterRole,你所作的变更会被 API 服务器在重启时自动覆盖, 这是通过自动协商机制完成的。要避免这类覆盖操作, 要么不要手动编辑这些角色,要么禁止自动协商机制。
默认 ClusterRole 默认 ClusterRoleBinding 描述
system:basic-user system:authenticated 组 允许用户以只读的方式去访问他们自己的基本信息。在 v1.14 版本之前,这个角色在默认情况下也绑定在 system:unauthenticated 上。
system:discovery system:authenticated 组 允许以只读方式访问 API 发现端点,这些端点用来发现和协商 API 级别。 在 v1.14 版本之前,这个角色在默认情况下绑定在 system:unauthenticated 上。
system:public-info-viewer system:authenticated 和 system:unauthenticated 组 允许对集群的非敏感信息进行只读访问,此角色是在 v1.14 版本中引入的。

面向用户的角色

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

面向用户的 ClusterRole 使用 ClusterRole 聚合以允许管理员在这些 ClusterRole 上添加用于定制资源的规则。如果想要添加规则到 admin、edit 或者 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)进行写操作。 

edit 允许对名字空间的大多数对象进行读/写操作。

此角色不允许查看或者修改角色或者角色绑定。 不过,此角色可以访问 Secret,以名字空间中任何 ServiceAccount 的身份运行 Pod, 所以可以用来了解名字空间内所有服务账户的 API 访问级别。 此角色也不允许对 Kubernetes v1.22+ 创建的 EndpointSlices(或 Endpoints)进行写操作。 

view 允许对名字空间的大多数对象有只读权限。 它不允许查看角色或角色绑定。

此角色不允许查看 Secret,因为读取 Secret 的内容意味着可以访问名字空间中 ServiceAccount 的凭据信息,进而允许利用名字空间中任何 ServiceAccount 的身份访问 API(这是一种特权提升)。

核心组件角色

默认 ClusterRole 默认 ClusterRoleBinding 描述
system:kube-scheduler system:kube-scheduler 用户 允许访问 scheduler 组件所需要的资源。
system:volume-scheduler system:kube-scheduler 用户 允许访问 kube-scheduler 组件所需要的卷资源。
system:kube-controller-manager system:kube-controller-manager 用户 允许访问控制器管理器组件所需要的资源。 各个控制回路所需要的权限在控制器角色详述。
system:node 允许访问 kubelet 所需要的资源,包括对所有 Secret 的读操作和对所有 Pod 状态对象的写操作。

你应该使用 Node 鉴权组件和 NodeRestriction 准入插件而不是 system:node 角色。同时基于 kubelet 上调度执行的 Pod 来授权 kubelet 对 API 的访问。

system:node 角色的意义仅是为了与从 v1.8 之前版本升级而来的集群兼容。

system:node-proxier system:kube-proxy 用户 允许访问 kube-proxy 组件所需要的资源。

其他组件角色

默认 ClusterRole 默认 ClusterRoleBinding 描述
system:auth-delegator 允许将身份认证和鉴权检查操作外包出去。 这种角色通常用在插件式 API 服务器上,以实现统一的身份认证和鉴权。
system:heapster 为 Heapster 组件(已弃用)定义的角色。
system:kube-aggregator 为 kube-aggregator 组件定义的角色。
system:kube-dns 在 kube-system 名字空间中的 kube-dns 服务账户 为 kube-dns 组件定义的角色。
system:kubelet-api-admin 允许 kubelet API 的完全访问权限。
system:node-bootstrapper 允许访问执行 kubelet TLS 启动引导 所需要的资源。
system:node-problem-detector 为 node-problem-detector 组件定义的角色。
system:persistent-volume-provisioner 允许访问大部分动态卷驱动所需要的资源。
system:monitoring system:monitoring 组 允许对控制平面监控端点的读取访问(例如:kube-apiserver 存活和就绪端点(/healthz/livez/readyz), 各个健康检查端点(/healthz/*/livez/*/readyz/*)和 /metrics)。 请注意,各个运行状况检查端点和度量标准端点可能会公开敏感信息。

初始化与预防权限提升

RBAC API 会阻止用户通过编辑角色或者角色绑定来提升权限。 由于这一点是在 API 级别实现的,所以在 RBAC 鉴权组件未启用的状态下依然可以正常工作。

对角色创建或更新的限制

只有在符合下列条件之一的情况下,你才能创建/更新角色:

1. 你已经拥有角色中包含的所有权限,且其作用域与正被修改的对象作用域相同。 (对 ClusterRole 而言意味着集群范围,对 Role 而言意味着相同名字空间或者集群范围)。
2. 你被显式授权在 rbac.authorization.k8s.io API 组中的 roles 或 clusterroles 资源使用 escalate 动词。
例如,如果 user-1 没有列举集群范围所有 Secret 的权限,他将不能创建包含该权限的 ClusterRole。 若要允许用户创建/更新角色:

1. 根据需要赋予他们一个角色,允许他们根据需要创建/更新 Role 或者 ClusterRole 对象。
2. 授予他们在所创建/更新角色中包含特殊权限的权限:
     隐式地为他们授权(如果它们试图创建或者更改 Role 或 ClusterRole 的权限, 但自身没有被授予相应权限,API 请求将被禁止)。
     通过允许他们在 Role 或 ClusterRole 资源上执行 escalate 动作显式完成授权。 这里的 roles 和 clusterroles 资源包含在 rbac.authorization.k8s.io API 组中。

对角色绑定创建或更新的限制

只有你已经具有了所引用的角色中包含的全部权限时,或者你被授权在所引用的角色上执行 bind 动词时,你才可以创建或更新角色绑定。这里的权限与角色绑定的作用域相同。 例如,如果用户 user-1 没有列举集群范围所有 Secret 的能力,则他不可以创建 ClusterRoleBinding 引用授予该许可权限的角色。 如要允许用户创建或更新角色绑定:

1. 赋予他们一个角色,使得他们能够根据需要创建或更新 RoleBinding 或 ClusterRoleBinding 对象。
2. 授予他们绑定某特定角色所需要的许可权限:
     隐式授权下,可以将角色中包含的许可权限授予他们;
     显式授权下,可以授权他们在特定 Role (或 ClusterRole)上执行 bind 动词的权限。
例如,下面的 ClusterRole 和 RoleBinding 将允许用户 user-1 把名字空间 user-1-namespace 中的 admin、edit 和 view 角色赋予其他用户:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: role-grantor
rules:
- apiGroups: ["rbac.authorization.k8s.io"]
  resources: ["rolebindings"]
  verbs: ["create"]
- apiGroups: ["rbac.authorization.k8s.io"]
  resources: ["clusterroles"]
  verbs: ["bind"]
  # 忽略 resourceNames 意味着允许绑定任何 ClusterRole
  resourceNames: ["admin","edit","view"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: role-grantor-binding
  namespace: user-1-namespace
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: role-grantor
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: User
  name: user-1
当启动引导第一个角色和角色绑定时,需要为初始用户授予他们尚未拥有的权限。 对初始角色和角色绑定进行初始化时需要:

  使用用户组为 system:masters 的凭据,该用户组由默认绑定关联到 cluster-admin 这个超级用户角色。

服务账户权限

默认的 RBAC 策略为控制面组件、节点和控制器授予权限。 但是不会对 kube-system 名字空间之外的服务账户授予权限。 (除了授予所有已认证用户的发现权限)

这使得你可以根据需要向特定 ServiceAccount 授予特定权限。 细粒度的角色绑定可带来更好的安全性,但需要更多精力管理。 粗粒度的授权可能导致 ServiceAccount 被授予不必要的 API 访问权限(甚至导致潜在的权限提升), 但更易于管理。
按从最安全到最不安全的顺序,存在以下方法:

为特定应用的服务账户授予角色(最佳实践)

这要求应用在其 Pod 规约中指定 serviceAccountName, 并额外创建服务账户(包括通过 API、应用程序清单、kubectl create serviceaccount 等)。

例如,在名字空间 “my-namespace” 中授予服务账户 “my-sa” 只读权限:
# kubectl create rolebinding my-sa-view   --clusterrole=view   --serviceaccount=my-namespace:my-sa   --namespace=my-namespace

将角色授予某名字空间中的 “default” 服务账户

如果某应用没有指定 serviceAccountName,那么它将使用 “default” 服务账户。
说明:
"default" 服务账户所具有的权限会被授予给名字空间中所有未指定 serviceAccountName 的 Pod。
# kubectl create rolebinding default-view   --clusterrole=view   --serviceaccount=my-namespace:default   --namespace=my-namespace
许多插件组件在 kube-system 名字空间以 “default” 服务账户运行。 要允许这些插件组件以超级用户权限运行,需要将集群的 cluster-admin 权限授予 kube-system 名字空间中的 “default” 服务账户。
注意:
启用这一配置意味着在 kube-system 名字空间中包含以超级用户账号来访问集群 API 的 Secret。
# ubectl create clusterrolebinding add-on-cluster-admin   --clusterrole=cluster-admin   --serviceaccount=kube-system:default

将角色授予名字空间中所有服务账户

如果你想要名字空间中所有应用都具有某角色,无论它们使用的什么服务账户, 可以将角色授予该名字空间的服务账户组。

例如,在名字空间 “my-namespace” 中的只读权限授予该名字空间中的所有服务账户:
# kubectl create rolebinding serviceaccounts-view   --clusterrole=view   --group=system:serviceaccounts:my-namespace   --namespace=my-namespace

在集群范围内为所有服务账户授予一个受限角色(不鼓励) 

如果你不想管理每一个名字空间的权限,你可以向所有的服务账户授予集群范围的角色。

例如,为集群范围的所有服务账户授予跨所有名字空间的只读权限:
# kubectl create clusterrolebinding serviceaccounts-view   --clusterrole=view   --group=system:serviceaccounts

授予超级用户访问权限给集群范围内的所有服务帐户(强烈不鼓励) 

如果你不在乎如何区分权限,你可以将超级用户访问权限授予所有服务账户。
警告:
这样做会允许所有应用都对你的集群拥有完全的访问权限,并将允许所有能够读取 Secret(或创建 Pod)的用户对你的集群有完全的访问权限。
# kubectl create clusterrolebinding serviceaccounts-cluster-admin   --clusterrole=cluster-admin   --group=system:serviceaccounts

EndpointSlices 和 Endpoints 写权限

在 Kubernetes v1.22 之前版本创建的集群里, “edit” 和 “admin” 聚合角色包含对 EndpointSlices(和 Endpoints)的写权限。此访问权限不包含在 Kubernetes 1.22 以及更高版本集群的聚合角色里。
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  annotations:
    kubernetes.io/description: |-
      将端点写入权限添加到 edit 和 admin 角色。此特性因 CVE-2021-25740 在 1.22
      中默认被移除。请参阅 https://issue.k8s.io/103675
      这一设置将允许写者要求 LoadBalancer 或 Ingress 的实现向外暴露后端 IP 地址,
      所暴露的 IP 地址无法通过其他方式访问,
      并且可以规避对这些后端访问进行预防/隔离的网络策略或安全控制机制。
      EndpointSlice 从未包含在 edit 和 admin 角色中,
      因此 EndpointSlice API 没有什么可恢复的。      
  labels:
    rbac.authorization.k8s.io/aggregate-to-edit: "true"
  name: custom:aggregate-to-edit:endpoints # 你可以随意愿更改这个 name
rules:
  - apiGroups: [""]
    resources: ["endpoints"]
    verbs: ["create", "delete", "deletecollection", "patch", "update"]

参考文档

https://kubernetes.io/docs/reference/access-authn-authz/rbac/