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

# API Server Should Set Kubelet Certificate Authority

### More Info:

Verifies that --kubelet-certificate-authority is set so the API server verifies kubelet certificates, preventing man-in-the-middle attacks against the kubelet connection.

### Risk Level

High

### 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, confirm the current API server flags and locate the manifest:

        ```bash theme={null}
        ps -ef | grep kube-apiserver | grep -v grep
        ls -l /etc/kubernetes/manifests/kube-apiserver.yaml
        ```

        2. Ensure you have or generate the CA certificate that issued the kubelet client certificates (example path used below). Copy it to the control plane node if needed and place it somewhere readable by the kube-apiserver, for example:

        ```bash theme={null}
        sudo mkdir -p /etc/kubernetes/pki
        sudo cp /path/to/your/kubelet-ca.crt /etc/kubernetes/pki/kubelet-ca.crt
        sudo chmod 600 /etc/kubernetes/pki/kubelet-ca.crt
        sudo chown root:root /etc/kubernetes/pki/kubelet-ca.crt
        ```

        3. On every control plane node, edit the API server static pod manifest:

        ```bash theme={null}
        sudo vi /etc/kubernetes/manifests/kube-apiserver.yaml
        ```

        Under the `command:` section (or `- --` arguments list), add or modify the flag so it reads:

        ```yaml theme={null}
            - --kubelet-certificate-authority=/etc/kubernetes/pki/kubelet-ca.crt
        ```

        Ensure there is only one `--kubelet-certificate-authority` entry.

        4. Save the file and exit. The kubelet will automatically restart the `kube-apiserver` static pod when the manifest changes. Monitor the restart and confirm the pod is running:

        ```bash theme={null}
        sudo crictl ps | grep kube-apiserver || sudo docker ps | grep kube-apiserver
        ```

        5. Check the API server logs for successful startup and no certificate validation errors toward kubelets:

        ```bash theme={null}
        sudo journalctl -u kubelet -f
        # in another terminal
        sudo crictl logs $(sudo crictl ps | awk '/kube-apiserver/ {print $1}') 2>&1 | grep -i kubelet
        ```

        6. Verify that the `--kubelet-certificate-authority` flag is now set correctly on every control plane node:

        ```bash theme={null}
        ps -ef | grep kube-apiserver | grep -v grep | grep -- '--kubelet-certificate-authority='
        ```
      </Accordion>

      <Accordion title="Using kubectl">
        kubectl cannot modify the kube-apiserver static pod manifest or its process flags, so it cannot be used to set `--kubelet-certificate-authority`. This must be fixed directly on every control plane node by editing `/etc/kubernetes/manifests/kube-apiserver.yaml`; see the Manual Steps section for the required procedure.
      </Accordion>

      <Accordion title="Automation">
        ```bash theme={null}
        #!/usr/bin/env bash
        #
        # Automation: Ensure kube-apiserver sets --kubelet-certificate-authority
        # Scope: run on every control plane node
        # Prereqs:
        #   - kube-apiserver is a static pod from /etc/kubernetes/manifests/kube-apiserver.yaml
        #   - A valid CA file exists at /etc/kubernetes/pki/kubelet-ca.crt
        #
        # Idempotent: safe to re-run.

        set -euo pipefail

        APISERVER_MANIFEST="/etc/kubernetes/manifests/kube-apiserver.yaml"
        KUBELET_CA="/etc/kubernetes/pki/kubelet-ca.crt"
        BACKUP_DIR="/etc/kubernetes/manifests/backup-$(date +%Y%m%d)"

        if [[ $EUID -ne 0 ]]; then
          echo "ERROR: Run this script as root on each control plane node." >&2
          exit 1
        fi

        if [[ ! -f "$APISERVER_MANIFEST" ]]; then
          echo "ERROR: $APISERVER_MANIFEST not found on this node; nothing to do." >&2
          exit 1
        fi

        if [[ ! -f "$KUBELET_CA" ]]; then
          echo "ERROR: Expected CA file $KUBELET_CA not found. Create or place the kubelet CA there, then re-run." >&2
          exit 1
        fi

        mkdir -p "$BACKUP_DIR"
        cp -n "$APISERVER_MANIFEST" "$BACKUP_DIR/kube-apiserver.yaml.$(date +%H%M%S)"

        # Detect if the flag is already set correctly
        if grep -q -- "--kubelet-certificate-authority=${KUBELET_CA}" "$APISERVER_MANIFEST"; then
          echo "INFO: --kubelet-certificate-authority already set to ${KUBELET_CA} in manifest. Skipping edit."
        else
          echo "INFO: Updating $APISERVER_MANIFEST with --kubelet-certificate-authority=${KUBELET_CA}"

          # Remove any existing --kubelet-certificate-authority flags (with any value)
          sed -i '/--kubelet-certificate-authority=/d' "$APISERVER_MANIFEST"

          # Insert the flag under the existing --kubelet-client-certificate arg if present,
          # otherwise append it at the end of the arguments list.
          if grep -q -- "--kubelet-client-certificate" "$APISERVER_MANIFEST"; then
            # Insert on the next line after kubelet-client-certificate
            awk -v flag="    - --kubelet-certificate-authority=${KUBELET_CA}" '
              /--kubelet-client-certificate/ && !inserted {
                print $0
                print flag
                inserted=1
                next
              }
              { print $0 }
            ' "$APISERVER_MANIFEST" > "${APISERVER_MANIFEST}.tmp"
            mv "${APISERVER_MANIFEST}.tmp" "$APISERVER_MANIFEST"
          else
            # Append near the end of the args list
            awk -v flag="    - --kubelet-certificate-authority=${KUBELET_CA}" '
              /- --/ && inargs==0 { inargs=1 }    # first arg line
              inargs && $0 ~ /^ *-/ { lastarg=NR } # track last arg line
              { lines[NR]=$0 }
              END {
                for (i=1; i<=NR; i++) {
                  print lines[i]
                  if (i==lastarg) print flag
                }
              }
            ' "$APISERVER_MANIFEST" > "${APISERVER_MANIFEST}.tmp"
            mv "${APISERVER_MANIFEST}.tmp" "$APISERVER_MANIFEST"
          fi

          echo "INFO: Manifest updated. kube-apiserver static pod will be restarted by kubelet."
        fi

        echo "INFO: Waiting for kube-apiserver process to reflect new flag..."
        sleep 20

        # Verification: match the audit command and confirm the flag is present
        if /bin/ps -ef | grep kube-apiserver | grep -v grep | grep -q -- "--kubelet-certificate-authority=${KUBELET_CA}"; then
          echo "SUCCESS: kube-apiserver is running with --kubelet-certificate-authority=${KUBELET_CA}"
          exit 0
        else
          echo "ERROR: kube-apiserver process does not show --kubelet-certificate-authority=${KUBELET_CA} yet." >&2
          echo "Current kube-apiserver command line:"
          /bin/ps -ef | grep kube-apiserver | grep -v grep || true
          exit 1
        fi
        ```
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
