gke-workload-identity

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

GKE Workload Identity Federation Troubleshooting Skill

GKE Workload Identity Federation 故障排查 Skill

Use this skill to systematically diagnose why a Pod using Workload Identity Federation for GKE cannot authenticate to Google Cloud APIs. Typical symptoms:
  • HTTP/403 ... Permission 'iam.serviceAccounts.getAccessToken' denied on resource
  • google.auth.exceptions ... could not find default credentials
    /
    ComputeEngineCredentials cannot find the metadata server
  • API calls that unexpectedly use the node's default Compute Engine service account instead of the workload's identity.
使用此 skill 系统地诊断为什么使用 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 service account,而不是工作负载的身份。

Read-only boundary

只读边界

This skill is diagnostic and non-interactive. It only reads cluster, IAM, and logging state and proposes fixes as commands or GitOps manifest changes for a human to apply. It must never create or modify IAM bindings, KSA annotations, node pools, or clusters automatically. When evidence is missing or the fix requires a privileged change, summarize findings and hand off to a human (see Step 7).
此 skill 是诊断性、非交互式的。它只读取集群、IAM 和日志状态,并将修复方案作为命令或 GitOps manifest 变更建议给人工执行。它绝不自动创建或修改 IAM 绑定、KSA 注解、节点池或集群。当证据缺失或修复需要特权变更时,汇总发现并交给人工(参见步骤 7)。

Output discipline (apply to every conclusion)

输出纪律(适用于每个结论)

When you report a diagnosis, always:
  1. Name the single most-likely root cause (not an open-ended list of possibilities).
  2. Explicitly rule out the other plausible causes, citing the evidence that excludes them. In particular, when the cause is node-pool configuration, state plainly that it is not a KSA annotation or IAM binding problem; when the cause is a missing role on a target resource, state that Workload Identity itself is not misconfigured.
  3. Give the exact remediation as a proposed change for a human — a concrete
    gcloud
    command (or GitOps manifest edit) with the real identifiers filled in — and never apply it automatically.

报告诊断时,始终:
  1. 指出最可能的单一根因(而不是开放式的可能性列表)。
  2. 明确排除其他可能的诱因,并引用排除它们的证据。特别是当原因是节点池配置时,明确说明它不是 KSA 注解或 IAM 绑定问题;当原因是目标资源缺少角色时,说明 Workload Identity 本身没有配置错误。
  3. 给出供人工执行的精确修复建议 —— 带有真实标识符的具体
    gcloud
    命令(或 GitOps manifest 编辑),且绝不自动应用。

Diagnostic Workflow

诊断工作流

Step 0: Context discovery & time window

步骤 0:上下文发现与时间窗口

Collect (from the user or the failing resource):
PROJECT_ID
,
PROJECT_NUMBER
,
CLUSTER
, cluster
LOCATION
,
NAMESPACE
, the KSA the Pod runs as, the node and node pool the Pod is scheduled on (used in Step 2), the target resource / API being called, and the exact error string. Define a time window around the first observed failure for log queries.
bash
undefined
从用户或失败资源收集:
PROJECT_ID
、
PROJECT_NUMBER
、
CLUSTER
、集群
LOCATION
、
NAMESPACE
、Pod 运行所用的 KSA、Pod 调度到的节点和节点池(步骤 2 中使用)、被调用的目标资源 / API,以及确切的错误字符串。围绕首次观察到失败的时间定义一个时间窗口,用于日志查询。
bash
undefined

Resolve the project number (used in the direct-binding principal identifier).

Resolve the project number (used in the direct-binding principal identifier).

gcloud projects describe "{PROJECT_ID}" --format="value(projectNumber)"
gcloud projects describe "{PROJECT_ID}" --format="value(projectNumber)"

Confirm which KSA the workload runs as.

Confirm which KSA the workload runs as.

kubectl get pod "{pod_name}" -n "{namespace}"
-o jsonpath='{.spec.serviceAccountName}'
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

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).

(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}'

--------------------------------------------------------------------------------
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}'

