> ## Documentation Index
> Fetch the complete documentation index at: https://cloudanix.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Verify RotateKubeletServerCertificate Argument Is Enabled

### More Info:

Enable kubelet server certificate rotation.

### Risk Level

Low

### Address

Security

### Compliance Standards

* CIS Kubernetes

### Triage and Remediation

<Tabs>
  <Tab title="Remediation">
    ### Remediation

    <AccordionGroup>
      <Accordion title="Manual Steps" defaultOpen="true">
        1. On every worker node, open the kubelet systemd drop-in file for editing:
           ```bash theme={null}
           sudo vi /etc/systemd/system/kubelet.service.d/10-kubeadm.conf
           ```

        2. 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:
           ```ini theme={null}
           Environment="KUBELET_CERTIFICATE_ARGS=--feature-gates=RotateKubeletServerCertificate=true"
           ```
           If other feature gates are already present, include this one in the same list, for example:
           ```ini theme={null}
           Environment="KUBELET_CERTIFICATE_ARGS=--feature-gates=ExistingFeature=true,RotateKubeletServerCertificate=true"
           ```

        3. Ensure the kubelet command line in the same file actually uses `KUBELET_CERTIFICATE_ARGS` (add it if missing). The `ExecStart=` line should include:
           ```ini theme={null}
           ExecStart=... $KUBELET_CERTIFICATE_ARGS ...
           ```

        4. Reload systemd and restart kubelet on the same worker node to apply the change (this will restart the kubelet process):
           ```bash theme={null}
           sudo systemctl daemon-reload
           sudo systemctl restart kubelet.service
           ```

        5. Repeat steps 1–4 on every worker node in the cluster.

        6. Verification (on each worker node): confirm the kubelet process was started with the correct feature gate:
           ```bash theme={null}
           /bin/ps -fC kubelet
           ```
           Check that the output command line includes:\
           `--feature-gates=RotateKubeletServerCertificate=true` (or within a comma-separated `--feature-gates=` list).
      </Accordion>

      <Accordion title="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.
      </Accordion>

      <Accordion title="Automation">
        ```bash theme={null}
        #!/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
        ```
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>

### Additional Reading:

* [https://kubernetes.io/docs/admin/kubelet-tls-bootstrapping/#kubelet-configuration](https://kubernetes.io/docs/admin/kubelet-tls-bootstrapping/#kubelet-configuration)
