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

# Enable Customer-Managed Encryption Keys (CMEK) For GKE Persistent Disks

### More Info:

Encrypt GKE Persistent Disks with Customer-Managed Encryption Keys so you control the key lifecycle for data at rest. CMEK provides stronger control than default Google-managed encryption.

### Risk Level

Medium

### Address

Security

### Compliance Standards

* CIS GKE

### Triage and Remediation

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

    <AccordionGroup>
      <Accordion title="Manual Steps" defaultOpen="true">
        1. **Inventory and review existing Persistent Disks and their encryption**
           * On any machine with `gcloud` access, list all PDs used by the cluster’s PVs:
             ```bash theme={null}
             # Get all PVs and their backing PDs
             kubectl get pv -o json | jq -r '.items[].spec.gcePersistentDisk.pdName' | sort -u
             ```
             For each PD name returned (replace `DISK_NAME`, `LOCATION`, `PROJECT_ID`):
             ```bash theme={null}
             gcloud compute disks describe DISK_NAME \
               --location=LOCATION \
               --project=PROJECT_ID \
               --format=json | jq '.diskEncryptionKey'
             ```
           * Record whether `kmsKeyName` is present and which key is used. Disks without `kmsKeyName` use Google-managed keys.

        2. **Review or create a suitable CMEK key in Cloud KMS**
           * On any machine with `gcloud` access, list existing keys that could be used for PD encryption:
             ```bash theme={null}
             gcloud kms keyrings list --location=LOCATION --project=PROJECT_ID
             gcloud kms keys list --location=LOCATION --keyring=KEYRING_NAME --project=PROJECT_ID
             ```
           * If no appropriate key exists, create one per your org’s policy (algorithm, rotation, IAM). Ensure key location matches or is compatible with your cluster’s region/zone.

        3. **Verify IAM and key policy allow GKE to use the CMEK key**
           * On any machine with `gcloud` access, get the GKE node service account (or default compute service account):
             ```bash theme={null}
             gcloud container clusters describe CLUSTER_NAME \
               --region=REGION \
               --project=PROJECT_ID \
               --format='get(nodeConfig.serviceAccount)'
             ```
           * Grant it the necessary roles on the KMS key (e.g. `roles/cloudkms.cryptoKeyEncrypterDecrypter`), adjusting for your security policy:
             ```bash theme={null}
             gcloud kms keys add-iam-policy-binding KEY_NAME \
               --location=LOCATION \
               --keyring=KEYRING_NAME \
               --member="serviceAccount:SERVICE_ACCOUNT_EMAIL" \
               --role="roles/cloudkms.cryptoKeyEncrypterDecrypter" \
               --project=PROJECT_ID
             ```

        4. **Decide per-PV remediation: recreate disks with CMEK or accept current state**
           * For each disk without CMEK, decide:
             * If data is non-critical or can be migrated: plan to create a new PD with CMEK and migrate workload data (e.g., via backup/restore, filesystem copy, or application-level migration).
             * If data is critical and difficult to migrate: document the business justification for continuing with Google-managed keys until a migration window is available.
           * Note that existing PDs cannot be “converted” in-place from Google-managed to CMEK; they must be recreated and data migrated.

        5. **Implement CMEK for new PD-backed PVs and workloads**
           * For disks created via `gcloud`, specify the key:
             ```bash theme={null}
             gcloud compute disks create NEW_DISK_NAME \
               --size=SIZE_GB \
               --type=DISK_TYPE \
               --zone=ZONE \
               --project=PROJECT_ID \
               --kms-key=projects/PROJECT_ID/locations/LOCATION/keyRings/KEYRING_NAME/cryptoKeys/KEY_NAME
             ```
           * For dynamically provisioned PVs, update your StorageClass (via IaC or console) to use the CMEK key per the GKE CMEK documentation, so future PVs are backed by CMEK-encrypted PDs.

        6. **Verify remediation**
           * After recreating/migrating PVs and/or updating StorageClasses, re-run the disk inspection for relevant PDs:
             ```bash theme={null}
             gcloud compute disks describe DISK_NAME \
               --location=LOCATION \
               --project=PROJECT_ID \
               --format=json | jq '.diskEncryptionKey.kmsKeyName'
             ```
           * Confirm `kmsKeyName` is set to the expected KMS key for all disks that must comply with this control.
      </Accordion>

      <Accordion title="Using kubectl">
        kubectl cannot enable or change Customer-Managed Encryption Keys (CMEK) for GKE Persistent Disks, because CMEK is configured at the GCP/GKE and disk/IaC level, not via Kubernetes API objects. Use the Google Cloud Console, `gcloud` CLI, or your IaC to configure CMEK as described in the Manual Steps section.
      </Accordion>

      <Accordion title="Automation">
        ```bash theme={null}
        #!/usr/bin/env bash
        #
        # Report GKE PersistentVolumes and their underlying PD CMEK status
        # Requirements:
        # - Run on any machine with kubectl and gcloud configured
        # - kubectl context set to the target cluster
        # - gcloud configured with the correct project and permissions
        #
        # Usage:
        #   PROJECT_ID="my-project" LOCATION="us-central1" ./check-gke-pd-cmek.sh
        #

        set -euo pipefail

        PROJECT_ID="${PROJECT_ID:-}"
        LOCATION="${LOCATION:-}"   # zone or region, e.g. us-central1-a or us-central1

        if [[ -z "$PROJECT_ID" || -z "$LOCATION" ]]; then
          echo "ERROR: Set PROJECT_ID and LOCATION env vars before running."
          echo "Example: PROJECT_ID=my-proj LOCATION=us-central1-a $0"
          exit 1
        fi

        if ! command -v kubectl >/dev/null 2>&1; then
          echo "ERROR: kubectl not found in PATH."
          exit 1
        fi

        if ! command -v gcloud >/dev/null 2>&1; then
          echo "ERROR: gcloud not found in PATH."
          exit 1
        fi

        echo "Checking PersistentVolumes in cluster: $(kubectl config current-context)"
        echo "Project: $PROJECT_ID"
        echo "Location: $LOCATION"
        echo

        # Header
        printf '%-40s %-15s %-20s %-80s\n' "PV_NAME" "STATUS" "PD_NAME" "KMS_KEY_NAME"
        printf '%-40s %-15s %-20s %-80s\n' "------" "------" "-------" "-----------"

        # Get PVs that are backed by GCE PDs
        kubectl get pv -o json \
        | jq -r '.items[]
          | select(.spec.gcePersistentDisk != null or .spec.persistentVolumeSource.gcePersistentDisk != null)
          | [
              .metadata.name,
              (if .spec.gcePersistentDisk != null
                 then .spec.gcePersistentDisk.pdName
                 else .spec.persistentVolumeSource.gcePersistentDisk.pdName
               end)
            ]
            | @tsv' \
        | while IFS=$'\t' read -r PV_NAME PD_NAME; do
            # Query the disk encryption key
            KMS_KEY_NAME=$(gcloud compute disks describe "$PD_NAME" \
              --location "$LOCATION" \
              --project "$PROJECT_ID" \
              --format="value(diskEncryptionKey.kmsKeyName)" 2>/dev/null || true)

            if [[ -z "$KMS_KEY_NAME" ]]; then
              STATUS="NO_CMEK"
            else
              STATUS="CMEK_ENABLED"
            fi

            printf '%-40s %-15s %-20s %-80s\n' "$PV_NAME" "$STATUS" "$PD_NAME" "${KMS_KEY_NAME:-<google-managed>}"
          done

        cat <<'EOF'

        Interpretation:

        - Rows with STATUS = CMEK_ENABLED:
            These Persistent Disks are encrypted with a Customer-Managed Encryption Key.
            KMS_KEY_NAME shows the full key resource path.

        - Rows with STATUS = NO_CMEK and KMS_KEY_NAME = <google-managed> (or empty):
            These Persistent Disks rely on default Google-managed encryption.
            For the CIS GKE 5.9.1 control, these entries are the findings that
            require review. Decide whether they should be recreated or future
            disks configured with CMEK following:
            https://cloud.google.com/kubernetes-engine/docs/how-to/using-cmek

        Note: This script only reports current state; it does not modify any disks or keys.
        EOF
        ```
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
