mcpField
G
gke-workload-identity

gke-workload-identity

github.com/google云与 DevOps21.6k2.2k 次安装今天更新
代码仓库

关于 gke-workload-identity

用于系统诊断 GKE Pod 无法通过 Workload Identity Federation 访问 Google Cloud API 的只读技能,面向集群运维人员。它按错误特征检查集群、节点池、身份绑定、IAM 权限及元数据服务器,并将修复作为建议交给人工执行。

主要功能

  1. 01检查集群与节点池身份配置
  2. 02分析认证错误和日志
  3. 03核对 KSA 与身份绑定
  4. 04检查目标资源的 IAM 权限
  5. 05提供人工执行的修复建议

适用场景

  1. 01排查 Pod 使用节点默认服务账号
  2. 02诊断获取访问令牌时的 403 错误
  3. 03调查默认凭据或元数据服务器错误

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/google/skills --skill gke-workload-identity

安装后在 Claude Code 中输入/gke-workload-identity确认已加载。

附带文件references/attribution.md

详细介绍

GKE Workload Identity Federation 故障排查技能

使用此技能,系统地诊断使用 Workload Identity Federation for GKE 的 Pod 为何无法向 Google Cloud API 进行身份验证。常见症状:

  • HTTP/403 ... Permission 'iam.serviceAccounts.getAccessToken' denied on resource
  • google.auth.exceptions ... could not find default credentials / ComputeEngineCredentials cannot find the metadata server
  • API 调用意外使用节点的默认 Compute Engine 服务账号,而不是工作负载的身份。

归属信息

必须阅读:执行任何 gcloud、REST API 或 MCP 命令之前,必须阅读 使用归属信息。对于直接在终端中执行的命令(绝不在生成的脚本、Terraform 或用户代码中执行),请为 gcloud 命令添加前缀 CLOUDSDK_METRICS_REQUEST_ATTRIBUTION="agent-skills/1.0 (skill:gke-workload-identity)" ,并在 User-Agent 标头中追加 agent-skills/1.0 (skill:gke-workload-identity)。

只读边界

此技能仅用于诊断,不进行交互式操作。它只读取集群、IAM 和日志状态,并提出可供人工应用的修复命令或 GitOps 清单变更。绝不能自动创建或修改 IAM 绑定、KSA 注释、节点池或集群。如果缺少证据,或修复需要特权变更,请总结调查结果并交由人工处理(见步骤 7)。

输出规范(适用于每项结论)

报告诊断结果时,始终:

  1. 指出最可能的单一根因(不要罗列各种可能性)。
  2. 明确排除其他合理原因,并引用排除依据。特别是,当原因是节点池配置时,要明确说明这不是 KSA 注释或 IAM 绑定问题;当目标资源缺少角色时,要明确说明 Workload Identity 本身没有配置错误。
  3. 给出供人工应用的确切修复方案——提供填写真实标识符的具体 gcloud 命令(或 GitOps 清单修改),绝不要自动应用。

诊断工作流

步骤 0:发现上下文并确定时间范围

从用户或故障资源收集:PROJECT_ID、PROJECT_NUMBER、CLUSTER、集群的 LOCATION、NAMESPACE、Pod 运行时使用的 KSA、Pod 所调度到的节点和节点池(步骤 2 中会用到)、所调用的目标资源 / API,以及确切的错误字符串。围绕首次观察到故障的时间确定日志查询时间范围。

# Resolve the project number (used in the direct-binding principal identifier).
gcloud projects describe "{PROJECT_ID}" --format="value(projectNumber)"

# Confirm which KSA the workload runs as.
kubectl get pod "{pod_name}" -n "{namespace}" \
  -o jsonpath='{.spec.serviceAccountName}'

# Identify the node the Pod runs on, then the node pool that node belongs to
# (Step 2 checks the node pool's Workload Identity mode).
NODE=$(kubectl get pod "{pod_name}" -n "{namespace}" -o jsonpath='{.spec.nodeName}')
kubectl get node "$NODE" \
  -o jsonpath='{.metadata.labels.cloud\.google\.com/gke-nodepool}'

