本页面介绍了规划基于角色的访问权限控制 (RBAC) 政策的最佳做法。如需了解如何在 Google Kubernetes Engine (GKE) 中实现 RBAC,请参阅配置基于角色的访问权限控制。
RBAC 是 Kubernetes 中的一个核心安全功能,可让您创建精细的权限,以管理用户和工作负载可以对集群中的资源执行的操作。作为平台管理员,您可以创建 RBAC 角色并将这些角色绑定到主体,这些主体是经过身份验证的用户,例如服务账号或 Google 群组。
准备工作
确保熟悉以下概念:
如需查看本指南的核对清单,请参阅核对清单摘要。
RBAC 的工作原理
RBAC 支持以下类型的角色和绑定:
- ClusterRole:一组可以应用于任何命名空间或整个集群的权限。
- 角色:一组仅限于单个命名空间的权限。
- ClusterRoleBinding:将
ClusterRole
绑定到集群中所有命名空间的用户或群组。 - RoleBinding:将
Role
或ClusterRole
绑定到特定命名空间中的用户或群组。
您可以在 Role
或 ClusterRole
中将权限定义为 rules
。角色中的每个 rules
字段都包含 API 组、该 API 组中的 API 资源以及允许对这些资源使用的动词(操作)。(可选)您可以使用 resourceNames
字段将动词的范围限定为已命名的 API 资源实例。如需查看示例,请参阅仅限访问特定资源实例。
定义角色后,使用 RoleBinding
或 ClusterRoleBinding
将该角色绑定到主体。根据您要在单个命名空间中还是在多个命名空间中授予权限,选择绑定类型。
RBAC 角色设计
遵循最小权限原则
在 RBAC 角色中分配权限时,请遵循最小权限原则并授予执行任务所需的最低权限。使用最小权限原则可以降低集群被盗用时提升权限的可能性,并降低过度访问导致安全事件的可能性。
设计角色时,请仔细考虑常见的提升权限风险,例如 escalate
或 bind
动词、PersistentVolume 的 create
访问权限或证书签名请求的 create
访问权限。如需查看风险列表,请参阅 Kubernetes RBAC - 提升权限风险。
避免使用默认角色和群组
Kubernetes 会创建一组默认 ClusterRole 和 ClusterRoleBinding,供您用于 API 发现和启用代管式组件功能。这些默认角色授予的权限可能比较广泛,具体取决于角色。Kubernetes 还有一组默认用户和用户组,由 system:
前缀标识。默认情况下,Kubernetes 和 GKE 会自动将这些角色绑定到默认群组和各种主体。如需查看 Kubernetes 创建的默认角色和绑定的完整列表,请参阅默认角色和角色绑定。
下表介绍了一些默认角色、用户和群组。除非您仔细评估这些角色、用户和群组,否则我们建议您不要与这些角色、用户和群组进行互动,因为与这些资源互动可能会产生意外的安全状况。
名称 | 类型 | 说明 |
---|---|---|
cluster-admin |
ClusterRole | 授予主体对集群中的任何资源执行任何操作的权限。 |
system:anonymous |
用户 | Kubernetes 会将此用户分配给未提供身份验证信息的 API 服务器请求。 将角色绑定到此用户后,所有未经身份验证的用户都会获得该角色授予的权限。 |
system:unauthenticated |
群组 | Kubernetes 将此群组分配给未提供身份验证信息的 API 服务器请求。 将角色绑定到该群组后,所有未经身份验证的用户都会获得该角色授予的权限。 |
system:authenticated |
组 | GKE 会将此群组分配给任何使用 Google 账号(包括所有 Gmail 账号)登录的用户发出的 API 服务器请求。实际上,这与 将角色绑定到该群组后,任何拥有 Google 账号(包括所有 Gmail 账号)的用户都会获得该角色授予的权限。 |
system:masters |
组 | 默认情况下,Kubernetes 会将 向该群组添加您自己的主体后,这些主体将能够对集群中的任何资源执行任何操作。 |
如果可能,请避免创建涉及默认用户、角色和群组的绑定。这可能会导致集群的安全状况出现意外后果。例如:
- 将默认的
cluster-admin
ClusterRole 绑定到system:unauthenticated
群组可让任何未经身份验证的用户访问集群中的所有资源(包括 Secret)。这些特权高的绑定由大规模恶意软件活动等攻击主动作为目标。 - 将自定义角色绑定到
system:unauthenticated
群组会使未经身份验证的用户获得该角色授予的权限。
如果可能,请使用以下准则:
- 请勿将您自己的主体添加到
system:masters
组。 - 请勿将
system:unauthenticated
群组绑定到任何 RBAC 角色。 - 请勿将
system:authenticated
群组绑定到任何 RBAC 角色。 - 请勿将
system:anonymous
用户绑定到任何 RBAC 角色。 - 请勿将
cluster-admin
ClusterRole 绑定到您自己的主体或任何默认用户和群组。如果您的应用需要许多权限,请确定所需的确切权限,然后为此创建一个特定角色。 - 在绑定主体之前评估其他默认角色授予的权限。
- 在修改这些群组的成员之前,请评估绑定到默认群组的角色。
检测并阻止使用默认角色和群组
您应评估集群,以确定是否已使用 ClusterRoleBinding 和 RoleBinding 绑定 system:anonymous
用户或者绑定 system:unauthenticated
或 system:authenticated
群组。
ClusterRoleBinding
列出主体为
system:anonymous
、system:unauthenticated
或system:authenticated
的任何 ClusterRoleBinding 的名称:kubectl get clusterrolebindings -o json \ | jq -r '["Name"], ["-----"], (.items[] | select((.subjects | length) > 0) | select(any(.subjects[]; .name == "system:anonymous" or .name == "system:unauthenticated" or .name == "system:authenticated")) | [.metadata.namespace, .metadata.name]) | @tsv'
输出应仅列出以下 ClusterRoleBinding:
Name ---- "system:basic-user" "system:discovery" "system:public-info-viewer"
如果输出包含其他非默认绑定,请对每个附加绑定执行以下操作。如果输出不包含非默认绑定,请跳过以下步骤。
列出与绑定关联的角色的权限:
kubectl get clusterrolebinding CLUSTER_ROLE_BINDING_NAME -o json \ | jq ' .roleRef.name +" " + .roleRef.kind' \ | sed -e 's/"//g' \ | xargs -l bash -c 'kubectl get $1 $0 -o yaml'
将
CLUSTER_ROLE_BINDING_NAME
替换为非默认 ClusterRoleBinding 的名称。输出类似于以下内容:
apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: ... rules: - apiGroups: - "" resources: - secrets verbs: - get - watch - list
如果您确定输出中的权限可安全授予给默认用户或群组,则无需执行进一步操作。如果您确定绑定授予的权限不安全,请继续执行下一步。
从集群中删除不安全的绑定:
kubectl delete clusterrolebinding CLUSTER_ROLE_BINDING_NAME
将
CLUSTER_ROLE_BINDING_NAME
替换为要删除的 ClusterRoleBinding 的名称。
RoleBinding
列出主体为
system:anonymous
、system:unauthenticated
或system:authenticated
的任何 RoleBinding 的命名空间和名称:kubectl get rolebindings -A -o json \ | jq -r '["Namespace", "Name"], ["---------", "-----"], (.items[] | select((.subjects | length) > 0) | select(any(.subjects[]; .name == "system:anonymous" or .name == "system:unauthenticated" or .name == "system:authenticated")) | [.metadata.namespace, .metadata.name]) | @tsv'
如果您的集群配置正确,则输出应显示为空白。 如果输出包含任何非默认绑定,请对每个附加绑定执行以下步骤。如果输出为空,请跳过以下步骤。
如果您只知道 RoleBinding 的名称,则可以使用以下命令在所有命名空间中查找匹配的角色绑定:
kubectl get rolebindings -A -o json \ | jq -r '["Namespace", "Name"], ["---------", "-----"], (.items[] | select((.subjects | length) > 0) | select(.metadata.name == "ROLE_BINDING_NAME") | [.metadata.namespace, .metadata.name]) | @tsv'
将
ROLE_BINDING_NAME
替换为非默认 RoleBinding 的名称。列出与绑定关联的角色的权限:
kubectl get rolebinding ROLE_BINDING_NAME --namespace ROLE_BINDING_NAMESPACE -o json \ | jq ' .roleRef.name +" " + .roleRef.kind' \ | sed -e 's/"//g' \ | xargs -l bash -c 'kubectl get $1 $0 -o yaml --namespace ROLE_BINDING_NAMESPACE'
替换以下内容:
ROLE_BINDING_NAME
:非默认 RoleBinding 的名称。ROLE_BINDING_NAMESPACE
:非默认 RoleBinding 的命名空间。
输出类似于以下内容:
apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: ... rules: - apiGroups: - "" resources: - secrets verbs: - get - watch - list
如果您确定输出中的权限可安全授予给默认用户或群组,则无需执行进一步操作。如果您确定绑定授予的权限不安全,请继续执行下一步。
从集群中删除不安全的绑定:
kubectl delete rolebinding ROLE_BINDING_NAME --namespace ROLE_BINDING_NAMESPACE
替换以下内容:
ROLE_BINDING_NAME
:要删除的 RoleBinding 的名称。ROLE_BINDING_NAMESPACE
:要删除的 RoleBinding 的命名空间。
将权限限制到命名空间级别
根据工作负载或用户的需求,使用如下绑定和角色:
- 要授予对一个命名空间中的资源的访问权限,请使用
Role
和RoleBinding
。 - 要授予对多个命名空间中资源的访问权限,请在每个命名空间中使用
ClusterRole
和RoleBinding
。 - 要授予对每个命名空间中资源的访问权限,请使用
ClusterRole
和ClusterRoleBinding
。
在尽可能少的命名空间中授予权限。
请勿使用通配符
*
字符是一个适用于所有内容的通配符。避免在规则中使用通配符。在 RBAC 规则中明确指定 API 组、资源和动词。例如,在 verbs
字段中指定 *
将授予 get
、list
、watch
、patch
、update
、deletecollection
和 delete
权限。下表举例说明了如何避免在规则中使用通配符:
推荐 | 不推荐 |
---|---|
- rules: apiGroups: ["apps","extensions"] resources: ["deployments"] verbs: ["get","list","watch"] 专门向 |
- rules: apiGroups: ["*"] resources: ["deployments"] verbs: ["get","list","watch"] 将动词授予任何 API 组中的 |
- rules: apiGroups: ["apps", "extensions"] resources: ["deployments"] verbs: ["get", "list", "watch"] 仅将 |
- rules: apiGroups: ["apps", "extensions"] resources: ["deployments"] verbs: ["*"] 授予所有动词,包括 |
使用单独规则对特定资源授予最小访问权限
规划规则时,请尝试以下简要步骤,在每个角色中采用更高效的最小权限规则设计:
- 为主体需要访问的每项资源上的动词草拟单独的 RBAC 规则。
- 草拟规则后,分析规则,以检查多条规则是否具有相同的
verbs
列表。将这些规则合并为一条规则。 - 请将其余的所有规则彼此分散。
这种方法可实现更有条理的规则设计,将对多个资源授予相同动词的规则组合起来,将为资源授予不同动词的规则彼此分散。
例如,如果您的工作负载需要获取 deployments
资源的权限,但需要 daemonsets
资源的 list
和 watch
权限,则您应该在创建角色是使用单独规则。当您将 RBAC 角色绑定到工作负载时,该角色将无法对 deployments
使用 watch
。
再举一例,如果您的工作负载需要 pods
资源和 daemonsets
资源的 get
和 watch
权限,您可以将它们组合成一条规则,因为工作负载需要在这两个资源上使用相同的动词。
在下表中,这两种规则设计均有效,但拆分规则会根据需要更精细地限制资源访问权限:
推荐 | 不推荐 |
---|---|
- rules: apiGroups: ["apps"] resources: ["deployments"] verbs: ["get"] - rules: apiGroups: ["apps"] resources: ["daemonsets"] verbs: ["list", "watch"] 为 Deployment 授予 |
- rules: apiGroups: ["apps"] resources: ["deployments", "daemonsets"] verbs: ["get","list","watch"] 将动词同时授予 Deployment 和 DaemonSet。可能对 |
- rules: apiGroups: ["apps"] resources: ["daemonsets", "deployments"] verbs: ["list", "watch"] 组合两条规则,因为主体需要对 |
- rules: apiGroups: ["apps"] resources: ["daemonsets"] verbs: ["list", "watch"] - rules: apiGroups: ["apps"] resources: ["deployments"] verbs: ["list", "watch"] 这些拆分规则的结果将与组合规则相同,但会导致角色清单产生不必要的杂乱 |
仅限访问特定资源实例
通过 RBAC,您可以使用规则中的 resourceNames
字段仅限访问资源的特定命名实例。例如,如果您正在编写仅需要 update
seccomp-high
ConfigMap 的 RBAC 角色,则可以使用 resourceNames
仅指定该 ConfigMap。尽可能使用 resourceNames
。
推荐 | 不推荐 |
---|---|
- rules: apiGroups: [""] resources: ["configmaps"] resourceNames: ["seccomp-high"] verbs: ["update"] 仅限主体更新 |
- rules: apiGroups: [""] resources: ["configmaps"] verbs: ["update"] 主体可以更新命名空间中的 |
- rules: apiGroups: [""] resources: ["configmaps"] verbs: ["list"] - rules: apiGroups: [""] resources: ["configmaps"] resourceNames: ["seccomp-high"] verbs: ["update"] 向命名空间中所有 ConfigMap 授予 |
- rules: apiGroups: [""] resources: ["configmaps"] verbs: ["update", "list"] 为所有 ConfigMap 授予 |
不允许服务账号修改 RBAC 资源
请勿将对 rbac.authorization.k8s.io
API 组具有 bind
、escalate
、create
、update
或 patch
权限的 Role
或 ClusterRole
资源绑定到任何命名空间中的服务账号。特别是 escalate
和 bind
可让攻击者绕过 RBAC 中内置的升级预防机制。
Kubernetes 服务账号
为每个工作负载创建 Kubernetes 服务账号
为每个工作负载创建单独的 Kubernetes 服务账号。将最小权限 Role
或 ClusterRole
绑定到该服务账号。
请勿使用默认服务账号
Kubernetes 会在每个命名空间中创建一个名为 default
的服务账号。default
服务账号会自动分配给未在清单中明确指定服务账号的 Pod。避免将 Role
或 ClusterRole
绑定到 default
服务账号。Kubernetes 可能会将 default
服务账号分配给不需要在这些角色中授予的访问权限的 Pod。
请勿自动装载服务账号令牌
Pod 规范中的 automountServiceAccountToken
字段指示 Kubernetes 将 Kubernetes 服务账号的凭据注入 Pod。Pod 可以使用此令牌向 Kubernetes API 服务器发出经过身份验证的请求。此字段的默认值为 true
。
在所有 GKE 版本中,如果您的 Pod 不需要与 API 服务器通信,请在 Pod 规范中设置 automountServiceAccountToken=false
。
首选临时令牌而不是基于 Secret 的令牌
默认情况下,节点上的 kubelet 进程会检索每个 Pod 中自动轮替的短期服务账号令牌。除非您在 Pod 规范中将 automountServiceAccountToken
字段设置为 false
,否则 kubelet 会将此令牌作为投影的卷装载在 Pod 上。从 Pod 对 Kubernetes API 的任何调用会使用此令牌向 API 服务器进行身份验证。
如果您要手动检索服务账号令牌,请避免使用 Kubernetes Secret 存储令牌。基于 Secret 的服务账号令牌是不会过期且不会自动轮替的旧版凭据。如果您需要服务账号的凭据,请使用 TokenRequest
API 获取可自动轮替的短期令牌。
持续审核 RBAC 权限
定期审核 RBAC 角色和访问权限,以确定潜在的上报路径和冗余规则。例如,假设您没有删除将具有特殊权限的 Role
绑定到已删除用户的 RoleBinding
。如果攻击者在该命名空间中创建一个与已删除用户同名的用户账号,则他们将绑定到该 Role
并继承相同的访问权限。定期审核可将此风险降至最低。