Skip to main content

API Server Encryption Providers Should Be Appropriately

More Info:

Verifies that the encryption provider config uses a strong provider such as aescbc, kms or secretbox so secrets at rest are properly encrypted.

Risk Level

High

Address

Security

Compliance Standards

  • CIS Kubernetes

Triage and Remediation

Remediation

Manual Steps
  1. Identify the encryption provider config file (control plane node)

    ENCRYPTION_PROVIDER_CONFIG=$(ps -ef | grep kube-apiserver | grep -- --encryption-provider-config | sed 's%.*encryption-provider-config[= ]\([^ ]*\).*%\1%')
    echo "$ENCRYPTION_PROVIDER_CONFIG"

    If this prints nothing, check /etc/kubernetes/manifests/kube-apiserver.yaml for an --encryption-provider-config flag and its path.

  2. Back up the existing encryption config and manifest (control plane node)

    mkdir -p /root/enc-backup
    cp -p "$ENCRYPTION_PROVIDER_CONFIG" /root/enc-backup/encryption-config-$(date +%F-%H%M%S).yaml
    cp -p /etc/kubernetes/manifests/kube-apiserver.yaml /root/enc-backup/kube-apiserver-$(date +%F-%H%M%S).yaml
  3. Edit the encryption configuration to use a strong provider (control plane node)
    Open the file in an editor:

    vi "$ENCRYPTION_PROVIDER_CONFIG"

    Replace or add a resources section using a strong provider (example using aescbc):

    apiVersion: apiserver.config.k8s.io/v1
    kind: EncryptionConfiguration
    resources:
    - resources:
    - secrets
    providers:
    - aescbc:
    keys:
    - name: key1
    secret: <BASE64_ENCODED_32_BYTE_KEY>
    - identity: {}

    Generate a 32‑byte key and base64‑encode it (run in another shell on the same node) and paste it into secret:

    head -c 32 /dev/urandom | base64
  4. Confirm kube-apiserver is using the encryption config (control plane node)
    Ensure /etc/kubernetes/manifests/kube-apiserver.yaml contains the flag and correct path; edit if needed:

    vi /etc/kubernetes/manifests/kube-apiserver.yaml

    Under spec.containers[].command, ensure a line like:

    - --encryption-provider-config=/etc/kubernetes/encryption-config.yaml

    Save the file; the kube-apiserver static pod will restart automatically when you edit this manifest.

  5. Optionally re-encrypt existing secrets (any machine with kubectl access)
    This is not enforced by the benchmark but is typically required to ensure all stored secrets use the new provider. Run:

    kubectl get secrets --all-namespaces -o json | \
    kubectl replace -f -
  6. Verification (control plane node)
    After the kube-apiserver pod has restarted and is Ready, re-run the audit logic:

    ENCRYPTION_PROVIDER_CONFIG=$(ps -ef | grep kube-apiserver | grep -- --encryption-provider-config | sed 's%.*encryption-provider-config[= ]\([^ ]*\).*%\1%')
    if test -e "$ENCRYPTION_PROVIDER_CONFIG"; then
    grep -A1 'providers:' "$ENCRYPTION_PROVIDER_CONFIG" | tail -n1 | grep -o "[A-Za-z]*" | sed 's/^/provider=/'
    fi

    Confirm the output shows provider=aescbc, provider=kms, or provider=secretbox as the first provider.

Using kubectl

kubectl cannot modify the API server’s --encryption-provider-config or the file it points to, because those are host-level settings controlled by /etc/kubernetes/manifests/kube-apiserver.yaml on each control plane node. To configure strong encryption providers (aescbc, kms, or secretbox), follow the guidance in the Manual Steps section on the control plane nodes themselves.

Automation
#!/usr/bin/env bash
#
# Automation: Ensure kube-apiserver is configured with a strong encryption provider
# Provider choice: aescbc
#
# Run on: every control plane node (as root)
#
# This script:
# 1. Locates the kube-apiserver encryption-provider-config file.
# 2. Creates/updates it to use aescbc if it is weak or missing.
# 3. Ensures the kube-apiserver manifest points to it.
# 4. Relies on the kubelet static pod behavior to restart kube-apiserver.
# 5. Verifies the configuration via the benchmark audit logic.
#
# NOTE: This script does NOT generate a secure, random encryption key for you.
# You MUST replace the placeholder key with a securely generated one and
# re-run the script (safe to re-run).

set -euo pipefail

APISERVER_MANIFEST="/etc/kubernetes/manifests/kube-apiserver.yaml"
ENCRYPTION_CONFIG_PATH_DEFAULT="/etc/kubernetes/encryption-config.yaml"
PLACEHOLDER_KEY="AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" # 32-byte base64 placeholder

if [[ $EUID -ne 0 ]]; then
echo "ERROR: This script must be run as root on each control plane node." >&2
exit 1
fi

if [[ ! -f "$APISERVER_MANIFEST" ]]; then
echo "ERROR: kube-apiserver manifest not found at $APISERVER_MANIFEST" >&2
exit 1
fi

echo ">>> Detecting existing --encryption-provider-config flag..."
set +e
CURRENT_CONFIG_PATH=$(grep -E -- '--encryption-provider-config(=| )' "$APISERVER_MANIFEST" 2>/dev/null | \
sed -E 's/.*--encryption-provider-config[= ]([^ ]*).*/\1/' | head -n1)
set -e

