> ## 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 Client CA File

### More Info:

Verifies that --client-ca-file is set so the API server can authenticate clients using certificates signed by the configured certificate authority.

### 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. **Prepare or identify the client CA file** (every control plane node)
           * Ensure you have a PEM‑encoded CA certificate that signs client certs for kube-apiserver auth, e.g.:\
             `/etc/kubernetes/pki/client-ca.crt`
           * If it does not exist yet, copy or create the appropriate CA file on each control plane node at that path with root ownership and restricted permissions:
             ```bash theme={null}
             sudo install -o root -g root -m 600 client-ca.crt /etc/kubernetes/pki/client-ca.crt
             ```

        2. **Edit the kube-apiserver static pod manifest** (every control plane node)
           * Open the manifest in an editor:
             ```bash theme={null}
             sudo vi /etc/kubernetes/manifests/kube-apiserver.yaml
             ```
           * In the `spec.containers[0].command` list, add or update the argument so it includes:
             ```yaml theme={null}
             - --client-ca-file=/etc/kubernetes/pki/client-ca.crt
             ```
           * Save and exit.\
             **Operational impact:** because this is a static pod manifest, the kubelet will automatically restart the API server container when the file is saved.

        3. **Ensure the CA file is mounted into the pod (if needed)** (every control plane node)
           * In the same file `/etc/kubernetes/manifests/kube-apiserver.yaml`, confirm there is a `volumeMounts` entry for the directory containing the CA file, for example:
             ```yaml theme={null}
             volumeMounts:
               - mountPath: /etc/kubernetes/pki
                 name: k8s-certs
                 readOnly: true
             ```
           * And a corresponding `volumes` entry:
             ```yaml theme={null}
             volumes:
               - name: k8s-certs
                 hostPath:
                   path: /etc/kubernetes/pki
                   type: DirectoryOrCreate
             ```
           * Adjust the `mountPath`, `name`, and `path` only if your existing manifest uses different values.

        4. **Wait for kube-apiserver to restart and stabilize** (every control plane node)
           * Monitor the pod restart (from any machine with `kubectl` access):
             ```bash theme={null}
             kubectl -n kube-system get pods -l component=kube-apiserver -w
             ```
           * Wait until the kube-apiserver pod is in `Running` and `Ready` state.

        5. **Verify the kube-apiserver process has the client-ca-file flag set** (every control plane node)
           * Run:
             ```bash theme={null}
             /bin/ps -ef | grep kube-apiserver | grep -v grep
             ```
           * Confirm the output includes the argument with the correct path, for example:
             ```
             --client-ca-file=/etc/kubernetes/pki/client-ca.crt
             ```
      </Accordion>

      <Accordion title="Using kubectl">
        kubectl cannot modify the API server’s host-level configuration or its static pod manifest. To set `--client-ca-file`, you must edit `/etc/kubernetes/manifests/kube-apiserver.yaml` directly on every control plane node; see the Manual Steps section for the exact procedure.
      </Accordion>

      <Accordion title="Automation">
        ```bash theme={null}
        #!/usr/bin/env bash
        #
        # Automation: Ensure kube-apiserver is started with --client-ca-file on all control plane nodes.
        #
        # Usage:
        #   1) Create /etc/kubernetes/pki/client-ca.crt on each control plane node with the desired CA.
        #   2) From an admin machine with SSH access, run:
        #        ./fix-apiserver-client-ca.sh "node1 node2 node3"
        #   3) Script is idempotent and safe to re-run.
        #

        set -euo pipefail

        CONTROL_PLANE_NODES="${1:-}"
        CLIENT_CA_FILE="/etc/kubernetes/pki/client-ca.crt"
        APISERVER_MANIFEST="/etc/kubernetes/manifests/kube-apiserver.yaml"
        SSH_USER="root"   # change if your SSH user is different

        if [[ -z "${CONTROL_PLANE_NODES}" ]]; then
          echo "ERROR: Provide a space-separated list of control plane nodes as the first argument."
          echo "Example: $0 \"cp-1 cp-2 cp-3\""
          exit 1
        fi

        for NODE in ${CONTROL_PLANE_NODES}; do
          echo "=== Processing control plane node: ${NODE} ==="

          ssh -o StrictHostKeyChecking=no -o BatchMode=yes "${SSH_USER}@${NODE}" bash -s <<'EOF'
        set -euo pipefail

        CLIENT_CA_FILE="/etc/kubernetes/pki/client-ca.crt"
        APISERVER_MANIFEST="/etc/kubernetes/manifests/kube-apiserver.yaml"
        BACKUP_SUFFIX="$(date +%Y%m%d%H%M%S)"

        echo "[${HOSTNAME}] Verifying prerequisites..."

        if [[ ! -f "${CLIENT_CA_FILE}" ]]; then
          echo "[${HOSTNAME}] ERROR: Client CA file ${CLIENT_CA_FILE} does not exist."
          echo "[${HOSTNAME}] Create this file (per your PKI policy) before running this script."
          exit 1
        fi

        if [[ ! -f "${APISERVER_MANIFEST}" ]]; then
          echo "[${HOSTNAME}] ERROR: API server manifest ${APISERVER_MANIFEST} not found."
          exit 1
        fi

        echo "[${HOSTNAME}] Checking if --client-ca-file is already set correctly..."

        if grep -q -- "--client-ca-file=${CLIENT_CA_FILE}" "${APISERVER_MANIFEST}"; then
          echo "[${HOSTNAME}] Correct --client-ca-file already present. No change needed."
        else
          if grep -q -- "--client-ca-file=" "${APISERVER_MANIFEST}"; then
            echo "[${HOSTNAME}] Updating existing --client-ca-file argument..."
          else
            echo "[${HOSTNAME}] Adding new --client-ca-file argument..."
          fi

          cp "${APISERVER_MANIFEST}" "${APISERVER_MANIFEST}.bak-${BACKUP_SUFFIX}"

          # Replace existing --client-ca-file or append a new one under the command section.
          if grep -q -- "--client-ca-file=" "${APISERVER_MANIFEST}"; then
            # Replace only the value, keep YAML structure intact.
            sed -i "s#--client-ca-file=.*#--client-ca-file=${CLIENT_CA_FILE}#g" "${APISERVER_MANIFEST}"
          else
            # Append under the 'command:' list for kube-apiserver container.
            # This assumes a standard kubeadm-generated manifest layout.
            awk -v ca_arg="    - --client-ca-file=${CLIENT_CA_FILE}" '
              /- kube-apiserver$/ && in_container==0 { in_container=1 }
              in_container==1 && /command:/ && cmd_seen==0 { cmd_seen=1 }
              in_container==1 && cmd_seen==1 && /\- --/ && !inserted {
                print $0
                print ca_arg
                inserted=1
                next
              }
              { print $0 }
              END {
                if (inserted!=1) {
                  # Fallback: if we didn’t find a good place, just append at end of file
                  print ca_arg
                }
              }
            ' "${APISERVER_MANIFEST}" > "${APISERVER_MANIFEST}.tmp"

            mv "${APISERVER_MANIFEST}.tmp" "${APISERVER_MANIFEST}"
          fi

          echo "[${HOSTNAME}] Updated ${APISERVER_MANIFEST}."
          echo "[${HOSTNAME}] kubelet will automatically restart the kube-apiserver static pod."
        fi

        echo "[${HOSTNAME}] Waiting for kube-apiserver process to reflect the new flag..."

        # Wait up to 120s for the process to restart with the desired flag
        timeout=120
        interval=5
        elapsed=0
        while true; do
          if /bin/ps -ef | grep kube-apiserver | grep -v grep | grep -q -- "--client-ca-file=${CLIENT_CA_FILE}"; then
            echo "[${HOSTNAME}] Verification SUCCESS: kube-apiserver running with --client-ca-file=${CLIENT_CA_FILE}."
            break
          fi
          if (( elapsed >= timeout )); then
            echo "[${HOSTNAME}] WARNING: kube-apiserver process did not show --client-ca-file=${CLIENT_CA_FILE} within ${timeout}s."
            echo "[${HOSTNAME}] Current kube-apiserver processes:"
            /bin/ps -ef | grep kube-apiserver | grep -v grep || true
            exit 1
          fi
          sleep "${interval}"
          elapsed=$((elapsed + interval))
        done

        EOF

          echo "=== Completed node: ${NODE} ==="
          echo
        done

        echo "All specified control plane nodes processed."
        ```
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>
