Skip to main content

Ensure The GKE Metadata Server Is Enabled

More Info:

Enable the GKE Metadata Server so workloads use Workload Identity and legacy metadata endpoints are not exposed to pods. Legacy metadata endpoints can leak node service-account credentials.

Risk Level

Critical

Address

Security

Compliance Standards

  • CIS GKE

Triage and Remediation

Remediation

Manual Steps
  1. Identify clusters and capture current Workload Identity / metadata settings

    • Run on any machine with gcloud access:
      gcloud config set project <PROJECT_ID>

      gcloud container clusters list \
      --format="table(name,location,workloadIdentityConfig.workloadPool)"

      # For a specific cluster:
      gcloud container clusters describe <CLUSTER_NAME> \
      --location <LOCATION> \
      --format="json(workloadIdentityConfig,nodePools.config.workloadMetadataConfig)" \
      | jq .
    • Note whether workloadIdentityConfig.workloadPool is set and whether each nodePools[].config.workloadMetadataConfig.mode is GKE_METADATA_SERVER.
  2. Decide on cluster-level Workload Identity configuration

    • If workloadIdentityConfig.workloadPool is unset or empty, decide whether this cluster should use Workload Identity (recommended for production/security‑sensitive clusters).
    • If yes, enable Workload Identity for the cluster (no node pools are changed yet):
      gcloud container clusters update <CLUSTER_NAME> \
      --location <LOCATION> \
      --identity-namespace=<PROJECT_ID>.svc.id.goog
    • Record the decision and any clusters intentionally left without Workload Identity (e.g., legacy/test).
  3. Review each node pool’s metadata mode and plan changes

    • For each cluster where Workload Identity is (or will be) enabled, list node pools and their metadata mode:
      gcloud container node-pools list \
      --cluster <CLUSTER_NAME> \
      --location <LOCATION> \
      --format="table(name,config.workloadMetadataConfig.mode)"
    • Identify node pools where mode is not GKE_METADATA_SERVER (e.g. GCE_METADATA).
    • For each such pool, confirm with workload owners whether any workloads still rely on node service account metadata access; plan remediation (migrating to Workload Identity) before flipping the setting.
  4. Enable GKE Metadata Server on selected node pools

    • When you are ready to cut over a node pool, run:
      gcloud container node-pools update <NODE_POOL_NAME> \
      --cluster=<CLUSTER_NAME> \
      --location=<LOCATION> \
      --workload-metadata-from-node=GKE_METADATA_SERVER
    • Repeat for each node pool you decided to protect with the metadata server.
    • Be aware this can trigger node recreation/rolling updates depending on configuration and may affect workloads that used legacy metadata endpoints.
  5. Adjust workloads to use Workload Identity (if needed)

    • For workloads that previously used node service account credentials, identify required Google Cloud IAM permissions and bind them to Kubernetes service accounts using Workload Identity.
    • Evidence collection:
      # Find service accounts in a namespace
      kubectl get sa -n <NAMESPACE>

      # Show annotations on a service account (to confirm Workload Identity binding)
      kubectl get sa <KSA_NAME> -n <NAMESPACE> -o yaml | grep -A2 'iam.gke.io/gcp-service-account'
    • Update manifests/IaC so pods mount and use the correct Kubernetes service accounts with the proper iam.gke.io/gcp-service-account annotation (per your internal standards and Google’s documentation).
  6. Verify remediation and document exceptions

    • Re-run the audit for each cluster:
      gcloud container clusters describe <CLUSTER_NAME> \
      --location <LOCATION> \
      --format json | jq '.nodePools[].config.workloadMetadataConfig'
    • Confirm that for all intended node pools, mode is GKE_METADATA_SERVER.
    • Document any clusters/node pools where GKE Metadata Server is intentionally not enabled, including owners, justification, and a review date.
Using kubectl

