Skip to main content

Integrity Monitoring For Shielded Gke Nodes Is Enabled

More Info:

Enable Integrity Monitoring For Shielded Gke Nodes To Be Notified Of Inconsistencies During The Node Boot Sequence.

Risk Level

Medium

Address

Security

Compliance Standards

  • CIS GKE
  • Securities and Exchange Board of India (SEBI) - Cloud Security Adoption Framework

Triage and Remediation

Remediation

Manual Steps
  1. Identify node pools and check Integrity Monitoring status

    • Run on: any machine with gcloud configured.
    PROJECT_ID="<your-project-id>"
    CLUSTER_NAME="<your-cluster-name>"
    LOCATION="<cluster-location>" # e.g. us-central1, us-central1-a

    gcloud container node-pools list \
    --cluster "$CLUSTER_NAME" \
    --location "$LOCATION" \
    --project "$PROJECT_ID"

    For each node pool name from the output:

    POOL_NAME="<node-pool-name>"

    gcloud container node-pools describe "$POOL_NAME" \
    --cluster "$CLUSTER_NAME" \
    --location "$LOCATION" \
    --project "$PROJECT_ID" \
    --format json | jq .config.shieldedInstanceConfig

    Review: enableIntegrityMonitoring should be true for compliant pools.

  2. Decide which node pools must be protected

    • Based on your security policy, decide whether all node pools, or at least those running sensitive workloads (e.g., production, regulated, or Internet-exposed apps), must have Shielded Integrity Monitoring enabled.
    • Document any justified exceptions (e.g., specialized images that are incompatible).
  3. Plan migration from non-compliant node pools

    • For each non-compliant node pool you intend to remediate, list workloads scheduled there:
    # On any machine with kubectl access
    kubectl get pods -A -o wide | grep "<non-compliant-node-pool-name-fragment>" || true
    • Decide a maintenance window and verify PodDisruptionBudgets, autoscaling, and capacity to ensure workloads can move to a new pool without unacceptable downtime.
  4. Create new compliant node pools with Integrity Monitoring enabled

    • For each target pool you are replacing, create a new one with Shielded Integrity Monitoring turned on:
    NEW_POOL_NAME="<new-node-pool-name>"

    gcloud container node-pools create "$NEW_POOL_NAME" \
    --cluster "$CLUSTER_NAME" \
    --location "$LOCATION" \
    --project "$PROJECT_ID" \
    --shielded-integrity-monitoring
    • Optionally add flags (e.g. --machine-type, --num-nodes, --node-locations, --enable-autoscaling) to match your existing pool’s configuration.
  5. Migrate workloads and delete old node pools

    • Cordon and drain old nodes so workloads move to the new, compliant pool:
    # Identify nodes belonging to the old pool
    kubectl get nodes --show-labels | grep "<old-node-pool-name>" || true

    # For each such node:
    NODE_NAME="<node-name>"

    kubectl cordon "$NODE_NAME"
    kubectl drain "$NODE_NAME" --ignore-daemonsets --delete-emptydir-data
    • After confirming workloads are healthy on the new pool, delete the old non-compliant node pool:
    gcloud container node-pools delete "$POOL_NAME" \
    --cluster "$CLUSTER_NAME" \
    --location "$LOCATION" \
    --project "$PROJECT_ID"
  6. Verify remediation (re-run the audit)

    • Re-check each remaining node pool:
    for POOL_NAME in $(gcloud container node-pools list \
    --cluster "$CLUSTER_NAME" \
    --location "$LOCATION" \
    --project "$PROJECT_ID" \
    --format="value(name)"); do
    echo "=== $POOL_NAME ==="
    gcloud container node-pools describe "$POOL_NAME" \
    --cluster "$CLUSTER_NAME" \
    --location "$LOCATION" \
    --project "$PROJECT_ID" \
    --format json | jq .config.shieldedInstanceConfig
    done
    • Confirm that all node pools you decided must be protected show "enableIntegrityMonitoring": true.
Using kubectl

kubectl cannot enable Integrity Monitoring for Shielded GKE nodes because this setting is configured on the managed control plane via GCP (gcloud, console, or IaC), not via Kubernetes objects. Please follow the guidance in the Manual Steps section to recreate/migrate node pools with --shielded-integrity-monitoring enabled.

Automation
#!/usr/bin/env bash
#
# Report Shielded GKE node pools and Integrity Monitoring status for all clusters
# in a given project. Requires:
# - gcloud configured and authenticated
# - jq installed
#
# Usage:
# PROJECT_ID="my-project" LOCATION="us-central1" ./check_shielded_integrity_monitoring.sh
#

set -euo pipefail

PROJECT_ID="${PROJECT_ID:-}"
LOCATION="${LOCATION:-}" # Optional: if empty, checks all regions/zones

if [[ -z "$PROJECT_ID" ]]; then
echo "ERROR: PROJECT_ID environment variable must be set" >&2
exit 1
fi

echo "Project: $PROJECT_ID"
if [[ -n "$LOCATION" ]]; then
echo "Location filter: $LOCATION"
fi
echo

# List all GKE clusters in the project (and optionally a given location)
if [[ -n "$LOCATION" ]]; then
clusters_json="$(gcloud container clusters list \
--project "$PROJECT_ID" \
--location "$LOCATION" \
--format=json)"
else
clusters_json="$(gcloud container clusters list \
--project "$PROJECT_ID" \
--format=json)"
fi

if [[ "$(echo "$clusters_json" | jq 'length')" -eq 0 ]]; then
echo "No GKE clusters found."
exit 0
fi

echo "Cluster,Location,NodePool,ShieldedVMEnabled,IntegrityMonitoringEnabled,STATUS"
echo "-------,--------,--------,-----------------,---------------------------,------"

echo "$clusters_json" | jq -r '.[] | [.name, .location] | @tsv' | while IFS=$'\t' read -r CLUSTER_NAME CLUSTER_LOCATION; do
# List node pools for this cluster
node_pools_json="$(gcloud container node-pools list \
--cluster "$CLUSTER_NAME" \
--location "$CLUSTER_LOCATION" \
--project "$PROJECT_ID" \
--format=json)"

if [[ "$(echo "$node_pools_json" | jq 'length')" -eq 0 ]]; then
echo "${CLUSTER_NAME},${CLUSTER_LOCATION},<none>,N/A,N/A,NO_NODE_POOLS"
continue
fi

echo "$node_pools_json" | jq -r '.[].name' | while read -r POOL_NAME; do
np_json="$(gcloud container node-pools describe "$POOL_NAME" \
--cluster "$CLUSTER_NAME" \
--location "$CLUSTER_LOCATION" \
--project "$PROJECT_ID" \
--format=json)"

shielded_config="$(echo "$np_json" | jq -r '.config.shieldedInstanceConfig // empty')"

if [[ -z "$shielded_config" || "$shielded_config" == "null" ]]; then
shielded_vm="false"
integrity_monitoring="false"
else
shielded_vm="$(echo "$shielded_config" | jq -r '.enableSecureBoot // false')"
integrity_monitoring="$(echo "$shielded_config" | jq -r '.enableIntegrityMonitoring // false')"
fi

status="OK"
# Problem conditions:
# - Shielded VM not enabled
# - Integrity Monitoring not enabled
if [[ "$shielded_vm" != "true" || "$integrity_monitoring" != "true" ]]; then
status="NON_COMPLIANT"
fi

echo "${CLUSTER_NAME},${CLUSTER_LOCATION},${POOL_NAME},${shielded_vm},${integrity_monitoring},${status}"
done
done

cat <<'EOF'

Interpretation:

- NON_COMPLIANT rows indicate a problem for this control:
* ShieldedVMEnabled != true -> node pool is not using Shielded GKE nodes.
* IntegrityMonitoringEnabled != true -> Integrity Monitoring is not enabled.

- To meet CIS GKE 5.5.6:
* Every node pool that should run sensitive workloads MUST have
IntegrityMonitoringEnabled=true (and be ShieldedVMEnabled=true).
* For any NON_COMPLIANT node pool, plan to:
- Create a new node pool with --shielded-integrity-monitoring
- Migrate workloads
- Delete the non-conforming node pool

This script only reports state; remediation must be planned and applied manually.
EOF

Additional Reading: