用户模拟 (User Impersonation)

用户模拟 (User Impersonation) 是一种允许已认证用户通过 HTTP 头信息以其他用户、组或服务账号身份行事的方法。

用户可以通过模拟头信息以其他用户身份行事。这些头信息允许请求手动覆盖请求所认证的用户信息。例如,管理员可以使用此功能通过临时模拟其他用户来调试授权策略,并观察请求是否被拒绝。

模拟请求首先会以请求者的身份进行认证,然后切换到被模拟的用户信息。

  • 用户使用其凭据以及模拟头信息发出 API 调用。
  • API 服务器对用户进行认证。
  • API 服务器确保已认证的用户拥有模拟权限。
  • 请求的用户信息被模拟值替换。
  • 请求被评估,授权行为作用于模拟的用户信息上。

以下 HTTP 头信息可用于执行模拟请求:

  • Impersonate-User: 要模拟的用户名。
  • Impersonate-Uid: 代表被模拟用户的唯一标识符。可选。需要 "Impersonate-User"。Kubernetes 对此字符串的格式没有任何要求。
  • Impersonate-Group: 要模拟的组名。可以多次提供以设置多个组。可选。需要 "Impersonate-User"。
  • Impersonate-Extra-( extra name ): 用于将额外字段与用户关联的动态头信息。可选。需要 "Impersonate-User"。为了保持一致,( extra name ) 必须是小写,并且任何在 HTTP 头标签中不合法的字符都必须是 utf8 并经过百分号编码

说明

在 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:
# Can set "Impersonate-Extra-scopes" header and the "Impersonate-Uid" header.
- apiGroups: ["authentication.k8s.io"]
  resources: ["userextras/scopes", "uids"]
  verbs: ["impersonate"]

模拟头信息的值也可以通过限制资源可以采用的 resourceNames 集合来加以限制。

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: limited-impersonator
rules:
# Can impersonate the user "jane.doe@example.com"
- apiGroups: [""]
  resources: ["users"]
  verbs: ["impersonate"]
  resourceNames: ["jane.doe@example.com"]

# Can impersonate the groups "developers" and "admins"
- apiGroups: [""]
  resources: ["groups"]
  verbs: ["impersonate"]
  resourceNames: ["developers","admins"]

# Can impersonate the extras field "scopes" with the values "view" and "development"
- apiGroups: ["authentication.k8s.io"]
  resources: ["userextras/scopes"]
  verbs: ["impersonate"]
  resourceNames: ["view", "development"]

# Can impersonate the 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 可能在命名空间之外执行操作。

受限模拟 (Constrained Impersonation)

特性状态: Kubernetes v1.36 [beta](默认启用)

使用 impersonate 动词时,模拟无法被限制或限定范围。它要么授予完全模拟权限,要么完全不授予。一旦被授予模拟用户的权限,你就可以执行该用户在所有资源和命名空间中能执行的任何操作。

通过受限模拟,可以将模拟者限制为仅在特定资源上针对特定操作模拟另一个用户,而不是能够执行被模拟用户能执行的所有操作。

此特性通过设置 ConstrainedImpersonation 特性门控来启用。

理解受限模拟

受限模拟需要两个独立的权限

  1. 模拟特定身份的权限(用户、UID、组、服务账号或节点)
  2. 在模拟时执行特定范围内特定操作的权限(例如,仅在 default 命名空间中 listwatch Pod)

这意味着模拟者可以被限制为仅针对特定操作模拟另一个用户。

模拟模式

受限模拟定义了三种不同的模式,每种模式都有其自己的一套动词:

user-info 模式

使用此模式模拟普通用户(非服务账号或节点)。此模式适用于 Impersonate-User 头值:

  • system:serviceaccount: 开头
  • system:node: 开头

动词:

  • impersonate:user-info - 模拟特定用户、组、UID 或额外字段的权限
  • impersonate-on:user-info:<verb> - 模拟普通用户时执行 <verb> 的权限

ServiceAccount 模式

使用此模式模拟 ServiceAccount。

动词:

  • impersonate:serviceaccount - 模拟特定服务账号的权限
  • impersonate-on:serviceaccount:<verb> - 模拟服务账号时执行 <verb> 的权限

arbitrary-node 和 associated-node 模式

使用这些模式来模拟节点。此模式适用于 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 且匹配被模拟节点的额外字段来确定。

使用 RBAC 配置受限模拟

所有受限模拟权限都使用 authentication.k8s.io API 组。以下是如何配置不同模式的方法:

示例:模拟用户执行特定操作

此示例展示了如何允许服务账号模拟名为 jane.doe@example.com 的用户,但仅限于在 default 命名空间中 listwatch Pod。你需要同时配置用于身份权限的 ClusterRoleBinding 和用于操作权限的 RoleBinding

第一步:授予模拟用户身份的权限

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

第二步:授予模拟时执行特定操作的权限

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 或访问其他命名空间中的资源等其他操作。

示例:模拟 ServiceAccount

要允许模拟 production 命名空间中名为 app-sa 的服务账号以创建和更新部署 (deployments):

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"]
  # For service accounts, you must specify the namespace in the 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 服务账号模拟名为 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-infoimpersonate:associated-node)。具体操作(例如,list)可以从审计事件中的 verb 字段中确定。对于缓慢的请求,延迟注解记录了为该请求处理模拟所花费的时间(例如,100ms)。

指标

kube-apiserver 为受限模拟公开了以下 Prometheus 指标:

  • apiserver_impersonation_attempts_total{mode, decision}: 一个在每次尝试模拟时递增的计数器。mode 可以是 associated-nodearbitrary-nodeserviceaccountuser-infolegacy 之一。decisionalloweddenied
  • apiserver_impersonation_attempts_duration_seconds{mode, decision}: 一个跟踪解析被模拟用户所花费时间的直方图。modedecision 标签具有与上述相同的值。由于处理程序内部的缓存,这反映了模拟请求的摊销延迟成本。
  • apiserver_impersonation_authorization_attempts_total{mode, decision}: 一个在每次模拟尝试调用授权器时递增的计数器。modedecision 标签具有与上述相同的值。
  • apiserver_impersonation_authorization_attempts_duration_seconds{mode, decision}: 一个跟踪每次模拟尝试中授权器所花费时间的直方图。modedecision 标签具有与上述相同的值。

对于指标 apiserver_impersonation_attempts_total{mode, decision}apiserver_impersonation_attempts_duration_seconds{mode, decision},当 decisiondenied 时,mode 为空字符串。

接下来


最后修改时间:2026 年 2 月 25 日 下午 4:00 PST:KEP-5284 受限模拟文档 (3793b2f6cd)