Ensure Encryption Providers Are Appropriately Configured
More Info:
Where etcd encryption is used, appropriate providers should be configured.
Risk Level
Medium
Address
Security
Compliance Standards
- CIS Kubernetes
Triage and Remediation
- Remediation
Remediation
Manual Steps
-
Identify the current encryption provider config file (if any)
Run on every control plane node:ps -ef | grep kube-apiserver | grep -v grep | grep -- --encryption-provider-configIf present, note the path after
--encryption-provider-config=(for example/etc/kubernetes/encryption-config.yaml). If not present, you will add this flag in a later step. -
Create or edit the EncryptionConfig file with a strong provider
Run on every control plane node. Adjust the path if you already have a config file; otherwise use the example path below:sudo mkdir -p /etc/kubernetessudo vi /etc/kubernetes/encryption-config.yamlPut content similar to this, ensuring the first provider under
resources.providersis one ofaescbc,kms, orsecretbox(example usesaescbc):apiVersion: apiserver.config.k8s.io/v1kind: EncryptionConfigurationresources:- resources:- secretsproviders:- aescbc:keys:- name: key1secret: REPLACE_WITH_BASE64_ENCODED_32_BYTE_KEY- identity: {}Generate a 32‑byte key and base64-encode it (run once, then paste the output in place of
REPLACE_WITH_BASE64_ENCODED_32_BYTE_KEY):head -c 32 /dev/urandom | base64 -
Ensure kube-apiserver uses the EncryptionConfig file
Run on every control plane node and edit the static pod manifest:sudo vi /etc/kubernetes/manifests/kube-apiserver.yamlIn the
command:orargs:list, ensure there is an entry like:- --encryption-provider-config=/etc/kubernetes/encryption-config.yamlSave the file. Because this is a static pod manifest under
/etc/kubernetes/manifests, the kubelet will automatically restart the API server; expect a brief control-plane interruption. -
Optionally re-encrypt existing resources with the new provider
Run on any machine with kubectl access (with cluster-admin privileges). This is an operational decision; coordinate during a maintenance window for large clusters.kubectl get secrets --all-namespaces -o json | \kubectl replace -f -This forces secrets to be rewritten and stored using the configured encryption provider.
-
Verify the effective encryption provider
Run on every control plane node (after kube-apiserver has restarted):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=/'; \fiConfirm the printed
provider=line showsaescbc,kms, orsecretboxas the first provider.
Using kubectl
kubectl cannot modify the kube-apiserver static pod manifest or its --encryption-provider-config setting; those are host-level files under /etc/kubernetes/manifests on every control plane node. To address this finding, follow the guidance in the Manual Steps section and apply the changes directly on the control plane nodes.
Automation
#!/usr/bin/env bash
#
# Automation: Ensure Encryption Providers Are Appropriately Configured
#
# RUN ON: every control plane node (with root or sudo)
#
# NOTES:
# - This script only assists with configuration and verification.
# - You MUST review and customize the generated EncryptionConfig before enabling it.
# - Changing /etc/kubernetes/manifests/kube-apiserver.yaml restarts the kube-apiserver static pod.
set -euo pipefail
APISERVER_MANIFEST="/etc/kubernetes/manifests/kube-apiserver.yaml"
ENCRYPTION_CONFIG_DIR="/etc/kubernetes"
ENCRYPTION_CONFIG_FILE="${ENCRYPTION_CONFIG_DIR}/encryption-config.yaml"
BACKUP_SUFFIX="$(date +%Y%m%d-%H%M%S)"
require_root() {
if [ "$(id -u)" -ne 0 ]; then
echo "ERROR: Run as root or with sudo." >&2
exit 1
fi
}
backup_file() {
local f="$1"
if [ -f "$f" ]; then
cp -p "$f" "${f}.${BACKUP_SUFFIX}.bak"
echo "Backup created: ${f}.${BACKUP_SUFFIX}.bak"
fi
}
detect_current_encryption_config() {
# Prefer manifest flag; fall back to running process (for verification)
if grep -q -- '--encryption-provider-config=' "$APISERVER_MANIFEST" 2>/dev/null; then
grep -- '--encryption-provider-config=' "$APISERVER_MANIFEST" \
| sed 's/.*--encryption-provider-config=\([^" ]*\).*/\1/' \
| head -n1
else
ps -ef | grep kube-apiserver | grep -- --encryption-provider-config \
| sed 's%.*encryption-provider-config[= ]\([^ ]*\).*%\1%' \
| head -n1 || true
fi
}
ensure_encryption_config_file() {
mkdir -p "$ENCRYPTION_CONFIG_DIR"
chmod 700 "$ENCRYPTION_CONFIG_DIR"
if [ -f "$ENCRYPTION_CONFIG_FILE" ]; then
echo "EncryptionConfig already exists at ${ENCRYPTION_CONFIG_FILE}."
echo "Review its contents to ensure it uses aescbc, kms, or secretbox."
return 0
fi
cat > "${ENCRYPTION_CONFIG_FILE}.template" <<'EOF'
# TEMPLATE EncryptionConfig (NOT ENABLED YET)
# Review and customize before renaming to encryption-config.yaml
# Choose one of: aescbc, kms, secretbox as the first provider.
#
# Example with aescbc:
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
- configmaps
providers:
- aescbc:
keys:
- name: key1
secret: BASE64_ENCODED_32_BYTE_KEY_HERE
- identity: {}
EOF
chmod 600 "${ENCRYPTION_CONFIG_FILE}.template"
echo "Created template: ${ENCRYPTION_CONFIG_FILE}.template"
echo "Next steps (MANUAL):"
echo " 1) Generate a 32-byte key and base64 encode it, e.g.:"
echo " head -c 32 /dev/urandom | base64"
echo " 2) Edit ${ENCRYPTION_CONFIG_FILE}.template, replace BASE64_ENCODED_32_BYTE_KEY_HERE."
echo " 3) When satisfied, move it into place:"
echo " mv ${ENCRYPTION_CONFIG_FILE}.template ${ENCRYPTION_CONFIG_FILE}"
echo " 4) Re-run this script to wire it into the kube-apiserver manifest."
}
ensure_manifest_uses_encryption_config() {
if [ ! -f "$ENCRYPTION_CONFIG_FILE" ]; then
echo "WARNING: ${ENCRYPTION_CONFIG_FILE} does not exist."
echo "Create and review it (see template) before enabling."
return 0
fi
backup_file "$APISERVER_MANIFEST"
if grep -q -- '--encryption-provider-config' "$APISERVER_MANIFEST"; then
# Update existing flag path if different
local current_path
current_path="$(grep -- '--encryption-provider-config' "$APISERVER_MANIFEST" \
| sed 's/.*--encryption-provider-config=\([^" ]*\).*/\1/' \
| head -n1 || true)"
if [ "$current_path" != "$ENCRYPTION_CONFIG_FILE" ]; then
sed -i "s|--encryption-provider-config=${current_path}|--encryption-provider-config=${ENCRYPTION_CONFIG_FILE}|g" \
"$APISERVER_MANIFEST"
echo "Updated --encryption-provider-config path in kube-apiserver manifest."
else
echo "kube-apiserver manifest already points to ${ENCRYPTION_CONFIG_FILE}."
fi
else
# Inject flag under the kube-apiserver command args
# This assumes a standard static pod manifest with 'command:' or 'args:'.
if grep -q '^- kube-apiserver' "$APISERVER_MANIFEST"; then
# Append as a new arg line under the command block
awk -v cfg="$ENCRYPTION_CONFIG_FILE" '
/^ *- kube-apiserver/ && !seen {
print
print " - --encryption-provider-config="cfg
seen=1
next
}
{ print }
' "$APISERVER_MANIFEST" > "${APISERVER_MANIFEST}.tmp"
mv "${APISERVER_MANIFEST}.tmp" "$APISERVER_MANIFEST"
echo "Added --encryption-provider-config to kube-apiserver manifest."
else
echo "WARNING: Could not automatically inject --encryption-provider-config."
echo "Edit ${APISERVER_MANIFEST} manually to add:"
echo " - --encryption-provider-config=${ENCRYPTION_CONFIG_FILE}"
echo "under the kube-apiserver command/args."
fi
fi
echo "NOTE: kubelet will restart the kube-apiserver static pod after manifest changes."
}
verify_encryption_provider() {
echo "Verifying encryption provider on this control plane node..."
ENCRYPTION_PROVIDER_CONFIG="$(detect_current_encryption_config || true)"
if [ -z "${ENCRYPTION_PROVIDER_CONFIG:-}" ] || [ ! -e "$ENCRYPTION_PROVIDER_CONFIG" ]; then
echo "ENCRYPTION_PROVIDER_CONFIG not set or file not found."
echo "Current kube-apiserver process flags:"
ps -ef | grep kube-apiserver | grep -v grep || true
return 1
fi
echo "Using EncryptionConfig file: $ENCRYPTION_PROVIDER_CONFIG"
provider_line="$(grep -A1 'providers:' "$ENCRYPTION_PROVIDER_CONFIG" | tail -n1 || true)"
if [ -z "$provider_line" ]; then
echo "Could not detect first provider from ${ENCRYPTION_PROVIDER_CONFIG}."
return 1
fi
provider="$(echo "$provider_line" | grep -o "[A-Za-z]*" | head -n1 || true)"
if [ -z "$provider" ]; then
echo "No provider name detected from line: $provider_line"
return 1
fi
echo "Detected first provider: provider=${provider}"
case "$provider" in
aescbc|kms|secretbox)
echo "OK: Encryption provider is one of the recommended types (aescbc, kms, secretbox)."
;;
*)
echo "WARNING: Encryption provider '${provider}' is NOT one of the recommended types (aescbc, kms, secretbox)."
return 1
;;
esac
}
main() {
require_root
if [ ! -f "$APISERVER_MANIFEST" ]; then
echo "ERROR: kube-apiserver manifest not found at ${APISERVER_MANIFEST}."
exit 1
fi
ensure_encryption_config_file
ensure_manifest_uses_encryption_config
echo
echo "=== Verification ==="
verify_encryption_provider || {
echo "Verification failed. Review the EncryptionConfig and kube-apiserver manifest."
exit 1
}
echo
echo "Completed. Repeat this process on every control plane node."
}
main "$@"