if [[ -z "${CURRENT_CONFIG_PATH:-}" ]]; then
echo "No --encryption-provider-config flag found; will use default path: $ENCRYPTION_CONFIG_PATH_DEFAULT"
ENCRYPTION_CONFIG_PATH="$ENCRYPTION_CONFIG_PATH_DEFAULT"
else
ENCRYPTION_CONFIG_PATH="$CURRENT_CONFIG_PATH"
echo "Found existing encryption provider config path: $ENCRYPTION_CONFIG_PATH"
fi

# Ensure directory exists
ENCRYPTION_CONFIG_DIR=$(dirname "$ENCRYPTION_CONFIG_PATH")
mkdir -p "$ENCRYPTION_CONFIG_DIR"
chmod 700 "$ENCRYPTION_CONFIG_DIR"

echo ">>> Writing/Updating encryption provider config at $ENCRYPTION_CONFIG_PATH"

# If an existing file is present, back it up once
if [[ -f "$ENCRYPTION_CONFIG_PATH" && ! -f "${ENCRYPTION_CONFIG_PATH}.pre-hardening.bak" ]]; then
cp -p "$ENCRYPTION_CONFIG_PATH" "${ENCRYPTION_CONFIG_PATH}.pre-hardening.bak"
echo "Backed up existing config to ${ENCRYPTION_CONFIG_PATH}.pre-hardening.bak"
fi

cat > "$ENCRYPTION_CONFIG_PATH" <<EOF
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
- configmaps
- persistentvolumeclaims
- serviceaccounts
providers:
- aescbc:
keys:
- name: key1
secret: ${PLACEHOLDER_KEY}
- identity: {}
EOF

chmod 600 "$ENCRYPTION_CONFIG_PATH"

echo ">>> Ensuring kube-apiserver manifest references encryption-provider-config..."

if grep -qE -- '--encryption-provider-config(=| )' "$APISERVER_MANIFEST"; then
# Update existing flag if path has changed
if [[ "$ENCRYPTION_CONFIG_PATH" != "$CURRENT_CONFIG_PATH" && -n "${CURRENT_CONFIG_PATH:-}" ]]; then
sed -i.bak_encprov \
-E "s#(--encryption-provider-config(=| )).*[[:space:]]#\1${ENCRYPTION_CONFIG_PATH} #g" \
"$APISERVER_MANIFEST"
echo "Updated existing --encryption-provider-config flag in kube-apiserver manifest."
else
echo "--encryption-provider-config already present; leaving as-is."
fi
else
# Add the flag under the kube-apiserver container args
# This assumes a standard static pod manifest layout.
if grep -q 'name: kube-apiserver' "$APISERVER_MANIFEST" && grep -q 'args:' "$APISERVER_MANIFEST"; then
cp -p "$APISERVER_MANIFEST" "${APISERVER_MANIFEST}.bak_encprov"
awk -v path="$ENCRYPTION_CONFIG_PATH" '
/name: kube-apiserver/ { in_apiserver=1 }
in_apiserver && /args:/ && !added {
print $0
indent = match($0, /args:/) - 1
pad = sprintf("%*s", indent+2, "")
print pad "- --encryption-provider-config=" path
added=1
next
}
{ print $0 }
' "$APISERVER_MANIFEST.bak_encprov" > "$APISERVER_MANIFEST"
echo "Added --encryption-provider-config flag to kube-apiserver manifest."
else
echo "WARNING: Could not safely inject --encryption-provider-config into kube-apiserver manifest." >&2
echo " Please edit $APISERVER_MANIFEST manually to add:" >&2
echo " - --encryption-provider-config=${ENCRYPTION_CONFIG_PATH}" >&2
fi
fi

echo ">>> Waiting for kube-apiserver static pod restart (triggered by manifest/config change)..."
echo " Monitor with: crictl ps | grep kube-apiserver (or docker/ctr as appropriate)."
sleep 10

echo ">>> Verifying that a strong encryption provider is configured..."

# Re-run the benchmark audit logic
set +e
ENCRYPTION_PROVIDER_CONFIG=$(ps -ef | grep kube-apiserver | grep -- --encryption-provider-config | \
sed 's%.*encryption-provider-config[= ]\([^ ]*\).*%\1%')
if [[ -z "$ENCRYPTION_PROVIDER_CONFIG" ]]; then
echo "VERIFICATION FAILED: kube-apiserver process does not show --encryption-provider-config flag." >&2
exit 1
fi

PROVIDER_LINE=$(if test -e "$ENCRYPTION_PROVIDER_CONFIG"; then \
grep -A1 'providers:' "$ENCRYPTION_PROVIDER_CONFIG" | tail -n1; fi)

PROVIDER_NAME=$(echo "$PROVIDER_LINE" | grep -o "[A-Za-z]*" | sed 's/^/provider=/' | cut -d= -f2)
set -e

if [[ "$PROVIDER_NAME" == "aescbc" || "$PROVIDER_NAME" == "kms" || "$PROVIDER_NAME" == "secretbox" ]]; then
echo "VERIFICATION PASSED: Strong encryption provider in use: $PROVIDER_NAME"
exit 0
else
echo "VERIFICATION FAILED: Expected aescbc/kms/secretbox, found: ${PROVIDER_NAME:-none}" >&2
echo "Check $ENCRYPTION_PROVIDER_CONFIG and adjust providers accordingly." >&2
exit 1
fi