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
Remediation
Manual Steps
-
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.yamlfor an--encryption-provider-configflag and its path. -
Back up the existing encryption config and manifest (control plane node)
mkdir -p /root/enc-backupcp -p "$ENCRYPTION_PROVIDER_CONFIG" /root/enc-backup/encryption-config-$(date +%F-%H%M%S).yamlcp -p /etc/kubernetes/manifests/kube-apiserver.yaml /root/enc-backup/kube-apiserver-$(date +%F-%H%M%S).yaml -
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/v1kind: EncryptionConfigurationresources:- resources:- secretsproviders:- aescbc:keys:- name: key1secret: <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 -
Confirm kube-apiserver is using the encryption config (control plane node)
Ensure/etc/kubernetes/manifests/kube-apiserver.yamlcontains the flag and correct path; edit if needed:vi /etc/kubernetes/manifests/kube-apiserver.yamlUnder
spec.containers[].command, ensure a line like:- --encryption-provider-config=/etc/kubernetes/encryption-config.yamlSave the file; the kube-apiserver static pod will restart automatically when you edit this manifest.
-
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 - -
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"; thengrep -A1 'providers:' "$ENCRYPTION_PROVIDER_CONFIG" | tail -n1 | grep -o "[A-Za-z]*" | sed 's/^/provider=/'fiConfirm the output shows
provider=aescbc,provider=kms, orprovider=secretboxas 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