> ## Documentation Index
> Fetch the complete documentation index at: https://cloudanix.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Prefer Using Dedicated GCP Service Accounts And Workload Identity

### More Info:

Enable Workload Identity so pods authenticate to Google Cloud as dedicated service accounts rather than sharing node credentials. This provides fine-grained, per-workload access control.

### Risk Level

High

### Address

Security

### Compliance Standards

* CIS GKE

### Triage and Remediation

<Tabs>
  <Tab title="Remediation">
    ### Remediation

    <AccordionGroup>
      <Accordion title="Manual Steps" defaultOpen="true">
        1. **Confirm whether Workload Identity is enabled at the cluster level**
           * Run on any machine with `gcloud` access:
             ```bash theme={null}
             gcloud container clusters describe CLUSTER_NAME \
               --location LOCATION \
               --project PROJECT_ID \
               --format json | jq '.workloadIdentityConfig'
             ```
           * If the output is `null` or empty, Workload Identity is not enabled and should be considered for activation.

        2. **If not enabled, plan and enable Workload Identity for the cluster**
           * Decide the workload pool name (typically `PROJECT_ID.svc.id.goog`) and assess impact on existing workloads and tooling (e.g., anything relying on node service account credentials).
           * Enable Workload Identity:
             ```bash theme={null}
             gcloud container clusters update CLUSTER_NAME \
               --location LOCATION \
               --project PROJECT_ID \
               --workload-pool PROJECT_ID.svc.id.goog
             ```
           * Note: this changes the control-plane configuration; it does not retroactively change existing node pools.

        3. **Review and update existing node pools to use GKE metadata server**
           * List node pools:
             ```bash theme={null}
             gcloud container node-pools list \
               --cluster CLUSTER_NAME \
               --location LOCATION \
               --project PROJECT_ID
             ```
           * For each node pool, inspect its workload metadata setting:
             ```bash theme={null}
             gcloud container node-pools describe NODE_POOL_NAME \
               --cluster CLUSTER_NAME \
               --location LOCATION \
               --project PROJECT_ID \
               --format 'value(config.workloadMetadataConfig.mode)'
             ```
           * For node pools not set to `GKE_METADATA`, schedule maintenance (this may recreate nodes) and update:
             ```bash theme={null}
             gcloud container node-pools update NODE_POOL_NAME \
               --cluster CLUSTER_NAME \
               --location LOCATION \
               --project PROJECT_ID \
               --workload-metadata=GKE_METADATA
             ```
           * Alternatively, create new node pools with `--workload-metadata=GKE_METADATA` and migrate workloads before draining/deleting old pools.

        4. **Identify workloads that currently depend on node service account credentials**
           * On any machine with `kubectl` access, look for pods not using projected service account tokens or that are known to call Google Cloud APIs:
             ```bash theme={null}
             kubectl get pods -A -o wide
             ```
           * Review deployment/statefulset manifests for these workloads to see how they obtain credentials (environment variables, mounted keys, or default ADC from node).
           * Document which namespaces and service accounts will need Workload Identity mappings.

        5. **Plan and apply per-workload migrations to Workload Identity**
           * For each identified workload:
             * Choose/create a dedicated Google service account.
             * Bind the Kubernetes service account in the relevant namespace to that Google service account using IAM policy binding (done via `gcloud` or IaC, outside Kubernetes).
             * Update manifests so pods run under the intended Kubernetes service account (not `default`) and do not rely on node credentials or legacy key files.
           * Use the guidance in the Workload Identity documentation to complete these mappings and test that each workload still has the required access.

        6. **Verify configuration after changes**
           * Re-run the cluster-level check:
             ```bash theme={null}
             gcloud container clusters describe CLUSTER_NAME \
               --location LOCATION \
               --project PROJECT_ID \
               --format json | jq '.workloadIdentityConfig'
             ```
             Confirm it shows the expected `workloadPool` (for example, `PROJECT_ID.svc.id.goog`).
           * For each node pool, confirm `GKE_METADATA` mode:
             ```bash theme={null}
             gcloud container node-pools describe NODE_POOL_NAME \
               --cluster CLUSTER_NAME \
               --location LOCATION \
               --project PROJECT_ID \
               --format 'value(config.workloadMetadataConfig.mode)'
             ```
           * Optionally, exec into a migrated pod and validate that Google Cloud API calls succeed using the intended service account (per your application’s logging or diagnostic commands).
      </Accordion>

      <Accordion title="Using kubectl">
        kubectl cannot enable Workload Identity or change node pool metadata, because this finding is controlled entirely through the GKE/Google Cloud control plane (gcloud, console, or IaC). Use the cloud provider configuration steps described in the Manual Steps section to remediate this finding.
      </Accordion>

      <Accordion title="Automation">
        ```bash theme={null}
        #!/usr/bin/env bash
        #
        # Report GKE Workload Identity usage at cluster and workload level.
        #
        # Requirements:
        # - gcloud configured with access to the project
        # - kubectl configured to talk to the target cluster
        # - jq installed
        #
        # Usage:
        #   PROJECT_ID="my-project" CLUSTER_NAME="my-cluster" LOCATION="us-central1" ./report-workload-identity.sh

        set -euo pipefail

        PROJECT_ID="${PROJECT_ID:?Set PROJECT_ID}"
        CLUSTER_NAME="${CLUSTER_NAME:?Set CLUSTER_NAME}"
        LOCATION="${LOCATION:?Set LOCATION}"

        echo "=== Cluster-level Workload Identity configuration ==="
        gcloud container clusters describe "$CLUSTER_NAME" \
          --location "$LOCATION" \
          --project "$PROJECT_ID" \
          --format="json(workloadIdentityConfig,workloadIdentityConfig.workloadPool,workloadIdentityConfig.identityNamespace)" \
          | jq .

        echo
        echo "Interpretation:"
        echo "- If workloadIdentityConfig is null or {} => Workload Identity NOT enabled at cluster level (non-compliant with CIS 5.2.2)."
        echo "- If workloadIdentityConfig.workloadPool ends with '.svc.id.goog' => Workload Identity ENABLED."

        echo
        echo "=== Node pool metadata configuration (Workload Identity plumbing) ==="
        gcloud container node-pools list \
          --cluster "$CLUSTER_NAME" \
          --location "$LOCATION" \
          --project "$PROJECT_ID" \
          --format="table(name,config.workloadMetadataConfig.mode)"

        echo
        echo "Interpretation:"
        echo "- For each node pool, config.workloadMetadataConfig.mode should be 'GKE_METADATA'."
        echo "- Any node pool with mode 'GCE_METADATA' or empty => nodes still exposing VM metadata, workloads not using Workload Identity on those nodes."

        echo
        echo "=== Namespaces and service accounts annotated for Workload Identity ==="
        echo "Listing service accounts with potential Workload Identity annotations..."
        echo

        # Check namespace-level annotation (rare, but sometimes used for policy)
        echo "Namespace-level annotations related to Workload Identity:"
        kubectl get ns -o json \
          | jq -r '.items[]
            | .metadata
            | {name: .name, annotations: (.annotations // {})
              | with_entries(select(.key | test("iam.gke.io|workloadIdentity|gsa"))) }
            | select(.annotations != {})
            | "NAMESPACE: \(.name)\n  ANNOTATIONS: \(.annotations)\n"' || true

        echo "ServiceAccount-level annotations (recommended for Workload Identity):"
        kubectl get sa --all-namespaces -o json \
          | jq -r '.items[]
            | .metadata as $m
            | ($m.annotations // {}) as $a
            | select(($a | keys[]? | test("iam.gke.io/gcp-service-account")))?
            | "NAMESPACE: \($m.namespace)\n  SA: \($m.name)\n  iam.gke.io/gcp-service-account: \($a["iam.gke.io/gcp-service-account"])\n"' || true

        echo
        echo "Interpretation:"
        echo "- Service accounts used with Workload Identity should have annotation:"
        echo "    iam.gke.io/gcp-service-account: <gsa-name>@${PROJECT_ID}.iam.gserviceaccount.com"
        echo "- Workloads lacking this annotation on their service account will not use a dedicated GCP service account."

        echo
        echo "=== Workloads and the service accounts they use ==="
        echo "Deployments:"
        kubectl get deploy --all-namespaces -o json \
          | jq -r '.items[]
            | "DEPLOYMENT: \(.metadata.namespace)/\(.metadata.name)\n  SERVICE ACCOUNT: \(.spec.template.spec.serviceAccountName // "default")\n"' || true

        echo
        echo "StatefulSets:"
        kubectl get statefulset --all-namespaces -o json \
          | jq -r '.items[]
            | "STATEFULSET: \(.metadata.namespace)/\(.metadata.name)\n  SERVICE ACCOUNT: \(.spec.template.spec.serviceAccountName // "default")\n"' || true

        echo
        echo "DaemonSets:"
        kubectl get daemonset --all-namespaces -o json \
          | jq -r '.items[]
            | "DAEMONSET: \(.metadata.namespace)/\(.metadata.name)\n  SERVICE ACCOUNT: \(.spec.template.spec.serviceAccountName // "default")\n"' || true

        echo
        cat <<'EOF'
        How to review this output for CIS GKE 5.2.2:

        1) Cluster-level:
           - Problem if: workloadIdentityConfig is null or missing.
           - Desired: workloadIdentityConfig.workloadPool is set (e.g. "PROJECT_ID.svc.id.goog").

        2) Node pools:
           - Problem if: any node pool shows config.workloadMetadataConfig.mode = "GCE_METADATA" or is blank.
           - Desired: all node pools show mode = "GKE_METADATA".

        3) Workload Identity usage:
           - Identify important Deployments/StatefulSets/DaemonSets and note their serviceAccountName.
           - For each such ServiceAccount:
               * Problem if: it has no iam.gke.io/gcp-service-account annotation
                 (workload will either use node credentials or fail, depending on config).
               * Desired: annotation exists and references a dedicated GCP service account.

        This script only reports state; enabling Workload Identity or changing node pools
        must be done through gcloud / console / IaC as described in the benchmark guidance.
        EOF
        ```
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
