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
Remediation
Manual Steps
-
Identify clusters and capture current Workload Identity / metadata settings
- Run on any machine with
gcloudaccess: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.workloadPoolis set and whether eachnodePools[].config.workloadMetadataConfig.modeisGKE_METADATA_SERVER.
- Run on any machine with
-
Decide on cluster-level Workload Identity configuration
- If
workloadIdentityConfig.workloadPoolis 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).
- If
-
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
modeis notGKE_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.
- For each cluster where Workload Identity is (or will be) enabled, list node pools and their metadata mode:
-
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.
- When you are ready to cut over a node pool, run:
-
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 namespacekubectl 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-accountannotation (per your internal standards and Google’s documentation).
-
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,
modeisGKE_METADATA_SERVER. - Document any clusters/node pools where GKE Metadata Server is intentionally not enabled, including owners, justification, and a review date.
- Re-run the audit for each cluster:
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):
- Ensure
gcloudandjqare installed and authenticated. - Set your project:
gcloud config set project YOUR_PROJECT_ID - Save the script as
check_gke_metadata_server.sh, make it executable:
chmod +x check_gke_metadata_server.sh - 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 MODEisGCE_METADATAorUNSET
→ That node pool is not using the GKE Metadata Server and still exposes the legacy metadata endpoint.