Skip to main content

Kubelet Client CA File Argument Set As Appropriate

More Info:

The --client-ca-file enables the kubelet to authenticate client certificates against a trusted CA. Setting it ensures client certificate authentication is enforced.

Risk Level

High

Address

Security

Compliance Standards

  • CIS Kubernetes

Triage and Remediation

Remediation

Manual Steps
  1. On every control plane node, confirm the API server is running as a static pod and locate its manifest:

    ps -ef | grep kube-apiserver | grep -v grep
    ls -l /etc/kubernetes/manifests/kube-apiserver.yaml
  2. On every control plane node, identify (or create if needed) the client CA file you want the API server to trust (replace the path below if your CA is elsewhere):

    ls -l /etc/kubernetes/pki/ca.crt
  3. On every control plane node, edit the API server manifest to add or correct the --client-ca-file flag, pointing to your chosen CA file:

    sudo sed -i 's#^\(\s*-\s*--client-ca-file=\).*#\1/etc/kubernetes/pki/ca.crt#' /etc/kubernetes/manifests/kube-apiserver.yaml || \
    sudo sed -i '/- --tls-cert-file=/a\ - --client-ca-file=/etc/kubernetes/pki/ca.crt' /etc/kubernetes/manifests/kube-apiserver.yaml

    Saving this file causes the kubelet to restart the kube-apiserver static pod; expect a brief control-plane disruption while it restarts.

  4. On every control plane node, wait for the API server container to be recreated and become Ready:

    sudo crictl ps | grep kube-apiserver || sudo docker ps | grep kube-apiserver
  5. On any machine with kubectl access, confirm the API server is responding again:

    kubectl get --raw=/healthz
  6. Verification (on every control plane node): confirm the --client-ca-file argument is present and set as intended:

    ps -ef | grep kube-apiserver | grep -v grep | grep -- '--client-ca-file=/etc/kubernetes/pki/ca.crt'
Using kubectl

kubectl cannot configure the kube-apiserver process flags or edit the static pod manifest on the host. To remediate this finding, you must modify /etc/kubernetes/manifests/kube-apiserver.yaml directly on every control plane node; see the Manual Steps section for exact host-level instructions.

Automation
#!/usr/bin/env bash
#
# Automation: Ensure kube-apiserver is started with --client-ca-file on every control plane node
#
# Usage:
# 1. Run on each control plane node with sudo/root.
# 2. Adjust CA_PATH below if your client CA is at a different location.
# 3. Re‑run safely; script is idempotent.

set -euo pipefail

CA_PATH="/etc/kubernetes/pki/ca.crt"
APISERVER_MANIFEST="/etc/kubernetes/manifests/kube-apiserver.yaml"
TMP_MANIFEST="/etc/kubernetes/manifests/kube-apiserver.yaml.tmp"

echo "[*] Running on control plane node: $(hostname)"

# Basic prechecks
if [[ ! -f "${APISERVER_MANIFEST}" ]]; then
echo "ERROR: ${APISERVER_MANIFEST} not found. This script expects a static pod apiserver manifest."
exit 1
fi

if [[ ! -f "${CA_PATH}" ]]; then
echo "WARNING: Client CA file ${CA_PATH} not found."
echo "Create/populate the appropriate client CA at this path before relying on certificate authentication."
# We still proceed to set the flag to this path so it's wired consistently.
fi

echo "[*] Ensuring --client-ca-file is configured in ${APISERVER_MANIFEST}"

# Backup manifest once
BACKUP="${APISERVER_MANIFEST}.$(date +%Y%m%d%H%M%S).bak"
cp -n "${APISERVER_MANIFEST}" "${BACKUP}" || true

# Idempotently ensure the flag exists and points to CA_PATH.
# Strategy:
# - If any --client-ca-file appears, normalize it to the desired path.
# - If none appears, add it to the command line args for kube-apiserver.

if grep -q -- "--client-ca-file" "${APISERVER_MANIFEST}"; then
# Normalize existing flag(s) to the desired path
sed -E "s#(--client-ca-file=)[^[:space:]]*#\1${CA_PATH}#g" "${APISERVER_MANIFEST}" > "${TMP_MANIFEST}"
else
# Insert flag into args list. We try to add a new '- --client-ca-file=...' line
# after the first '- --secure-port=' or similar existing arg, falling back to
# appending inside the args list.
if grep -qE '^\s*- --secure-port=' "${APISERVER_MANIFEST}"; then
awk -v ca="--client-ca-file=${CA_PATH}" '
/^\s*- --secure-port=/ && !added {
print
print " - " ca
added=1
next
}
{ print }
' "${APISERVER_MANIFEST}" > "${TMP_MANIFEST}"
else
# Generic append inside args: block
awk -v ca="--client-ca-file=${CA_PATH}" '
$0 ~ /^\s*args:\s*$/ && !added {
print
getline
print
print " - " ca
added=1
next
}
{ print }
' "${APISERVER_MANIFEST}" > "${TMP_MANIFEST}"
fi
fi

mv "${TMP_MANIFEST}" "${APISERVER_MANIFEST}"

echo "[*] Updated ${APISERVER_MANIFEST}. kubelet will restart the kube-apiserver static pod automatically."
echo " NOTE: This restarts the API server on this control plane node."

# Wait briefly for kube-apiserver to restart and stabilize
sleep 15

echo "[*] Verifying kube-apiserver process has --client-ca-file=${CA_PATH}"

if /bin/ps -ef | grep kube-apiserver | grep -v grep | grep -q -- "--client-ca-file=${CA_PATH}"; then
echo "[OK] kube-apiserver is running with --client-ca-file=${CA_PATH}"
exit 0
else
echo "ERROR: kube-apiserver is not running with the expected --client-ca-file flag."
echo "Current kube-apiserver command line:"
/bin/ps -ef | grep kube-apiserver | grep -v grep || true
exit 1
fi