Ensure Rotate Kubelet Server Certificate Argument Is Enabled
More Info:
Enable kubelet server certificate rotation.
Risk Level
Low
Address
Security
Compliance Standards
- APRA CPS 234 (Australia)
- BSI C5 (Germany)
- Brazil LGPD
- CCPA / CPRA (California)
- CIS AKS
- CIS Critical Security Controls v8
- CMMC 2.0
- CSA Cloud Controls Matrix v4
- DPDPA
- Digital Operational Resilience Act (EU)
- Essential 8
- ISO/IEC 27017
- ISO/IEC 27018
- ISO/IEC 27701
- KSA PDPL
- MAS Technology Risk Management (Singapore)
- MITRE ATT&CK (Cloud)
- NIST SP 800-171
- NYDFS 23 NYCRR 500
- SWIFT Customer Security Controls Framework
- Sarbanes-Oxley IT General Controls
- UK NCSC Cyber Assessment Framework
Triage and Remediation
- Remediation
Remediation
Manual Steps
-
On every worker node, open the kubelet systemd drop-in file for editing:
sudo vi /etc/systemd/system/kubelet.service.d/10-kubeadm.conf -
In that file, locate the line starting with
Environment="KUBELET_CERTIFICATE_ARGS=. If it exists, ensure it includes the feature gate flag; if it does not exist, add a new line. For example:Environment="KUBELET_CERTIFICATE_ARGS=--feature-gates=RotateKubeletServerCertificate=true"If there are already other kubelet args in this variable, append the feature gate separated by a space, keeping everything inside the same quotes.
-
Save the file and reload systemd units on the same worker node:
sudo systemctl daemon-reload -
Restart the kubelet on the same worker node (this will briefly disrupt workloads scheduled on this node):
sudo systemctl restart kubelet.service -
Repeat steps 1–4 on every remaining worker node.
-
Verification on each worker node: confirm the kubelet process was started with the correct feature gate:
/bin/ps -fC kubelet | grep -- '--feature-gates=RotateKubeletServerCertificate=true'
Using kubectl
kubectl cannot modify kubelet process flags or the host-level files on worker nodes, so this finding cannot be fixed via Kubernetes API objects. The required change must be made on each worker node’s systemd configuration for kubelet (for example, /etc/systemd/system/kubelet.service.d/10-kubeadm.conf and /var/lib/kubelet/config.yaml); see the Manual Steps section for how to implement the remediation.
Automation
#!/usr/bin/env bash
#
# Automation: Enable kubelet server certificate rotation on every worker node
#
# Usage:
# 1) Put this script on each worker node (e.g. /root/fix-rotate-kubelet-cert.sh)
# 2) Run as root: bash /root/fix-rotate-kubelet-cert.sh
# 3) Safe to re-run; it is idempotent.
set -euo pipefail
SYSTEMD_DROPIN_DIR="/etc/systemd/system/kubelet.service.d"
KUBEADM_CONF="${SYSTEMD_DROPIN_DIR}/10-kubeadm.conf"
FEATURE_FLAG="RotateKubeletServerCertificate=true"
echo "[INFO] Running on worker node: $(hostname)"
if [[ ! -f "${KUBEADM_CONF}" ]]; then
echo "[ERROR] ${KUBEADM_CONF} not found. This script expects kubelet to be managed by systemd + kubeadm."
exit 1
fi
# Ensure the drop-in directory exists (usually does)
mkdir -p "${SYSTEMD_DROPIN_DIR}"
# Backup once
BACKUP="${KUBEADM_CONF}.bak.$(date +%Y%m%d%H%M%S)"
cp "${KUBEADM_CONF}" "${BACKUP}"
echo "[INFO] Backed up ${KUBEADM_CONF} to ${BACKUP}"
# Read current file
CURRENT_CONTENT="$(cat "${KUBEADM_CONF}")"
# Normalize: ensure KUBELET_CERTIFICATE_ARGS line exists
if ! grep -q '^Environment="KUBELET_CERTIFICATE_ARGS=' "${KUBEADM_CONF}"; then
echo "[INFO] KUBELET_CERTIFICATE_ARGS not present; adding new Environment line."
{
echo ""
echo 'Environment="KUBELET_CERTIFICATE_ARGS=--feature-gates=RotateKubeletServerCertificate=true"'
} >> "${KUBEADM_CONF}"
else
echo "[INFO] KUBELET_CERTIFICATE_ARGS found; updating as needed."
# Extract existing value
EXISTING_LINE="$(grep '^Environment="KUBELET_CERTIFICATE_ARGS=' "${KUBEADM_CONF}")"
EXISTING_VALUE="${EXISTING_LINE#Environment=\"KUBELET_CERTIFICATE_ARGS=}"
EXISTING_VALUE="${EXISTING_VALUE%\"}"
# If the feature gate is already present and set to true, no change needed
if echo "${EXISTING_VALUE}" | grep -q 'RotateKubeletServerCertificate=true'; then
echo "[INFO] RotateKubeletServerCertificate=true already present in KUBELET_CERTIFICATE_ARGS."
else
# Remove any existing RotateKubeletServerCertificate feature gate (true/false) to avoid duplicates
CLEANED_VALUE="$(echo "${EXISTING_VALUE}" \
| sed -E 's/(,)?RotateKubeletServerCertificate=(true|false)(,)?/\1\3/g' \
| sed -E 's/,,+/,/g' \
| sed -E 's/^,//;s/,$//')"
# Append (or set) the required feature gate
if [[ -z "${CLEANED_VALUE}" ]]; then
NEW_VALUE="--feature-gates=RotateKubeletServerCertificate=true"
elif [[ "${CLEANED_VALUE}" == *"--feature-gates="* ]]; then
# Append to existing --feature-gates list
NEW_VALUE="$(echo "${CLEANED_VALUE}" \
| sed -E "s/(--feature-gates=[^ ]*)/\1,RotateKubeletServerCertificate=true/")"
else
# Add a new --feature-gates flag alongside existing args
NEW_VALUE="${CLEANED_VALUE} --feature-gates=RotateKubeletServerCertificate=true"
fi
# Replace the line in the file
TMP_FILE="$(mktemp)"
sed "s#^Environment=\"KUBELET_CERTIFICATE_ARGS=.*#Environment=\"KUBELET_CERTIFICATE_ARGS=${NEW_VALUE}\"#" \
"${KUBEADM_CONF}" > "${TMP_FILE}"
mv "${TMP_FILE}" "${KUBEADM_CONF}"
echo "[INFO] Updated KUBELET_CERTIFICATE_ARGS to include RotateKubeletServerCertificate=true."
fi
fi
# If file content hasn't changed, skip restart
NEW_CONTENT="$(cat "${KUBEADM_CONF}")"
if [[ "${CURRENT_CONTENT}" == "${NEW_CONTENT}" ]]; then
echo "[INFO] No change to ${KUBEADM_CONF}; kubelet restart not required."
else
echo "[INFO] Reloading systemd and restarting kubelet (this will restart the kubelet process)."
systemctl daemon-reload
systemctl restart kubelet.service
fi
# Verification (same machine: worker node)
echo "[INFO] Verifying kubelet process arguments..."
if /bin/ps -fC kubelet | grep -q "RotateKubeletServerCertificate=true"; then
echo "[SUCCESS] kubelet is running with RotateKubeletServerCertificate=true:"
/bin/ps -fC kubelet
exit 0
else
echo "[ERROR] kubelet is NOT running with RotateKubeletServerCertificate=true. Current process:"
/bin/ps -fC kubelet || true
exit 1
fi