kubectl cannot enable the GKE Metadata Server or configure Workload Identity, because this setting is managed at the GKE control-plane/node-pool level via Google Cloud (gcloud CLI, Console, or IaC), not via Kubernetes API objects. Use the cloud provider configuration steps described in the Manual Steps section to remediate this finding.

Automation
#!/usr/bin/env bash
# Check GKE Metadata Server / Workload Identity status for all clusters in a project
# Requirements:
# - gcloud, jq installed
# - Authenticated to GCP with permissions to list/describe clusters

set -euo pipefail

PROJECT_ID="$(gcloud config get-value project 2>/dev/null || true)"
if [[ -z "${PROJECT_ID}" ]]; then
echo "ERROR: No active GCP project is set in gcloud."
echo "Run: gcloud config set project YOUR_PROJECT_ID"
exit 1
fi

echo "Using project: ${PROJECT_ID}"
echo

# List all clusters (regional and zonal) in the project
clusters_json="$(gcloud container clusters list --project "${PROJECT_ID}" --format=json)"

if [[ "${clusters_json}" == "[]" ]]; then
echo "No GKE clusters found in project ${PROJECT_ID}."
exit 0
fi

echo "Cluster Workload Identity / GKE Metadata Server status:"
echo "---------------------------------------------------------------------"

# Iterate over clusters
echo "${clusters_json}" | jq -r '.[] | [.name, .location] | @tsv' | while IFS=$'\t' read -r CLUSTER_NAME LOCATION; do
echo
echo "Cluster: ${CLUSTER_NAME}"
echo "Location: ${LOCATION}"

# Describe cluster (control-plane level Workload Identity setting)
desc="$(gcloud container clusters describe "${CLUSTER_NAME}" \
--location "${LOCATION}" \
--project "${PROJECT_ID}" \
--format=json)"

# Cluster-level Workload Identity identityNamespace
identity_ns="$(echo "${desc}" | jq -r '.workloadIdentityConfig.identityNamespace // "NONE"')"

if [[ "${identity_ns}" == "NONE" ]]; then
echo " Workload Identity: DISABLED (identityNamespace is not set)"
else
echo " Workload Identity: ENABLED"
echo " identityNamespace: ${identity_ns}"
fi

echo " Node pools:"
echo " NAME | WORKLOAD METADATA MODE | STATUS"

# For each node pool, show workloadMetadataConfig.mode
echo "${desc}" | jq -r '
.nodePools[] |
[
.name,
(.config.workloadMetadataConfig.mode // "UNSET"),
.status
] | @tsv' | while IFS=$'\t' read -r NP_NAME MODE STATUS; do
echo " ${NP_NAME} | ${MODE} | ${STATUS}"
done

cat <<'EOF'

Interpretation (what indicates a problem):
- If "Workload Identity: DISABLED", the cluster is not using Workload Identity.
- For node pools:
* MODE == "GCE_METADATA" or "UNSET" => LEGACY METADATA ENDPOINT IN USE (PROBLEM).
* MODE == "GKE_METADATA_SERVER" => GKE Metadata Server enabled (DESIRED).
- To fully align with CIS 5.4.1:
* identityNamespace SHOULD be set (Workload Identity enabled).
* ALL node pools SHOULD have mode "GKE_METADATA_SERVER".
EOF

done

How to run (any machine with gcloud access):

  1. Ensure gcloud and jq are installed and authenticated.
  2. Set your project:
    gcloud config set project YOUR_PROJECT_ID
  3. Save the script as check_gke_metadata_server.sh, make it executable:
    chmod +x check_gke_metadata_server.sh
  4. Run:
    ./check_gke_metadata_server.sh

Output indicating a problem:

  • Line: Workload Identity: DISABLED (identityNamespace is not set)
    → Cluster not configured for Workload Identity.
  • Any node pool line where WORKLOAD METADATA MODE is GCE_METADATA or UNSET
    → That node pool is not using the GKE Metadata Server and still exposes the legacy metadata endpoint.