步骤 1:捕获确切的错误特征

查看工作负载自身的日志和 gke-metadata-server 日志,对故障进行分类。

kubectl logs "{pod_name}" -n "{namespace}" --all-containers --prefix
kubectl describe pod "{pod_name}" -n "{namespace}"

也可通过 Cloud Logging 查看(建议用于查看历史事件)。将其作为 Logs Explorer 深度链接 打开——对查询进行 URL 编码,并附加项目和时间范围: https://console.cloud.google.com/logs/query;query={URL_ENCODED_QUERY};timeRange={start}%2F{end}?project={project_id} (将 / 编码为 %2F,或者使用 ;duration=PT1H 指定滚动的一小时范围):

resource.type="k8s_container"
resource.labels.namespace_name="{namespace}"
resource.labels.pod_name="{pod_name}"
severity>=WARNING

对错误特征进行分类:

  • iam.serviceAccounts.getAccessToken 被拒绝 / HTTP/403 → 工作负载正在使用 GSA 模拟路径。对于旧版配置,这是预期行为;但如果你打算使用直接绑定,则表示 KSA 仍带有遗留的 iam.gke.io/gcp-service-account 注释(或客户端 SDK 已配置为进行模拟),因而意外模拟了 GSA;请转到步骤 3。
  • 目标 API/资源上的 403 PERMISSION_DENIED(错误中没有 getAccessToken)→ 已解析的身份缺少该资源所需的 IAM 角色;请转到步骤 4。
  • could not find default credentials / cannot find the metadata server → 元数据服务器连接问题或启动时序竞争;请转到步骤 5。
  • 调用成功,但使用的是节点默认服务账号 → 此节点池未启用 Workload Identity;请转到步骤 2。

步骤 2:确认 Workload Identity 已启用(集群 + 节点池)

集群和 Pod 所在的节点池都必须启用 Workload Identity。节点池使用 GCE_METADATA(而非 GKE_METADATA)时,Pod 会回退到节点的默认 Compute Engine 服务账号。

# Cluster must have a workload identity pool (PROJECT_ID.svc.id.goog).
gcloud container clusters describe "{cluster}" --location "{location}" \
  --format="value(workloadIdentityConfig.workloadPool)"

# Node pool must have workloadMetadataConfig.mode = GKE_METADATA.
gcloud container node-pools describe "{node_pool}" --cluster "{cluster}" \
  --location "{location}" \
  --format="value(config.workloadMetadataConfig.mode)"
  • 工作负载池为空 → 集群未启用 Workload Identity。
  • 节点池模式为 GCE_METADATA(或为空)→ 节点池未使用 GKE 元数据服务器;这是“以节点默认服务账号运行”的常见原因。修复方法:在节点池上启用 --workload-metadata=GKE_METADATA(建议由人工执行;这会重新创建节点)。这是节点池配置问题,而非 KSA 注释或 IAM 绑定问题,因此不要通过修改 KSA 注释或 IAM 绑定来修复。如果症状是“以节点默认服务账号运行”,请明确指出:根本原因是节点池的 workloadMetadataConfig.mode,KSA 注释和 IAM 绑定已排除为原因。提出确切的修复方法,例如:
gcloud container node-pools update "{node_pool}" --cluster "{cluster}" \
  --location "{location}" --workload-metadata=GKE_METADATA

步骤 3:确认 KSA → 身份绑定

支持两种模式。优先使用直接绑定(当前默认方式);将 GSA 模拟视为旧版路径。

两种模式的故障表现有何不同:使用直接绑定时,KSA 主体直接访问资源,因此故障会表现为目标 API 上的普通 403 PERMISSION_DENIED(请在步骤 4 中修复)。403 iam.serviceAccounts.getAccessToken 则表示发生了模拟尝试——这是旧版路径下的预期行为;如果原本要使用直接绑定的 KSA 上仍残留 iam.gke.io/gcp-service-account 注释,则属于意外模拟。