--------------------------------------------------------------------------------

Step 1: Capture the exact error signature

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

Read the workload's own logs and the
gke-metadata-server
logs to classify the failure.
bash
kubectl logs "{pod_name}" -n "{namespace}" --all-containers --prefix
kubectl describe pod "{pod_name}" -n "{namespace}"
Equivalent via Cloud Logging (preferred for historical events). Open it as a Logs Explorer deep link — URL-encode the query and append the project and time window:
https://console.cloud.google.com/logs/query;query={URL_ENCODED_QUERY};timeRange={start}%2F{end}?project={project_id}
(encode
/
as
%2F
, or use
;duration=PT1H
for a rolling hour):
resource.type="k8s_container"
resource.labels.namespace_name="{namespace}"
resource.labels.pod_name="{pod_name}"
severity>=WARNING
Classify the signature:
  • iam.serviceAccounts.getAccessToken
    denied /
    HTTP/403
    → the workload is using the GSA impersonation path. This is expected for the legacy setup, but if you intend to use direct binding, it means the KSA still carries a leftover
    iam.gke.io/gcp-service-account
    annotation (or the client SDK is configured to impersonate) and is unintentionally impersonating a GSA; go to Step 3.
  • 403 PERMISSION_DENIED
    on the target API/resource (no
    getAccessToken
    in the error) → the resolved identity lacks the required IAM role on that resource; go to Step 4.
  • could not find default credentials
    /
    cannot find the metadata server
    → metadata-server connectivity or a startup race; go to Step 5.
  • Calls succeed but as the node default service account → Workload Identity is not in effect for this node pool; go to Step 2.

读取工作负载自己的日志和
gke-metadata-server
日志,对失败进行分类。
bash
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
    denied /
    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
    → metadata 服务器连通性或启动竞态;转至步骤 5。
  • 调用成功但以节点默认 service account身份执行 → Workload Identity 未在该节点池上生效;转至步骤 2。

Step 2: Verify Workload Identity is enabled (cluster + node pool)

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

Both the cluster and the node pool the Pod runs on must have Workload Identity enabled. A node pool with
GCE_METADATA
(instead of
GKE_METADATA
) causes Pods to fall back to the node's default Compute Engine service account.
bash
undefined
集群和 Pod 所在的节点池都必须启用 Workload Identity。节点池使用
GCE_METADATA
(而不是
GKE_METADATA
)会导致 Pod 回退到节点的默认 Compute Engine service account。
bash
undefined

Cluster must have a workload identity pool (PROJECT_ID.svc.id.goog).

Cluster must have a workload identity pool (PROJECT_ID.svc.id.goog).

gcloud container clusters describe "{cluster}" --location "{location}"
--format="value(workloadIdentityConfig.workloadPool)"
gcloud container clusters describe "{cluster}" --location "{location}"
--format="value(workloadIdentityConfig.workloadPool)"

Node pool must have workloadMetadataConfig.mode = GKE_METADATA.

Node pool must have workloadMetadataConfig.mode = GKE_METADATA.

gcloud container node-pools describe "{node_pool}" --cluster "{cluster}"
--location "{location}"
--format="value(config.workloadMetadataConfig.mode)"

-   Empty workload pool → Workload Identity is **not enabled on the cluster**.
-   Node-pool mode is `GCE_METADATA` (or empty) → the node pool is **not** using
    the GKE metadata server; this is the usual cause of "runs as the node
    default service account". Remediation: enable
    `--workload-metadata=GKE_METADATA` on the node pool (propose to a human;
    recreates nodes). This is a **node-pool configuration** problem — not a KSA
    annotation or IAM binding problem — so do not change KSA annotations or IAM
    bindings to fix it. When the symptom is "runs as the node default service
    account", say so explicitly: the root cause is the node pool's
    `workloadMetadataConfig.mode`, and the KSA annotation and IAM bindings are
    **ruled out** as the cause. Propose the exact fix, e.g.:

