Skip to main content

Ensure Client Ca File Argument Is Appropriate

More Info:

Setup TLS connection on the API server.

Risk Level

Low

Address

Security

Compliance Standards

  • CIS Kubernetes

Triage and Remediation

Remediation

Manual Steps
  1. On every control plane node, confirm the current kube-apiserver options and identify whether --client-ca-file is present and what it is set to:

    ps -ef | grep kube-apiserver | grep -v grep

    If --client-ca-file is missing or incorrect, proceed.

  2. On the same control plane node, ensure you have (or create) a valid client CA file that will sign client certificates used to authenticate to the API server, for example:

    ls -l /etc/kubernetes/pki/ca.crt

    If you need a dedicated client CA, place it in a secure path such as /etc/kubernetes/pki/client-ca.crt and ensure it is readable by the kube-apiserver process:

    chown root:root /etc/kubernetes/pki/client-ca.crt
    chmod 600 /etc/kubernetes/pki/client-ca.crt
  3. Edit the kube-apiserver static pod manifest on the control plane node:

    sudo vi /etc/kubernetes/manifests/kube-apiserver.yaml

    In the spec.containers[0].command list, add or correct the flag so it points to your chosen client CA file, for example:

    - --client-ca-file=/etc/kubernetes/pki/client-ca.crt

    Save and exit. Editing this file will automatically restart the kube-apiserver static pod.

  4. Still on the control plane node, ensure the path in --client-ca-file matches an existing file and that the directory is not writable by non-privileged users:

    ls -l /etc/kubernetes/pki/client-ca.crt
    ls -ld /etc/kubernetes/pki
  5. Wait for the kube-apiserver static pod to restart and become Ready:

    crictl ps | grep kube-apiserver || docker ps | grep kube-apiserver
  6. Verification (on every control plane node): confirm the running kube-apiserver process includes the correct --client-ca-file argument and path:

    ps -ef | grep kube-apiserver | grep -v grep | grep -- '--client-ca-file='

    Ensure the printed value matches the intended client CA file path.

Using kubectl

kubectl cannot modify the kube-apiserver static pod manifest or its process flags, including --client-ca-file, because these are defined in /etc/kubernetes/manifests/kube-apiserver.yaml on each control plane node. To remediate this finding, make the changes directly on the node as described in the Manual Steps section.

Automation
#!/usr/bin/env bash
#
# Automation: Ensure kube-apiserver is started with a valid --client-ca-file
#
# Run on: every control plane node (with root privileges)
#
# Requirements:
# - bash
# - yq (v4) for safe YAML edits: https://github.com/mikefarah/yq
#
# Behavior:
# - Ensures /etc/kubernetes/pki/client-ca.crt exists (or create from cluster CA if appropriate)
# - Ensures kube-apiserver manifest has --client-ca-file set to that path
# - Relies on kubelet to restart kube-apiserver static pod
# - Idempotent: safe to re-run

set -euo pipefail

MANIFEST="/etc/kubernetes/manifests/kube-apiserver.yaml"
CLIENT_CA_DEFAULT="/etc/kubernetes/pki/client-ca.crt"

echo "[INFO] Starting remediation for --client-ca-file on kube-apiserver"

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

if ! command -v yq >/dev/null 2>&1; then
echo "[ERROR] yq (v4) is required but not installed. Install yq and re-run." >&2
exit 1
fi

if [[ ! -f "$MANIFEST" ]]; then
echo "[ERROR] kube-apiserver manifest not found at $MANIFEST" >&2
exit 1
fi

backup_manifest() {
local ts
ts="$(date +%Y%m%d-%H%M%S)"
local backup="${MANIFEST}.${ts}.bak"
cp -p "$MANIFEST" "$backup"
echo "[INFO] Backup created at $backup"
}

ensure_client_ca_file() {
local ca_path="$1"

if [[ -f "$ca_path" ]]; then
echo "[INFO] Client CA file already exists at $ca_path"
return 0
fi

# Heuristic: if no dedicated client CA exists, reuse the main cluster CA if present.
# Adjust according to your security design before using in production.
local cluster_ca="/etc/kubernetes/pki/ca.crt"

if [[ -f "$cluster_ca" ]]; then
echo "[WARN] $ca_path not found; copying $cluster_ca as client CA. Review if this matches your design."
cp -p "$cluster_ca" "$ca_path"
return 0
fi

echo "[ERROR] Neither $ca_path nor $cluster_ca exist. Create an appropriate client CA certificate and place it at $ca_path, then re-run."
exit 1
}

set_client_ca_arg() {
local ca_path="$1"

# Detect if --client-ca-file already present in the manifest
if grep -q -- "--client-ca-file=" "$MANIFEST"; then
local current
current="$(grep -oE -- '--client-ca-file=[^[:space:]]+' "$MANIFEST" | head -n1 | cut -d= -f2 || true)"
if [[ "$current" == "$ca_path" ]]; then
echo "[INFO] --client-ca-file already set correctly to $ca_path in manifest"
return 0
fi
echo "[INFO] Updating existing --client-ca-file from $current to $ca_path in manifest"
backup_manifest
# Replace only the first occurrence
sed -i "0,/--client-ca-file=.*$/s//--client-ca-file=${ca_path}/" "$MANIFEST"
else
echo "[INFO] Adding --client-ca-file=${ca_path} to kube-apiserver manifest"
backup_manifest

# Use yq to append the argument to the containers[0].command list
yq -i '
.spec.containers[0].command += ["--client-ca-file='"$ca_path"'"]
' "$MANIFEST"
fi
}

verify_running_process() {
local ca_path="$1"

echo "[INFO] Waiting for kube-apiserver static pod restart (up to 120s)..."
# Wait up to 120 seconds for the process to reflect the new flag
local timeout=120
local elapsed=0
while (( elapsed < timeout )); do
if ps -ef | grep kube-apiserver | grep -v grep | grep -q -- "--client-ca-file=${ca_path}"; then
echo "[INFO] Verification succeeded: kube-apiserver running with --client-ca-file=${ca_path}"
return 0
fi
sleep 5
elapsed=$((elapsed + 5))
done

echo "[ERROR] Verification failed: kube-apiserver process does not show --client-ca-file=${ca_path} after ${timeout}s." >&2
echo "[INFO] Current kube-apiserver processes:"
ps -ef | grep kube-apiserver | grep -v grep || true
exit 1
}

main() {
local ca_path="$CLIENT_CA_DEFAULT"

echo "[INFO] Ensuring client CA file exists at $ca_path"
ensure_client_ca_file "$ca_path"

echo "[INFO] Ensuring kube-apiserver manifest has --client-ca-file=${ca_path}"
set_client_ca_arg "$ca_path"

echo "[INFO] Verifying running kube-apiserver process uses --client-ca-file=${ca_path}"
verify_running_process "$ca_path"

echo "[INFO] Remediation complete on this control plane node."
}

main "$@"

Additional Reading: