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

### More Info:

Enable kubelet server certificate rotation on controller-manager.

### 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 control plane node, open the kube-controller-manager static pod manifest for editing:
           ```bash theme={null}
           sudo vi /etc/kubernetes/manifests/kube-controller-manager.yaml
           ```

        2. In the `spec.containers[0].command` list, ensure there is a `--feature-gates=` entry that includes `RotateKubeletServerCertificate=true`.
           * If `--feature-gates` does not exist, add a new line under the other `--` flags, for example:
             ```yaml theme={null}
             - --feature-gates=RotateKubeletServerCertificate=true
             ```
           * If `--feature-gates` already exists, append `RotateKubeletServerCertificate=true` to the comma-separated list, for example:
             ```yaml theme={null}
             - --feature-gates=SomeOtherFeature=true,RotateKubeletServerCertificate=true
             ```

        3. Save the file and exit the editor. The kube-controller-manager static pod will be automatically restarted by the kubelet on that control plane node due to the manifest change.

        4. Wait for the kube-controller-manager pod to restart and become Running (from any machine with kubectl access):
           ```bash theme={null}
           kubectl -n kube-system get pods -l component=kube-controller-manager -w
           ```
           Press `Ctrl+C` once the relevant pod is in `Running` state and no longer restarting.

        5. On each control plane node, verify the kube-controller-manager process includes the correct feature gate:
           ```bash theme={null}
           /bin/ps -ef | grep kube-controller-manager | grep -v grep
           ```
           Confirm the output contains a `--feature-gates=` argument with `RotateKubeletServerCertificate=true` present in its value.
      </Accordion>

      <Accordion title="Using kubectl">
        kubectl cannot modify the kube-controller-manager static pod manifest or its process flags on the control plane node. To enable `RotateKubeletServerCertificate`, you must edit `/etc/kubernetes/manifests/kube-controller-manager.yaml` directly on every control plane node; follow the guidance in the Manual Steps section.
      </Accordion>

      <Accordion title="Automation">
        ```bash theme={null}
        #!/usr/bin/env bash
        #
        # Automation: Enable RotateKubeletServerCertificate feature-gate on kube-controller-manager
        # Scope: Run on every control plane node with direct access to /etc/kubernetes/manifests
        # Usage: sudo ./enable-rotate-kubelet-server-cert.sh

        set -euo pipefail

        MANIFEST="/etc/kubernetes/manifests/kube-controller-manager.yaml"
        BACKUP_DIR="/etc/kubernetes/manifests/backup-rotate-kubelet-server-cert"
        TIMESTAMP="$(date +%Y%m%d%H%M%S)"

        if [[ $EUID -ne 0 ]]; then
          echo "ERROR: This script must be run as root (or with sudo)." >&2
          exit 1
        fi

        if [[ ! -f "$MANIFEST" ]]; then
          echo "ERROR: Manifest not found at $MANIFEST. Are you on a control plane node?" >&2
          exit 1
        fi

        mkdir -p "$BACKUP_DIR"

        backup_file="$BACKUP_DIR/kube-controller-manager.yaml.$TIMESTAMP"
        cp -p "$MANIFEST" "$backup_file"
        echo "Backup created at: $backup_file"

        # Normalize feature-gates lines and ensure RotateKubeletServerCertificate=true is present
        tmpfile="$(mktemp)"
        trap 'rm -f "$tmpfile"' EXIT

        # Idempotent edit:
        # - If a --feature-gates arg exists:
        #     * If it already includes RotateKubeletServerCertificate=true: leave as-is
        #     * If it includes RotateKubeletServerCertificate=false: change to true
        #     * Else append ,RotateKubeletServerCertificate=true
        # - If no --feature-gates exists: add a new - --feature-gates=RotateKubeletServerCertificate=true
        python3 - "$MANIFEST" > "$tmpfile" << 'PYEOF'
        import sys, re

        path = sys.argv[1]
        with open(path, "r", encoding="utf-8") as f:
            lines = f.readlines()

        out = []
        feature_gates_key = "- --feature-gates="
        found_fg = False

        for i, line in enumerate(lines):
            stripped = line.lstrip()
            if stripped.startswith(feature_gates_key):
                found_fg = True
                indent = line[:len(line) - len(stripped)]
                value = stripped[len(feature_gates_key):].rstrip("\n")

                # Ensure RotateKubeletServerCertificate is set to true
                # Remove any existing RotateKubeletServerCertificate=<...> and append =true
                parts = [p for p in value.split(",") if not p.startswith("RotateKubeletServerCertificate=")]
                parts.append("RotateKubeletServerCertificate=true")
                # Deduplicate while preserving order
                seen = set()
                new_parts = []
                for p in parts:
                    if p not in seen:
                        seen.add(p)
                        new_parts.append(p)
                new_value = ",".join(new_parts)
                out.append(f"{indent}{feature_gates_key}{new_value}\n")
            else:
                out.append(line)

        if not found_fg:
            # Insert new feature-gates arg under the kube-controller-manager container args
            inserted = False
            for i, line in enumerate(out):
                if re.match(r'^\s*name:\s*kube-controller-manager\s*$', line):
                    # Look for the args: block following this
                    j = i + 1
                    while j < len(out) and not re.match(r'^\s*args:\s*$', out[j]):
                        j += 1
                    if j < len(out):
                        indent = re.match(r'^(\s*)args:\s*$', out[j]).group(1) + "  "
                        out.insert(j + 1, f"{indent}- --feature-gates=RotateKubeletServerCertificate=true\n")
                        inserted = True
                        break
            if not inserted:
                # Fallback: append at end, minimally valid for static pod
                out.append("  - --feature-gates=RotateKubeletServerCertificate=true\n")

        sys.stdout.writelines(out)
        PYEOF

        # Only overwrite if there is a change
        if cmp -s "$MANIFEST" "$tmpfile"; then
          echo "No changes needed; RotateKubeletServerCertificate=true already configured."
        else
          mv "$tmpfile" "$MANIFEST"
          sync
          echo "Updated $MANIFEST with RotateKubeletServerCertificate=true in --feature-gates."
        fi

        # Note: kubelet will automatically restart the static pod after manifest change.

        echo "Waiting 30 seconds for kube-controller-manager to restart..."
        sleep 30

        echo "Verification: checking running kube-controller-manager arguments..."
        if /bin/ps -ef | grep kube-controller-manager | grep -v grep | grep -q -- '--feature-gates=.*RotateKubeletServerCertificate=true'; then
          echo "SUCCESS: kube-controller-manager is running with RotateKubeletServerCertificate=true enabled."
          exit 0
        else
          echo "WARNING: kube-controller-manager process not yet showing RotateKubeletServerCertificate=true."
          echo "Current kube-controller-manager processes:"
          /bin/ps -ef | grep kube-controller-manager | grep -v grep || true
          exit 1
        fi
        ```
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>

### Additional Reading:

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