```bash
gcloud container node-pools update "{node_pool}" --cluster "{cluster}" \
  --location "{location}" --workload-metadata=GKE_METADATA

gcloud container node-pools describe "{node_pool}" --cluster "{cluster}"
--location "{location}"
--format="value(config.workloadMetadataConfig.mode)"

-   空的 workload pool → Workload Identity **未在集群上启用**。
-   节点池模式为 `GCE_METADATA`(或为空)→ 节点池**未**使用 GKE metadata 服务器;这通常是“以节点默认 service account 运行”的诱因。修复:在节点池上启用 `--workload-metadata=GKE_METADATA`(建议给人工;会重建节点)。这是一个**节点池配置**问题——不是 KSA 注解或 IAM 绑定问题——因此不要通过修改 KSA 注解或 IAM 绑定来修复它。当症状是“以节点默认 service account 运行”时,请明确说明:根因是节点池的 `workloadMetadataConfig.mode`,KSA 注解和 IAM 绑定**被排除**为诱因。建议精确修复,例如:

```bash
gcloud container node-pools update "{node_pool}" --cluster "{cluster}" \
  --location "{location}" --workload-metadata=GKE_METADATA

Step 3: Verify the KSA → identity binding

步骤 3:验证 KSA → 身份绑定

There are two supported models. Prefer direct binding (current default); treat GSA impersonation as the legacy path.
How the two models fail differently: with direct binding the KSA principal accesses resources directly, so failures show up as a plain
403 PERMISSION_DENIED
on the target API (fix in Step 4). A
403 iam.serviceAccounts.getAccessToken
instead means an impersonation attempt — intended under the legacy path, or unintended if a leftover
iam.gke.io/gcp-service-account
annotation remains on a KSA that was meant to use direct binding.
(a) Direct KSA binding (no GSA impersonation). The KSA principal is granted roles directly. Construct the principal identifier and search for its bindings:
principal://iam.googleapis.com/projects/{PROJECT_NUMBER}/locations/global/workloadIdentityPools/{PROJECT_ID}.svc.id.goog/subject/ns/{NAMESPACE}/sa/{KSA_NAME}
(b) Legacy: KSA + GSA impersonation. The KSA must be annotated to point at a GSA, and the KSA must hold
roles/iam.workloadIdentityUser
on that GSA.
bash
undefined
有两种受支持的模型。优先使用直接绑定(当前默认);将 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
。
bash
undefined

The KSA annotation must reference the intended GSA.

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}'
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.

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
gcloud iam service-accounts get-iam-policy
"{gsa_name}@{project_id}.iam.gserviceaccount.com"
--format=json

Expect a binding: role roles/iam.workloadIdentityUser,

Expect a binding: role roles/iam.workloadIdentityUser,

member serviceAccount:{PROJECT_ID}.svc.id.goog[{NAMESPACE}/{KSA_NAME}]

member serviceAccount:{PROJECT_ID}.svc.id.goog[{NAMESPACE}/{KSA_NAME}]


> If the annotation is present but the binding is missing, the binding was
> likely removed — check the `setIamPolicy` audit logs around the failure time
> to find the responsible principal.

--------------------------------------------------------------------------------

> 如果注解存在但绑定缺失,则绑定很可能已被移除——检查失败时间附近的 `setIamPolicy` 审计日志,以找到负责的主体。

--------------------------------------------------------------------------------

Step 4: Verify IAM permissions on the target resource

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

Even with a correct binding, the identity (the KSA principal for direct binding, or the GSA for legacy) must hold the role required by the API call (for example
roles/storage.objectViewer
). The standard IAM Policy Troubleshooter has limited support for Workload Identity principals; use Cloud Asset Inventory to search all IAM policies for the principal instead.
bash
undefined
即使绑定正确,身份(直接绑定为 KSA 主体,旧版为 GSA)也必须持有 API 调用所需的角色(例如
roles/storage.objectViewer
)。标准 IAM Policy Troubleshooter 对 Workload Identity 主体支持有限;请改用 Cloud Asset Inventory 搜索所有 IAM 策略中该主体的绑定。
bash
undefined

Direct binding: search for the KSA principal's bindings across the project.

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"'
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.

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"'

If no binding grants the required role on the target resource, **that missing
role is the root cause** (and Workload Identity itself is **not**
misconfigured). Present the fix as a **proposed change for a human to apply** —
the exact `add-iam-policy-binding` with the real principal and role — never
applying it automatically. For example, for direct binding on a project-level
resource:

```bash
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

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` —— 绝不自动应用。例如,对于项目级资源上的直接绑定:

```bash
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

Step 5: GKE metadata server & connectivity

步骤 5:GKE metadata 服务器与连通性

Applies when the signature is
could not find the metadata server
,
cannot find the metadata server
, or a connection/timeout error. The
gke-metadata-server
DaemonSet (in
kube-system
) brokers the token exchange on each node; requests to
169.254.169.254
are redirected to it, so a Pod fails closed if it cannot reach a healthy metadata-server Pod on its node.
(a) Check gke-metadata-server Pod health on the workload's node.
bash
undefined
当特征为
could not find the metadata server
、
cannot find the metadata server
或连接/超时错误时适用。
gke-metadata-server
DaemonSet(位于
kube-system
)在每个节点上代理 token 交换;对
169.254.169.254
的请求被重定向到它,因此如果 Pod 无法到达其节点上健康的 metadata-server Pod,就会 fail closed(无法访问时安全失败)。
(a) 检查工作负载节点上的 gke-metadata-server Pod 健康状况。
bash
undefined

The DaemonSet Pods must be healthy on the workload's node.

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
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

A gke-metadata-server Pod can be OOM-evicted when the cluster has many

(>3,000) Kubernetes service accounts. Look for CrashLoopBackOff, then confirm

(>3,000) Kubernetes service accounts. Look for CrashLoopBackOff, then confirm

the eviction was OOMKilled.

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) Inspect the `gke-metadata-server` logs** (historical, via Cloud Logging;
open as a Logs Explorer deep link, see Step 1):
resource.type="k8s_container" resource.labels.namespace_name="kube-system" labels."k8s-pod/k8s-app"="gke-metadata-server" severity>=WARNING

**(c) Connectivity test** from the affected Pod — it must reach the metadata
server and (for some client libraries) resolve its DNS name:

```bash
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 进行连通性测试** —— 它必须能到达 metadata 服务器,并且(对于某些客户端库)解析其 DNS 名称:

