> ## 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.

# Ensure 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

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

    <AccordionGroup>
      <Accordion title="Manual Steps" defaultOpen="true">
        1. On every worker node, check how kubelet is configured:
           ```bash theme={null}
           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):
           ```bash theme={null}
           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
        ````

        4. 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`:
           ```bash theme={null}
           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:
           ```bash theme={null}
           echo 'KUBELET_ARGS="--rotate-kubelet-server-certificate=true"' | \
             sudo tee -a /etc/systemd/system/kubelet.service.d/10-kubelet-args.conf
           ```

        5. Restart kubelet on every worker node so changes take effect (this restarts the kubelet process):
           ```bash theme={null}
           sudo systemctl daemon-reload
           sudo systemctl restart kubelet.service
           sudo systemctl status kubelet -l
           ```

        6. Verify on every worker node that kubelet is running with server certificate rotation enabled:
           ```bash theme={null}
           /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`.
      </Accordion>

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

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