(a)KSA 直接绑定(不进行 GSA 模拟)。 KSA 主体被直接授予角色。构造主体标识符并搜索其绑定:

principal://iam.googleapis.com/projects/{PROJECT_NUMBER}/locations/global/workloadIdentityPools/{PROJECT_ID}.svc.id.goog/subject/ns/{NAMESPACE}/sa/{KSA_NAME}

(b)旧版:KSA + GSA 模拟。 KSA 必须添加注释以指向某个 GSA,并且 KSA 必须在该 GSA 上拥有 roles/iam.workloadIdentityUser 角色。

# The KSA annotation must reference the intended GSA.
kubectl get serviceaccount "{ksa_name}" -n "{namespace}" \
  -o jsonpath='{.metadata.annotations.iam\.gke\.io/gcp-service-account}'

# The GSA's IAM policy must bind the KSA member to workloadIdentityUser.
gcloud iam service-accounts get-iam-policy \
  "{gsa_name}@{project_id}.iam.gserviceaccount.com" \
  --format=json
# Expect a binding: role roles/iam.workloadIdentityUser,
# member serviceAccount:{PROJECT_ID}.svc.id.goog[{NAMESPACE}/{KSA_NAME}]

如果注释存在,但绑定缺失,则该绑定很可能已被删除——请检查故障发生时间前后的 setIamPolicy 审计日志,以找出负责的主体。


步骤 4:验证目标资源上的 IAM 权限

即使绑定正确,身份(直接绑定时为 KSA 主体,旧版配置时为 GSA)也必须拥有 API 调用所需的角色(例如 roles/storage.objectViewer)。标准 IAM Policy Troubleshooter 对 Workload Identity 主体的支持有限;请使用 Cloud Asset Inventory 搜索该主体的所有 IAM 策略。

# Direct binding: search for the KSA principal's bindings across the project.
gcloud asset search-all-iam-policies \
  --scope="projects/{PROJECT_ID}" \
  --query='policy:"{PROJECT_ID}.svc.id.goog"'

# Legacy: search for the GSA's bindings on the target resource's project.
gcloud asset search-all-iam-policies \
  --scope="projects/{TARGET_PROJECT_ID}" \
  --query='policy:"{gsa_name}@{project_id}.iam.gserviceaccount.com"'

如果没有任何绑定授予该主体在目标资源上所需的角色,缺少该角色就是根本原因(Workload Identity 本身并未配置错误)。请将修复方案作为供人工应用的建议变更提供——使用真实主体和角色给出确切的 add-iam-policy-binding 命令,切勿自动应用。例如,对于直接绑定到项目级资源的情况:

gcloud projects add-iam-policy-binding "{TARGET_PROJECT_ID}" \
  --member="principal://iam.googleapis.com/projects/{PROJECT_NUMBER}/locations/global/workloadIdentityPools/{PROJECT_ID}.svc.id.goog/subject/ns/{NAMESPACE}/sa/{KSA_NAME}" \
  --role="{REQUIRED_ROLE}"   # e.g. roles/storage.objectViewer

步骤 5:GKE 元数据服务器和连接

适用于错误特征为 could not find the metadata server、cannot find the metadata server 或连接/超时错误的情况。kube-system 中的 gke-metadata-server DaemonSet 会在每个节点上代理令牌交换;对 169.254.169.254 的请求会被重定向到该服务,因此,如果 Pod 无法连接到其所在节点上健康的元数据服务器 Pod,就会默认拒绝访问。

(a)检查工作负载所在节点上的 gke-metadata-server Pod 健康状态。

# The DaemonSet Pods must be healthy on the workload's node.
kubectl get pods -n kube-system -l k8s-app=gke-metadata-server -o wide

# A gke-metadata-server Pod can be OOM-evicted when the cluster has many
# (>3,000) Kubernetes service accounts. Look for CrashLoopBackOff, then confirm
# the eviction was OOMKilled.
kubectl get pods -n kube-system | grep CrashLoopBackOff
kubectl describe pod {gke_metadata_server_pod} --namespace=kube-system | grep OOMKilled