```bash

Token endpoint via the hardcoded metadata IP (a healthy path returns a token).

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'

Possible causes:

-   **Startup race**: the metadata server needs a few seconds after a Pod
    starts. Applications that authenticate immediately may fail; the fix is
    application-side retries or an `initContainer` that waits for the metadata
    server, not a cluster change.
-   **DNS**: some client libraries resolve `metadata.google.internal`; broken
    in-cluster DNS surfaces as `cannot find the metadata server`. As a
    workaround, set `GCE_METADATA_HOST=169.254.169.254` to skip DNS resolution.
-   **Network policy / egress**: a cluster network policy must allow egress to
    `169.254.169.254/32` on port `80` (GKE Dataplane V2), or
    `169.254.169.252/32` on port `988`. A default-deny egress rule that blocks
    HTTPS to the public Security Token Service (`sts.googleapis.com:443`)
    produces `504 Gateway Timeout` or `context deadline exceeded`.
-   **`gke-metadata-server` crashing / high restarts**: see (a) — reduce the
    number of Kubernetes service accounts (<3,000) to restore functionality.

--------------------------------------------------------------------------------
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 启动后 metadata 服务器需要几秒钟。立即进行身份验证的应用可能会失败;修复方式是应用侧重试或一个等待 metadata 服务器的 `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` 的出站流量。默认拒绝的出站规则如果阻止对公共 Security Token Service(`sts.googleapis.com:443`)的 HTTPS 访问,会产生 `504 Gateway Timeout` 或 `context deadline exceeded`。
-   **`gke-metadata-server` 崩溃 / 频繁重启**:参见 (a) —— 减少 Kubernetes service account 的数量(<3,000)以恢复功能。

--------------------------------------------------------------------------------

Step 6: "Invalid form of account ID" edge case (legacy GSA impersonation)

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

Applies only to the legacy GSA impersonation setup (a KSA annotated with
iam.gke.io/gcp-service-account
), not to direct principal binding.
Signature: an operation that requires an IAM service account email (for example, manually creating a Cloud Storage signed URL) fails with:
ERROR: Invalid form of account ID SERVICEACCOUNT_NAME.svc.id.goog.
Should be [Gaia ID | Email | Unique ID | ] of the account
Cause: for a KSA linked to a GSA via annotation, the GKE metadata server by default returns
SERVICEACCOUNT_NAME.svc.id.goog
as the identifier, which is not a valid IAM service account email. This is not a missing IAM binding and does not require reverting the setup.
Fix (propose to a human): add the
iam.gke.io/return-principal-id-as-email="true"
annotation to the Pod's KSA so the metadata server returns the identity in the expected form:
bash
kubectl annotate serviceaccount "{ksa_name}" --namespace "{namespace}" \
  iam.gke.io/return-principal-id-as-email="true"

仅适用于旧版 GSA 模拟设置(KSA 带有
iam.gke.io/gcp-service-account
注解),不适用于直接主体绑定。
特征:需要 IAM service account email 的操作(例如手动创建 Cloud Storage signed 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 metadata 服务器默认返回
SERVICEACCOUNT_NAME.svc.id.goog
作为标识符,而这不是有效的 IAM service account email。这不是缺少 IAM 绑定,也不需要回退该设置。
修复(建议给人工):在 Pod 的 KSA 上添加
iam.gke.io/return-principal-id-as-email="true"
注解,使 metadata 服务器以预期形式返回身份:
bash
kubectl annotate serviceaccount "{ksa_name}" --namespace "{namespace}" \
  iam.gke.io/return-principal-id-as-email="true"

Step 7: Remediation boundary & escalation

步骤 7:修复边界与升级

  • Present the root cause + evidence (the exact error, the missing binding / annotation / node-pool mode, or the metadata-server state). Include a Cloud Logging deep link (see Step 1) to the supporting entries.
  • Propose the fix as a command or GitOps manifest change for a human to apply — never modify IAM, KSAs, or node pools automatically.
Escalate (instead of proposing more self-service diagnostics) when either:
  • the relevant logs are unavailable (excluded by a filter, or past the log bucket's retention); or
  • the identity/binding is correct, the target-resource IAM is correct, and the metadata server is healthy, yet the failure persists (undetermined root cause).
In those cases: state the limitation plainly, summarize the findings gathered (cluster/node-pool config, bindings, annotations, metadata-server state, and any
setIamPolicy
audit logs), and route to GKE support / engineering escalation. Do not fabricate a diagnosis when evidence is missing.

  • 呈现根因 + 证据(确切的错误、缺失的绑定 / 注解 / 节点池模式,或 metadata-server 状态)。包含指向支持性日志条目的 Cloud Logging 深层链接(参见步骤 1)。
  • 将修复作为供人工执行的命令或 GitOps manifest 变更提出——绝不自动修改 IAM、KSA 或节点池。
升级(而不是提出更多自助诊断)当出现以下任一情况:
  • 相关日志不可用(被过滤器排除,或已超过日志桶保留期);或
  • 身份/绑定正确、目标资源 IAM 正确、metadata 服务器健康,但失败仍然持续(根因未确定)。
在这些情况下:明确说明限制,汇总已收集的发现(集群/节点池配置、绑定、注解、metadata-server 状态,以及任何
setIamPolicy
审计日志),并转交 GKE 支持 / 工程升级。当证据缺失时不要编造诊断。

References

参考文档

This skill is derived from public Google Cloud documentation:
此 skill 源自 Google Cloud 公开文档: