用户 伪装(Impersonation) 是一种允许已认证用户通过 HTTP 头部以另一个用户、 组或服务账号身份执行操作的方法。
用户可以通过伪装头部以另一个用户的身份执行操作。 这些头部允许请求手动覆盖请求认证所得到的用户信息。 例如,管理员可以使用此特性临时伪装成另一个用户, 查看某个请求是否被拒绝,从而调试鉴权策略。
伪装请求首先以发出请求的用户身份通过认证,然后切换到被伪装的用户信息。
以下 HTTP 头部可用于执行伪装请求:
Impersonate-User:要伪装成的用户名。Impersonate-Uid:表示被伪装用户的唯一标识符。可选。
需要设置 "Impersonate-User"。Kubernetes 不对此字符串施加任何格式要求。Impersonate-Group:要伪装成的组名。可以多次提供以设置多个组。
可选。需要设置 "Impersonate-User"。Impersonate-Extra-( extra name ):用于将额外字段关联到用户的动态头部。
可选。需要设置 "Impersonate-User"。为了被一致地保留,( extra name )
必须是小写,并且所有不是 HTTP 头部标签合法字符
的字符都必须采用 UTF-8 并进行百分号编码。在 1.11.3(以及 1.10.7、1.9.11)之前,( extra name ) 只能包含
HTTP 头部标签中的合法字符。
Impersonate-Uid 仅在 1.22.0 及更高版本中可用。
伪装带有组的用户时所使用的伪装头部示例:
Impersonate-User: jane.doe@example.com
Impersonate-Group: developers
Impersonate-Group: admins
伪装带有 UID 和额外字段的用户时所使用的伪装头部示例:
Impersonate-User: jane.doe@example.com
Impersonate-Uid: 06f6ce97-e2c5-4ab8-7ba5-7654dd08d52b
Impersonate-Extra-dn: cn=jane,ou=engineers,dc=example,dc=com
Impersonate-Extra-acme.com%2Fproject: some-project
Impersonate-Extra-scopes: view
Impersonate-Extra-scopes: development
使用 kubectl 时,设置 --as 命令行参数可以配置 Impersonate-User
头部;还可以设置 --as-group 标志来配置 Impersonate-Group 头部,
设置 --as-uid 标志(1.23)来配置 Impersonate-Uid 头部,以及设置
--as-user-extra 标志(1.35)来配置 Impersonate-Extra-( extra name ) 头部。
kubectl drain mynode
Error from server (Forbidden): User "clark" cannot get nodes at the cluster scope. (get nodes mynode)
设置 --as 和 --as-group 标志:
kubectl drain mynode --as=superman --as-group=system:masters
node/mynode cordoned
node/mynode drained
若要伪装用户、用户标识符(UID)、组或额外字段,执行伪装的用户必须能够对被伪装的属性类别 ("user"、"uid"、"group" 等)执行 impersonate 动词。对于启用了 RBAC 鉴权插件的集群,以下 ClusterRole 包含了设置用户和组伪装头部所需的规则:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: impersonator
rules:
- apiGroups: [""]
resources: ["users", "groups", "serviceaccounts"]
verbs: ["impersonate"]
对于伪装,额外字段和被伪装的 UID 都位于 "authentication.k8s.io" apiGroup 下。
额外字段被作为 "userextras" 资源的子资源来评估。若要允许用户针对额外字段
scopes 和 UID 使用伪装头部,应向用户授予以下角色:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: scopes-and-uid-impersonator
rules:
# 可以设置 "Impersonate-Extra-scopes" 头部和 "Impersonate-Uid" 头部。
- apiGroups: ["authentication.k8s.io"]
resources: ["userextras/scopes", "uids"]
verbs: ["impersonate"]
还可以通过限制资源可接受的 resourceNames 集合来约束伪装头部的取值。
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: limited-impersonator
rules:
# 可以伪装用户 "jane.doe@example.com"
- apiGroups: [""]
resources: ["users"]
verbs: ["impersonate"]
resourceNames: ["jane.doe@example.com"]
# 可以伪装组 "developers" 和 "admins"
- apiGroups: [""]
resources: ["groups"]
verbs: ["impersonate"]
resourceNames: ["developers","admins"]
# 可以伪装取值为 "view" 和 "development" 的额外字段 "scopes"
- apiGroups: ["authentication.k8s.io"]
resources: ["userextras/scopes"]
verbs: ["impersonate"]
resourceNames: ["view", "development"]
# 可以伪装 UID "06f6ce97-e2c5-4ab8-7ba5-7654dd08d52b"
- apiGroups: ["authentication.k8s.io"]
resources: ["uids"]
verbs: ["impersonate"]
resourceNames: ["06f6ce97-e2c5-4ab8-7ba5-7654dd08d52b"]
伪装用户或组允许你像该用户或组一样执行任何操作;因此,伪装不受命名空间范围约束。 如果你想使用 Kubernetes RBAC 允许伪装,这需要使用 ClusterRole 和 ClusterRoleBinding, 而不是 Role 和 RoleBinding。
授予对 ServiceAccount 的伪装权限是命名空间范围的,但被伪装的 ServiceAccount 可能会在命名空间之外执行操作。
Kubernetes v1.36 [beta](默认启用)使用 impersonate 动词时,伪装无法被限制或限定范围。它要么授予完整伪装权限, 要么完全不授予。一旦被授予伪装某个用户的权限,你就可以跨所有资源和命名空间执行该用户可执行的任何操作。
借助受限伪装,可以限制伪装者只能在特定资源上为特定操作伪装成另一个用户, 而不是能够执行被伪装用户可执行的所有操作。
通过设置 ConstrainedImpersonation
特性门控可以启用此特性。
受限伪装需要两个独立权限:
default 命名空间中
list 和 watch Pod)。这意味着伪装者可以被限制为只能针对特定操作伪装成另一个用户。
受限伪装定义了三种不同模式,每种模式都有自己的一组动词:
使用此模式伪装普通用户(不是服务账号或节点)。当 Impersonate-User 头部值满足以下条件时,
适用此模式:
system:serviceaccount: 开头system:node: 开头动词:
impersonate:user-info:伪装特定用户、组、UID 或额外字段的权限。impersonate-on:user-info:<verb>:伪装普通用户时执行 <verb> 的权限。使用此模式伪装 ServiceAccount。
动词:
impersonate:serviceaccount:伪装特定服务账号的权限。impersonate-on:serviceaccount:<verb>:伪装服务账号时执行 <verb> 的权限。使用这些模式伪装节点。当 Impersonate-User 头部值以 system:node: 开头时,
适用此模式。
动词:
impersonate:arbitrary-node:伪装任意指定节点的权限。impersonate:associated-node:仅伪装伪装者所绑定节点的权限。impersonate-on:arbitrary-node:<verb>:伪装任意节点时执行 <verb> 的权限。impersonate-on:associated-node:<verb>:伪装关联节点时执行 <verb> 的权限。impersonate:associated-node 动词仅在伪装者是绑定到其尝试伪装节点的服务账号时适用。
这是通过检查服务账号的用户信息中是否包含键为 authentication.kubernetes.io/node-name
且与被伪装节点匹配的额外字段来确定的。
所有受限伪装权限都使用 authentication.k8s.io API 组。以下展示如何配置不同模式。
此示例展示如何允许某个服务账号伪装名为 jane.doe@example.com 的用户,
但只能在 default 命名空间中 list 和 watch Pod。你既需要一个用于身份权限的
ClusterRoleBinding,也需要一个用于操作权限的 RoleBinding。
步骤 1:授予伪装用户身份的权限
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: impersonate-jane-identity
rules:
- apiGroups: ["authentication.k8s.io"]
resources: ["users"]
resourceNames: ["jane.doe@example.com"]
verbs: ["impersonate:user-info"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: impersonate-jane-identity
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: impersonate-jane-identity
subjects:
- kind: ServiceAccount
name: my-controller
namespace: default
步骤 2:授予伪装时执行特定操作的权限
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: impersonate-list-watch-pods
namespace: default
rules:
- apiGroups: [""]
resources: ["pods"]
verbs:
- "impersonate-on:user-info:list"
- "impersonate-on:user-info:watch"
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: impersonate-list-watch-pods
namespace: default
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: impersonate-list-watch-pods
subjects:
- kind: ServiceAccount
name: my-controller
namespace: default
现在,my-controller 服务账号可以伪装成 jane.doe@example.com 来列举和监视
default 命名空间中的 Pod,但不能执行删除 Pod 或访问其他命名空间中资源等其他操作。
若要允许伪装 production 命名空间中名为 app-sa 的服务账号来创建和更新 Deployment:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: impersonate-app-sa
namespace: default
rules:
- apiGroups: ["authentication.k8s.io"]
resources: ["serviceaccounts"]
resourceNames: ["app-sa"]
# 对于服务账号,你必须在 RoleBinding 中指定命名空间
verbs: ["impersonate:serviceaccount"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: impersonate-manage-deployments
namespace: production
rules:
- apiGroups: ["apps"]
resources: ["deployments"]
verbs:
- "impersonate-on:serviceaccount:create"
- "impersonate-on:serviceaccount:update"
- "impersonate-on:serviceaccount:patch"
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: impersonate-app-sa
namespace: default
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: impersonate-app-sa
subjects:
- kind: ServiceAccount
name: deputy-controller
namespace: default
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: impersonate-manage-deployments
namespace: production
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: impersonate-manage-deployments
subjects:
- kind: ServiceAccount
name: deputy-controller
namespace: default
若要允许 default 命名空间中的 node-impersonator ServiceAccount
伪装名为 mynode 的节点来获取和列举 Pod:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: impersonate-node-sa
rules:
- apiGroups: ["authentication.k8s.io"]
resources: ["nodes"]
resourceNames: ["mynode"]
verbs: ["impersonate:arbitrary-node"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: impersonate-list-pods
rules:
- apiGroups: [""]
resources: ["pods"]
verbs:
- "impersonate-on:arbitrary-node:list"
- "impersonate-on:arbitrary-node:get"
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: impersonate-node-sa
namespace: default
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: impersonate-node-sa
subjects:
- kind: ServiceAccount
name: node-impersonator
namespace: default
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: impersonate-list-pods
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: impersonate-list-pods
subjects:
- kind: ServiceAccount
name: node-impersonator
namespace: default
对于需要读取自身节点上 Pod、但不具有集群范围 Pod 访问权限的节点代理 (例如 CNI 插件),这是一种常见模式。
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: impersonate-associated-node-identity
rules:
- apiGroups: ["authentication.k8s.io"]
resources: ["nodes"]
verbs: ["impersonate:associated-node"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: impersonate-list-pods-on-node
rules:
- apiGroups: [""]
resources: ["pods"]
verbs:
- "impersonate-on:associated-node:list"
- "impersonate-on:associated-node:get"
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: node-agent-impersonate-node
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: impersonate-associated-node-identity
subjects:
- kind: ServiceAccount
name: node-agent
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: node-agent-impersonate-list-pods
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: impersonate-list-pods-on-node
subjects:
- kind: ServiceAccount
name: node-agent
namespace: kube-system
控制器将使用 Downward API 获取节点名称:
env:
- name: MY_NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
然后配置 kubeconfig 以进行伪装:
kubeConfig, _ := clientcmd.BuildConfigFromFlags("", "")
kubeConfig.Impersonate = rest.ImpersonationConfig{
UserName: "system:node:" + os.Getenv("MY_NODE_NAME"),
}
从客户端角度看,使用受限伪装与使用传统伪装完全相同。你使用相同的伪装头部:
Impersonate-User: jane.doe@example.com
或者使用 kubectl:
kubectl get pods -n default --as=jane.doe@example.com
区别完全在于 API 服务器所执行的鉴权检查。
impersonate 动词 如果你已有使用 impersonate 动词的 RBAC 规则,启用此特性门控后,这些规则会继续生效。
发出伪装请求时,API 服务器首先检查受限伪装权限。如果这些检查失败,
它会回退为检查 impersonate 权限。
系统会为每个伪装请求记录审计事件,以帮助跟踪伪装的使用方式。
当请求使用受限伪装时,审计事件包含一个 authenticationMetadata 对象,
其中的 impersonationConstraint 字段表明使用哪个受限伪装动词对请求进行了鉴权。
对于耗时超过 500ms 的非 watch 请求,API 服务器还会向审计事件添加
apiserver.latency.k8s.io/impersonation 注解,记录处理伪装所用的时间
(以及其他对总耗时有贡献的处理程序)。
审计事件示例:
{
"kind": "Event",
"apiVersion": "audit.k8s.io/v1",
"user": {
"username": "system:serviceaccount:default:my-controller"
},
"impersonatedUser": {
"username": "jane.doe@example.com"
},
"authenticationMetadata": {
"impersonationConstraint": "impersonate:user-info"
},
"annotations": {
"apiserver.latency.k8s.io/impersonation": "100ms"
},
"verb": "list",
"objectRef": {
"resource": "pods",
"namespace": "default"
}
}
impersonationConstraint 值表示使用了哪种模式
(例如 impersonate:user-info、impersonate:associated-node)。
具体操作(例如 list)可以根据审计事件中的 verb 字段确定。对于慢请求,
延迟注解会记录为该请求处理伪装所用的时间(例如 100ms)。
kube-apiserver 为受限伪装公开以下 Prometheus 指标:
apiserver_impersonation_attempts_total{mode, decision}:计数器,每次伪装尝试都会递增。
mode 是 associated-node、arbitrary-node、serviceaccount、user-info
或 legacy 之一。decision 为 allowed 或 denied。apiserver_impersonation_attempts_duration_seconds{mode, decision}:直方图,
跟踪解析被伪装用户所用的时间。mode 和 decision 标签的取值与上面相同。
由于处理程序内部存在缓存,这反映的是伪装请求的摊销延迟成本。apiserver_impersonation_authorization_attempts_total{mode, decision}:计数器,
每当伪装尝试调用鉴权器时递增。mode 和 decision 标签的取值与上面相同。apiserver_impersonation_authorization_attempts_duration_seconds{mode, decision}:
直方图,跟踪鉴权器处理每次伪装尝试所用的时间。mode 和 decision
标签的取值与上面相同。对于指标 apiserver_impersonation_attempts_total{mode, decision} 和
apiserver_impersonation_attempts_duration_seconds{mode, decision},
当 decision 为 denied 时,mode 为空字符串。