Skip to main content

Rotate Kubelet Server Certificate Argument Is Set True

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 Critical Security Controls v8
  • CIS GKE
  • 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

Manual Steps
  1. On every worker node, check how kubelet is configured:

    ps -fC kubelet
    ls -l /var/lib/kubelet/config.yaml /etc/kubernetes/kubelet-config.yaml /etc/systemd/system/kubelet.service.d

    Review the kubelet command line from ps for any --rotate-kubelet-server-certificate flag.

  2. If using the kubelet config file at /var/lib/kubelet/config.yaml, edit it to enable the feature gate (create featureGates if missing):

    sudo sed -i '/^featureGates:/,/^[^ ]/d' /var/lib/kubelet/config.yaml
    sudo bash -c 'cat <<EOF >> /var/lib/kubelet/config.yaml

featureGates: RotateKubeletServerCertificate: true EOF'

Adjust indentation if your file is using different YAML structure.

3. Ensure no systemd drop-in overrides this by forcing it to false. On every worker node, edit `/etc/systemd/system/kubelet.service.d/10-kubelet-args.conf` and remove any `--rotate-kubelet-server-certificate=false` from `KUBELET_ARGS`, or change it to true:
```bash
sudo sed -i 's/--rotate-kubelet-server-certificate=false/--rotate-kubelet-server-certificate=true/g' \
/etc/systemd/system/kubelet.service.d/10-kubelet-args.conf
  1. If the kubelet is configured only via executable arguments and not via the config file, on every worker node append the flag to KUBELET_ARGS in /etc/systemd/system/kubelet.service.d/10-kubelet-args.conf:

    sudo sed -i 's/^KUBELET_ARGS="/KUBELET_ARGS="--rotate-kubelet-server-certificate=true /' \
    /etc/systemd/system/kubelet.service.d/10-kubelet-args.conf

    Or, if KUBELET_ARGS does not exist, add a new line:

    echo 'KUBELET_ARGS="--rotate-kubelet-server-certificate=true"' | \
    sudo tee -a /etc/systemd/system/kubelet.service.d/10-kubelet-args.conf
  2. Restart kubelet on every worker node so changes take effect (this restarts the kubelet process):

    sudo systemctl daemon-reload
    sudo systemctl restart kubelet.service
    sudo systemctl status kubelet -l
  3. Verify on every worker node that kubelet is running with server certificate rotation enabled:

    /bin/ps -fC kubelet

    Confirm either the flag --rotate-kubelet-server-certificate=true is present and not set to false, and/or that the kubelet config file in use contains RotateKubeletServerCertificate: true in featureGates.

Using kubectl

kubectl cannot modify kubelet’s host-level configuration or systemd unit files, so this finding cannot be fixed via the Kubernetes API. To remediate, you must change the kubelet config and/or systemd unit on every worker node (as described in the Manual Steps section) and then restart the kubelet service.

Automation
#!/usr/bin/env bash
#
# Enable kubelet server certificate rotation on all worker nodes.
# Run this script on each worker node as root.
#
# Idempotent:
# - Safely updates /var/lib/kubelet/config.yaml featureGates
# - Ensures no conflicting --rotate-kubelet-server-certificate=false flag
# - Restarts kubelet only when needed
#
# Requirements: bash, systemd, kubelet using /var/lib/kubelet/config.yaml

set -euo pipefail

KUBELET_CONFIG="/var/lib/kubelet/config.yaml"
KUBELET_DROPIN_DIR="/etc/systemd/system/kubelet.service.d"
KUBELET_DROPIN_FILE="${KUBELET_DROPIN_DIR}/10-kubelet-args.conf"
NEED_RESTART=0

echo "[INFO] Running on host: $(hostname)"

#-----------------------------
# Helper: ensure featureGates map exists and set RotateKubeletServerCertificate: true
#-----------------------------
ensure_feature_gate() {
local cfg="${1}"

if [[ ! -f "${cfg}" ]]; then
echo "[ERROR] Kubelet config file not found at ${cfg}. Aborting."
exit 1
fi

# Create a backup once per run
if [[ ! -f "${cfg}.bak.cis-rotate-kubelet-server-cert" ]]; then
cp -p "${cfg}" "${cfg}.bak.cis-rotate-kubelet-server-cert"
echo "[INFO] Backup created at ${cfg}.bak.cis-rotate-kubelet-server-cert"
fi

# If the exact line exists and is already true, assume it's correctly set
if grep -Eq '^[[:space:]]*RotateKubeletServerCertificate:[[:space:]]*true[[:space:]]*$' "${cfg}"; then
echo "[INFO] RotateKubeletServerCertificate already set to true in ${cfg}"
return
fi

# If featureGates block exists, update or add key
if grep -q '^[[:space:]]*featureGates:' "${cfg}"; then
# If key exists with false, flip to true
if grep -Eq '^[[:space:]]*RotateKubeletServerCertificate:' "${cfg}"; then
echo "[INFO] Updating existing RotateKubeletServerCertificate in featureGates to true"
# Replace only in the featureGates section
perl -0pi -e '
s/(featureGates:[^\n]*\n(?:[ \t].*\n)*)/do {
my $blk = $1;
$blk =~ s/^([ \t]*RotateKubeletServerCertificate:).*/$1 true/m;
$blk;
}/e
' "${cfg}"
else
# Insert key into existing featureGates map (2 spaces indent)
echo "[INFO] Adding RotateKubeletServerCertificate: true to existing featureGates"
perl -0pi -e '
s/(^[ \t]*featureGates:[^\n]*\n)/$1 RotateKubeletServerCertificate: true\n/m
' "${cfg}"
fi
else
# No featureGates: append a minimal block
echo "[INFO] Adding featureGates section with RotateKubeletServerCertificate: true"
cat <<'EOF' >> "${cfg}"

featureGates:
RotateKubeletServerCertificate: true
EOF
fi

NEED_RESTART=1
}

#-----------------------------
# Helper: adjust systemd kubelet args drop-in
# - Ensure no explicit false flag
# - Optionally ensure true flag if using arg-based config
#-----------------------------
ensure_systemd_args() {
local dropin="${KUBELET_DROPIN_FILE}"

if [[ ! -d "${KUBELET_DROPIN_DIR}" ]]; then
echo "[INFO] Kubelet systemd drop-in dir not found at ${KUBELET_DROPIN_DIR}, skipping arg checks."
return
fi

if [[ -f "${dropin}" ]]; then
if [[ ! -f "${dropin}.bak.cis-rotate-kubelet-server-cert" ]]; then
cp -p "${dropin}" "${dropin}.bak.cis-rotate-kubelet-server-cert"
echo "[INFO] Backup created at ${dropin}.bak.cis-rotate-kubelet-server-cert"
fi

# Remove any explicit --rotate-kubelet-server-certificate=false
if grep -q -- '--rotate-kubelet-server-certificate=false' "${dropin}"; then
echo "[INFO] Removing --rotate-kubelet-server-certificate=false from ${dropin}"
sed -i 's/--rotate-kubelet-server-certificate=false//g' "${dropin}"
NEED_RESTART=1
fi

# If args-based config is used, ensure true flag exists.
# We add it only if no flag is present at all.
if ! grep -q -- '--rotate-kubelet-server-certificate=' "${dropin}"; then
if grep -q 'KUBELET_ARGS' "${dropin}"; then
echo "[INFO] Adding --rotate-kubelet-server-certificate=true to KUBELET_ARGS in ${dropin}"
# Append to the end of KUBELET_ARGS line
sed -i 's/^\(.*KUBELET_ARGS=.*\)"$/\1 --rotate-kubelet-server-certificate=true"/' "${dropin}" || true
NEED_RESTART=1
else
echo "[INFO] No KUBELET_ARGS found in ${dropin}; not adding flag."
fi
else
echo "[INFO] A --rotate-kubelet-server-certificate flag already exists in ${dropin}; not modifying it (except removing false)."
fi
else
echo "[INFO] ${dropin} not found; kubelet may be managed differently. Skipping arg-based remediation."
fi
}

#-----------------------------
# Apply config file remediation (Method 1)
#-----------------------------
ensure_feature_gate "${KUBELET_CONFIG}"

#-----------------------------
# Ensure no conflicting systemd arg, and add true if using args (Method 2)
#-----------------------------
ensure_systemd_args

#-----------------------------
# Restart kubelet if needed
#-----------------------------
if [[ "${NEED_RESTART}" -eq 1 ]]; then
echo "[INFO] Reloading systemd and restarting kubelet"
systemctl daemon-reload
systemctl restart kubelet.service
else
echo "[INFO] No changes made; kubelet restart not required"
fi

echo "[INFO] Checking kubelet status"
systemctl status kubelet -l --no-pager || {
echo "[ERROR] kubelet is not healthy after configuration changes"
exit 1
}

#-----------------------------
# Verification: confirm RotateKubeletServerCertificate is enabled
# Prefer local config file; optionally cross-check with process flags.
#-----------------------------
echo "[INFO] Verifying kubelet configuration"

if grep -Eq '^[[:space:]]*RotateKubeletServerCertificate:[[:space:]]*true[[:space:]]*$' "${KUBELET_CONFIG}"; then
echo "[OK] ${KUBELET_CONFIG} has RotateKubeletServerCertificate: true"
else
echo "[WARN] ${KUBELET_CONFIG} does not clearly show RotateKubeletServerCertificate: true"
fi

echo "[INFO] Current kubelet process flags:"
/bin/ps -fC kubelet || true

echo "[INFO] Verification complete on host: $(hostname)"

Additional Reading: