关于 gke-workload-identity
用于系统诊断 GKE Pod 无法通过 Workload Identity Federation 访问 Google Cloud API 的只读技能,面向集群运维人员。它按错误特征检查集群、节点池、身份绑定、IAM 权限及元数据服务器,并将修复作为建议交给人工执行。
主要功能
- 01检查集群与节点池身份配置
- 02分析认证错误和日志
- 03核对 KSA 与身份绑定
- 04检查目标资源的 IAM 权限
- 05提供人工执行的修复建议
适用场景
- 01排查 Pod 使用节点默认服务账号
- 02诊断获取访问令牌时的 403 错误
- 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 resourcegoogle.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)。
输出规范(适用于每项结论)
报告诊断结果时,始终:
- 指出最可能的单一根因(不要罗列各种可能性)。
- 明确排除其他合理原因,并引用排除依据。特别是,当原因是节点池配置时,要明确说明这不是 KSA 注释或 IAM 绑定问题;当目标资源缺少角色时,要明确说明 Workload Identity 本身没有配置错误。
- 给出供人工应用的确切修复方案——提供填写真实标识符的具体
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 公开文档:
- 排查 GKE 身份验证问题
— Workload Identity Federation 部分:WI 启用检查、KSA
注解、
roles/iam.workloadIdentityUser、iam.serviceAccounts.getAccessToken403、iam.gke.io/return-principal-id-as-email、元数据服务器 DNS,以及gke-metadata-serverPod 崩溃 指南。 - 关于 GKE 的 Workload Identity Federation — 身份、GKE 元数据服务器,以及 Pod 不会继承节点身份的原因。
- 配置应用以使用 GKE 的 Workload Identity Federation
— 节点池
--workload-metadata=GKE_METADATA、直接绑定principal://...标识符,以及旧版serviceAccount:PROJECT_ID.svc.id.goog[NAMESPACE/KSA_NAME]成员格式。 - 搜索 IAM 政策(Cloud Asset Inventory)
— 使用
gcloud asset search-all-iam-policies审查主体的绑定。 - 查看 GKE 日志 — 用于工作负载和系统日志的 Cloud Logging 查询。