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

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

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

    <AccordionGroup>
      <Accordion title="Manual Steps" defaultOpen="true">
        1. **Identify node pools and check Integrity Monitoring status**
           * Run on: any machine with `gcloud` configured.
           ```bash theme={null}
           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:
           ```bash theme={null}
           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:
           ```bash theme={null}
           # 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:
           ```bash theme={null}
           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:
           ```bash theme={null}
           # 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:
           ```bash theme={null}
           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:
           ```bash theme={null}
           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`.
      </Accordion>

      <Accordion title="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.
      </Accordion>

      <Accordion title="Automation">
        ```bash theme={null}
        #!/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
        ```
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>

### Additional Reading:

* [https://cloud.google.com/kubernetes-engine/docs/how-to/shielded-gke-nodes#system\_integrity\_monitoring](https://cloud.google.com/kubernetes-engine/docs/how-to/shielded-gke-nodes#system_integrity_monitoring)
