Skip to main content

Ensure Kubernetes Secrets Are Encrypted

More Info:

Encrypt Kubernetes secrets at rest using a customer-managed key in Azure Key Vault (KMS) so sensitive data is protected beyond the default platform encryption.

Risk Level

High

Address

Security

Compliance Standards

  • CIS AKS

Triage and Remediation

Remediation

Manual Steps
  1. Identify the AKS cluster and resource group

    • On any machine with Azure CLI access:
      az account show
      az aks list -o table
    • Note the name and resourceGroup of the target cluster.
  2. Check if a customer-managed key (CMK) is configured for secrets encryption

    • On any machine with Azure CLI access:
      az aks show \
      --name <AKS_CLUSTER_NAME> \
      --resource-group <RESOURCE_GROUP> \
      --query "securityProfile.secretsEncryption" -o json
    • Review the output:
      • enabled: should be true.
      • keyVaultKeyUri: should point to your Azure Key Vault key.
    • If enabled is null/false or keyVaultKeyUri is empty, the cluster is not using Key Vault CMK for secrets encryption.
  3. Review the Key Vault key used (if any)

    • On any machine with Azure CLI access, if keyVaultKeyUri was set:
      KEY_URI="<PASTE_keyVaultKeyUri_FROM_PREVIOUS_STEP>"
      az keyvault key show --id "$KEY_URI"
    • Verify:
      • The key is in a production Key Vault (correct subscription, resource group, region).
      • The key is in an appropriate state (e.g., not expired, not disabled).
      • The key rotation policy meets your org’s requirements.
  4. Decide on desired state: enable or update CMK-based encryption

    • If CMK-based secrets encryption is not enabled and your policy requires it, plan to enable it with a suitable Key Vault and key.
    • If enabled but using an incorrect Key Vault or key, plan to update to the correct keyVaultKeyUri.
    • Ensure the AKS-managed identity (system-assigned or user-assigned) has get, wrapKey, and unwrapKey permissions on the Key Vault key.
  5. Configure or update CMK secrets encryption (if change is required)

    • On any machine with Azure CLI access, first ensure the AKS identity has the needed Key Vault access:
      # Get the AKS kubelet identity principalId (used for key access)
      az aks show \
      --name <AKS_CLUSTER_NAME> \
      --resource-group <RESOURCE_GROUP> \
      --query "identityProfile.kubeletidentity.objectId" -o tsv
    • In Azure Portal or via az keyvault set-policy, grant that identity get, wrapKey, unwrapKey on the target key.
    • Then enable or update secrets encryption with your Key Vault key:
      az aks update \
      --name <AKS_CLUSTER_NAME> \
      --resource-group <RESOURCE_GROUP> \
      --enable-secrets-encryption \
      --key-vault-key-url <KEY_VAULT_KEY_URI>
  6. Verify configuration after changes

    • On any machine with Azure CLI access:
      az aks show \
      --name <AKS_CLUSTER_NAME> \
      --resource-group <RESOURCE_GROUP> \
      --query "securityProfile.secretsEncryption" -o json
    • Confirm enabled is true and keyVaultKeyUri matches the intended Azure Key Vault CMK.
Using kubectl

kubectl cannot configure encryption at rest for Kubernetes Secrets in AKS; this must be set on the managed control plane via the Azure portal, Azure CLI, or your IaC (ARM/Bicep/Terraform) targeting AKS and Azure Key Vault. Refer to the Manual Steps section for how to review and configure customer-managed key encryption for Secrets.

Automation
#!/usr/bin/env bash
# Report AKS secret encryption-at-rest configuration using Azure CLI and kubectl.
# Requirements:
# - Azure CLI logged in with access to the subscription(s)
# - kubectl configured (only used to list clusters via az; no in-cluster changes)
# - jq installed

set -euo pipefail

# ====== CONFIGURATION ======
# Optional: limit to specific subscription IDs (space-separated). Leave empty for all.
SUBSCRIPTIONS="${SUBSCRIPTIONS:-}"

# ====== FUNCTIONS ======

