Verify RotateKubeletServerCertificate Argument Is Enabled
More Info:
Enable kubelet server certificate rotation.
Risk Level
Low
Address
Security
Compliance Standards
- CIS Kubernetes
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 that defines
KUBELET_CERTIFICATE_ARGS(create it if it does not exist), and set/merge the feature gate so it contains:Environment="KUBELET_CERTIFICATE_ARGS=--feature-gates=RotateKubeletServerCertificate=true"If other feature gates are already present, include this one in the same list, for example:
Environment="KUBELET_CERTIFICATE_ARGS=--feature-gates=ExistingFeature=true,RotateKubeletServerCertificate=true" -
Ensure the kubelet command line in the same file actually uses
KUBELET_CERTIFICATE_ARGS(add it if missing). TheExecStart=line should include:ExecStart=... $KUBELET_CERTIFICATE_ARGS ... -
Reload systemd and restart kubelet on the same worker node to apply the change (this will restart the kubelet process):
sudo systemctl daemon-reloadsudo systemctl restart kubelet.service -
Repeat steps 1–4 on every worker node in the cluster.
-
Verification (on each worker node): confirm the kubelet process was started with the correct feature gate:
/bin/ps -fC kubeletCheck that the output command line includes:
--feature-gates=RotateKubeletServerCertificate=true(or within a comma-separated--feature-gates=list).
Using kubectl
kubectl cannot configure kubelet process flags or edit host files like /etc/systemd/system/kubelet.service.d/10-kubeadm.conf or /var/lib/kubelet/config.yaml on worker nodes. To enable RotateKubeletServerCertificate, you must modify the kubelet systemd unit and configuration directly on every worker node; see the Manual Steps section for the exact host-level commands.
Automation
#!/usr/bin/env bash
#
# Enable kubelet server certificate rotation on all worker nodes.
# Run on each worker node via SSH (can be orchestrated by Ansible).
# Idempotent and safe to re-run.
set -euo pipefail
KUBELET_DROPIN="/etc/systemd/system/kubelet.service.d/10-kubeadm.conf"
BACKUP_SUFFIX="$(date +%Y%m%d%H%M%S)"
echo "==> Checking for kubelet systemd drop-in: ${KUBELET_DROPIN}"
if [[ ! -f "${KUBELET_DROPIN}" ]]; then
echo "ERROR: ${KUBELET_DROPIN} not found. This script assumes kubeadm-style systemd config."
echo " Adjust manually for your environment."
exit 1
fi
echo "==> Backing up existing file (once per unique content)"
CHECKSUM_FILE="${KUBELET_DROPIN}.sha256"
CURRENT_SUM="$(sha256sum "${KUBELET_DROPIN}" | awk '{print $1}')"
if [[ ! -f "${CHECKSUM_FILE}" ]] || [[ "$(cat "${CHECKSUM_FILE}")" != "${CURRENT_SUM}" ]]; then
cp -p "${KUBELET_DROPIN}" "${KUBELET_DROPIN}.bak-${BACKUP_SUFFIX}"
echo "${CURRENT_SUM}" > "${CHECKSUM_FILE}"
echo " Backup created at ${KUBELET_DROPIN}.bak-${BACKUP_SUFFIX}"
else
echo " No changes since last backup; skipping new backup."
fi
echo "==> Ensuring KUBELET_CERTIFICATE_ARGS contains RotateKubeletServerCertificate=true"
# Ensure the variable exists
if ! grep -q '^Environment="KUBELET_CERTIFICATE_ARGS=' "${KUBELET_DROPIN}"; then
echo 'Environment="KUBELET_CERTIFICATE_ARGS=--feature-gates=RotateKubeletServerCertificate=true"' \
>> "${KUBELET_DROPIN}"
echo " Added new KUBELET_CERTIFICATE_ARGS line."
else
# Update existing line, merging other feature-gates if present
tmpfile="$(mktemp)"
while IFS='' read -r line; do
if [[ "${line}" =~ ^Environment=\"KUBELET_CERTIFICATE_ARGS= ]]; then
# Strip prefix/suffix
payload="${line#Environment=\"KUBELET_CERTIFICATE_ARGS=}"
payload="${payload%\"}"
# If RotateKubeletServerCertificate already enabled, leave as-is
if [[ "${payload}" == *"RotateKubeletServerCertificate=true"* ]]; then
echo "${line}" >> "${tmpfile}"
continue
fi
# Remove any existing RotateKubeletServerCertificate flag (true/false) to avoid duplicates
payload="$(echo "${payload}" | sed -E 's/--feature-gates=([^"]*)RotateKubeletServerCertificate=(true|false),?([^"]*)/--feature-gates=\1\3/g' )"
# Clean potential trailing commas in feature-gates
payload="$(echo "${payload}" | sed -E 's/--feature-gates=,*/--feature-gates=/')"
payload="$(echo "${payload}" | sed -E 's/,\"/\"/')"
# If there is an existing --feature-gates, append to it; otherwise add a new one
if [[ "${payload}" == *"--feature-gates="* ]]; then
payload="$(echo "${payload}" | sed -E 's/(--feature-gates=[^"]*)/\1,RotateKubeletServerCertificate=true/')"
else
if [[ -n "${payload}" ]]; then
payload="${payload} --feature-gates=RotateKubeletServerCertificate=true"
else
payload="--feature-gates=RotateKubeletServerCertificate=true"
fi
fi
echo "Environment=\"KUBELET_CERTIFICATE_ARGS=${payload}\"" >> "${tmpfile}"
else
echo "${line}" >> "${tmpfile}"
fi
done < "${KUBELET_DROPIN}"
mv "${tmpfile}" "${KUBELET_DROPIN}"
echo " Updated existing KUBELET_CERTIFICATE_ARGS line."
fi
echo "==> Reloading systemd and restarting kubelet (this restarts the kubelet process)"
systemctl daemon-reload
systemctl restart kubelet.service
echo "==> Verifying kubelet process flags"
/bin/ps -fC kubelet || {
echo "ERROR: kubelet process not found after restart."
exit 1
}
if /bin/ps -fC kubelet | grep -q 'RotateKubeletServerCertificate=true'; then
echo "SUCCESS: kubelet is running with RotateKubeletServerCertificate=true"
else
echo "WARNING: kubelet process does not show RotateKubeletServerCertificate=true in arguments."
echo " Verify that systemd drop-in is being used and that no other unit overrides it:"
echo " systemctl cat kubelet.service"
exit 1
fi