(b)检查 gke-metadata-server 日志(通过 Cloud Logging 查看历史记录;请打开 Logs Explorer 深层链接,参见步骤 1):

resource.type="k8s_container"
resource.labels.namespace_name="kube-system"
labels."k8s-pod/k8s-app"="gke-metadata-server"
severity>=WARNING

(c)从受影响的 Pod 进行连接测试——它必须能够访问元数据服务器,并且(对于某些客户端库)能够解析其 DNS 名称:

# Token endpoint via the hardcoded metadata IP (a healthy path returns a token).
kubectl exec {pod_name} -n {namespace} -- \
  curl -sS -H 'Metadata-Flavor: Google' \
  'http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token'

可能的原因:

  • 启动竞争:Pod 启动后,元数据服务器需要几秒钟才能就绪。立即进行身份验证的应用可能会失败;解决方法是在应用端重试,或使用等待元数据服务器就绪的 initContainer,而不是更改集群。
  • DNS:某些客户端库会解析 metadata.google.internal;集群内 DNS 故障会导致 cannot find the metadata server。作为变通方法,可设置 GCE_METADATA_HOST=169.254.169.254 以跳过 DNS 解析。
  • 网络策略/出站流量:集群网络策略必须允许出站访问 169.254.169.254/32 的 80 端口(GKE Dataplane V2),或 169.254.169.252/32 的 988 端口。默认拒绝出站流量的规则如果阻止了到公共安全令牌服务(sts.googleapis.com:443)的 HTTPS 连接,就会导致 504 Gateway Timeout 或 context deadline exceeded。
  • gke-metadata-server 崩溃/重启次数过多:参见(a)——减少 Kubernetes 服务账号数量(<3,000)以恢复功能。

步骤 6:“Invalid form of account ID” 边缘情况(旧版 GSA 模拟)

仅适用于旧版 GSA 模拟配置(KSA 使用 iam.gke.io/gcp-service-account 注释),不适用于直接主体绑定。

错误特征:需要 IAM 服务账号电子邮件地址的操作(例如,手动创建 Cloud Storage 签名 URL)失败,并出现以下错误:

ERROR: Invalid form of account ID SERVICEACCOUNT_NAME.svc.id.goog.
Should be [Gaia ID | Email | Unique ID | ] of the account

原因:对于通过注释关联到 GSA 的 KSA,GKE 元数据服务器默认会返回 SERVICEACCOUNT_NAME.svc.id.goog 作为标识符,而这不是有效的 IAM 服务账号电子邮件地址。这不是缺少 IAM 绑定,也不需要回退配置。

修复方法(建议由人工处理):为 Pod 的 KSA 添加 iam.gke.io/return-principal-id-as-email="true" 注释,使元数据服务器以预期格式返回身份:

kubectl annotate serviceaccount "{ksa_name}" --namespace "{namespace}" \
  iam.gke.io/return-principal-id-as-email="true"

步骤 7:修复边界与升级处理
  • 提供根本原因 + 证据(确切错误、缺失的绑定 / 注解 / 节点池模式,或元数据服务器状态)。添加指向支持日志条目的 Cloud Logging 深度链接(参见步骤 1)。
  • 将修复方案作为命令或 GitOps 清单更改提供,由人工执行——切勿自动修改 IAM、KSA 或节点池。

遇到以下任一情况时,应升级处理,而不是继续提出自助诊断步骤:

  • 相关日志不可用(被筛选条件排除,或已超出日志存储桶的保留期限);或
  • 身份 / 绑定正确,目标资源 IAM 正确,元数据服务器也运行正常,但故障仍然存在(根本原因未明)。

遇到这些情况时:明确说明限制,总结已收集的调查结果(集群 / 节点池配置、绑定、注解、元数据服务器状态,以及任何 setIamPolicy 审计日志),并转交 GKE 支持 / 工程团队升级处理。 如果缺少证据,不要捏造诊断结论。


参考资料

此技能源自 Google Cloud 公开文档:

评论

支持 Markdown