list_subscriptions() {
if [ -n "${SUBSCRIPTIONS}" ]; then
for sid in ${SUBSCRIPTIONS}; do
echo "${sid}"
done
else
az account list --query '[].id' -o tsv
fi
}

report_cluster_encryption() {
local sub="$1" rg="$2" name="$3"

# Query cluster encryption profile (AKS)
# For AKS, customer-managed keys for secret encryption are surfaced via:
# - securityProfile.azureKeyVaultKms
# Fallback fields are printed if not present.
local raw
raw="$(az aks show \
--subscription "${sub}" \
--resource-group "${rg}" \
--name "${name}" \
-o json)"

local kv_enabled kv_key_id kv_kms_version kv_anon

kv_enabled="$(echo "${raw}" | jq -r '.securityProfile.azureKeyVaultKms.enabled // "false"')"
kv_key_id="$(echo "${raw}" | jq -r '.securityProfile.azureKeyVaultKms.keyId // "null"')"
kv_kms_version="$(echo "${raw}" | jq -r '.securityProfile.azureKeyVaultKms.keyVaultNetworkAccess // "null"')"

echo "=================================================================="
echo "Subscription : ${sub}"
echo "ResourceGroup: ${rg}"
echo "Cluster : ${name}"
echo "------------------------------------------------------------------"
echo "Azure Key Vault KMS enabled : ${kv_enabled}"
echo "Azure Key Vault KMS keyId : ${kv_key_id}"
echo "Azure Key Vault KMS network access : ${kv_kms_version}"
echo

# Interpretation / flags
if [ "${kv_enabled}" != "true" ]; then
echo "STATUS: NOT USING CUSTOMER-MANAGED KEY (Azure Key Vault KMS disabled)"
echo "INTERPRETATION: Secrets rely only on default platform-managed encryption."
else
if [ "${kv_key_id}" = "null" ] || [ -z "${kv_key_id}" ]; then
echo "STATUS: MISCONFIGURED - KMS enabled but keyId is not set"
else
echo "STATUS: USING CUSTOMER-MANAGED KEY"
echo "INTERPRETATION: Secrets should be encrypted at rest with the CMK:"
echo " ${kv_key_id}"
fi
fi
echo
}

# ====== MAIN ======

echo "Enumerating AKS clusters and reporting secret encryption (Azure Key Vault KMS / CMK usage)..."
echo

for sid in $(list_subscriptions); do
echo ">>> Subscription: ${sid}"
# List all AKS clusters in this subscription
while IFS=$'\t' read -r rg name; do
report_cluster_encryption "${sid}" "${rg}" "${name}"
done < <(az aks list --subscription "${sid}" --query '[].{rg:resourceGroup,name:name}' -o tsv || true)
echo
done

How to run (any machine with Azure CLI and kubectl access):

chmod +x report-aks-secret-encryption.sh
./report-aks-secret-encryption.sh

Optional: restrict to certain subscriptions:

SUBSCRIPTIONS="00000000-0000-0000-0000-000000000000 11111111-1111-1111-1111-111111111111" \
./report-aks-secret-encryption.sh

How to interpret the output

For each AKS cluster, focus on:

  • Azure Key Vault KMS enabled : true
    and
    STATUS: USING CUSTOMER-MANAGED KEY

    → This is the desired state: Kubernetes secrets are configured to use a customer-managed key in Azure Key Vault (subject to your own risk review of the key and access model).

  • Azure Key Vault KMS enabled : false
    and
    STATUS: NOT USING CUSTOMER-MANAGED KEY

    Problem / needs review for this control: the cluster is not using a customer-managed key for secret encryption; it relies only on default platform encryption.

  • Azure Key Vault KMS enabled : true
    but keyId is null or empty and
    STATUS: MISCONFIGURED - KMS enabled but keyId is not set

    Problem / needs review: KMS is nominally enabled but lacks a valid CMK. Verify cluster configuration in the Azure portal/CLI/IaC and correct or disable as appropriate.

Because this check is MANUAL, you must decide, per cluster, whether “NOT USING CUSTOMER-MANAGED KEY” is acceptable given your policy, and then configure or re-create the cluster via Azure (portal/CLI/IaC) if you require CMK-backed encryption.