Skip to main content

Ensure Root CA File Argument Is Appropriate

More Info:

Allow pods to verify the API servers serving certificate before establishing connections.

Risk Level

Low

Address

Security

Compliance Standards

  • CIS Kubernetes

Triage and Remediation

Remediation

Manual Steps
  1. On every control plane node, back up the existing manifest:

    sudo cp -a /etc/kubernetes/manifests/kube-controller-manager.yaml /etc/kubernetes/manifests/kube-controller-manager.yaml.bak
  2. On the same node, identify the appropriate root CA bundle file present on the host (coordinate with your PKI/security team if unsure). Common examples include:

    ls -l /etc/kubernetes/pki/ca.crt
    ls -l /etc/ssl/certs/ca-bundle.crt
    ls -l /etc/ssl/certs/ca-certificates.crt
  3. Edit the controller manager manifest on the control plane node to configure the root CA file (replace the example path with the correct bundle you identified in step 2):

    sudo vi /etc/kubernetes/manifests/kube-controller-manager.yaml

    Under the command: or args: list for kube-controller-manager, add (or update) the flag, for example:

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

    Save and exit. Because this is a static pod manifest, the kube-controller-manager pod will be automatically restarted by the kubelet when the file changes.

  4. (If needed) Ensure the configured file is readable by the kube-controller-manager container on the control plane node:

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

    If the file is not already accessible in the pod via a volume mount, add an appropriate hostPath volume and volumeMount in kube-controller-manager.yaml pointing to the directory that contains the CA file, then save the manifest again to trigger a restart.

  5. Wait for the kube-controller-manager pod to restart and become Running on the control plane node:

    sudo crictl pods | grep kube-controller-manager || sudo docker ps | grep kube-controller-manager
  6. Verification on every control plane node (derived from the audit command): confirm the running process includes the --root-ca-file flag with the correct path:

    /bin/ps -ef | grep kube-controller-manager | grep -v grep

    Check that the output shows --root-ca-file=/etc/kubernetes/pki/ca.crt (or the exact path you configured).

Using kubectl

kubectl cannot modify the kube-controller-manager static pod manifest or its process flags, so this finding cannot be fixed via the Kubernetes API. To remediate, you must edit /etc/kubernetes/manifests/kube-controller-manager.yaml directly on every control plane node as described in the Manual Steps section.

Automation
#!/usr/bin/env bash
#
# Automation: Ensure kube-controller-manager has --root-ca-file set
# Scope: Run on every control plane node (with root privileges)
#
# This script:
# - Backs up /etc/kubernetes/manifests/kube-controller-manager.yaml
# - Ensures --root-ca-file=/etc/kubernetes/pki/ca.crt is present
# - Is idempotent and safe to re-run
# - Verifies the running process flags
#
# NOTE:
# - Editing a static pod manifest under /etc/kubernetes/manifests will
# cause the kube-controller-manager pod to be restarted by the kubelet.

set -euo pipefail

MANIFEST="/etc/kubernetes/manifests/kube-controller-manager.yaml"
BACKUP_DIR="/etc/kubernetes/manifests/backup-$(date +%Y%m%d)"
ROOT_CA_PATH="/etc/kubernetes/pki/ca.crt"
FLAG_NAME="--root-ca-file"
FLAG_VALUE="${FLAG_NAME}=${ROOT_CA_PATH}"

require_root() {
if [ "$(id -u)" -ne 0 ]; then
echo "ERROR: This script must be run as root on each control plane node." >&2
exit 1
fi
}

check_files() {
if [ ! -f "${MANIFEST}" ]; then
echo "ERROR: Manifest not found: ${MANIFEST}" >&2
exit 1
fi
if [ ! -f "${ROOT_CA_PATH}" ]; then
echo "ERROR: Root CA file not found: ${ROOT_CA_PATH}" >&2
echo " Adjust ROOT_CA_PATH in this script if your CA bundle is elsewhere." >&2
exit 1
fi
}

backup_manifest() {
mkdir -p "${BACKUP_DIR}"
local base
base="$(basename "${MANIFEST}")"
if [ ! -f "${BACKUP_DIR}/${base}" ]; then
cp -p "${MANIFEST}" "${BACKUP_DIR}/${base}"
echo "Backup created at ${BACKUP_DIR}/${base}"
else
echo "Backup already exists at ${BACKUP_DIR}/${base}, not overwriting."
fi
}

ensure_flag_in_manifest() {
# If flag already set with correct value, do nothing
if grep -qE "[[:space:]]${FLAG_NAME}=${ROOT_CA_PATH}([[:space:]]|$|\"|')" "${MANIFEST}"; then
echo "Manifest already contains ${FLAG_VALUE}, no change needed."
return
fi

# If flag present with wrong value, replace it
if grep -q "${FLAG_NAME}=" "${MANIFEST}"; then
echo "Updating existing ${FLAG_NAME} value in manifest to ${ROOT_CA_PATH}."
# Replace any existing --root-ca-file=<something> token with the desired one
# Handles lines in args: [ "...", "--root-ca-file=/old", ... ]
sed -i -E "s#${FLAG_NAME}=[^\"'[:space:]]*#${FLAG_VALUE}#g" "${MANIFEST}"
return
fi

# Otherwise, add the flag under the command/args section.
# Try to append to an existing args: list if present.
if grep -q '^\s*args:\s*$' "${MANIFEST}"; then
echo "Adding ${FLAG_VALUE} to existing args list in manifest."
# Insert a new "- --root-ca-file=..." line after the 'args:' line
awk -v flag="${FLAG_VALUE}" '
/^\s*args:\s*$/ && !added {
print $0
print " - " flag
added=1
next
}
{ print $0 }
' "${MANIFEST}" > "${MANIFEST}.tmp"
mv "${MANIFEST}.tmp" "${MANIFEST}"
else
# Fallback: append a minimal args section with the flag under the container spec.
echo "No args list found; appending args section with ${FLAG_VALUE}."
awk -v flag="${FLAG_VALUE}" '
/- name: kube-controller-manager/ && !added {
print $0
getline
print $0
print " args:"
print " - " flag
added=1
next
}
{ print $0 }
' "${MANIFEST}" > "${MANIFEST}.tmp"
mv "${MANIFEST}.tmp" "${MANIFEST}"
fi
}

wait_for_pod_restart() {
echo "Waiting up to 120 seconds for kube-controller-manager static pod to restart..."
local timeout=120
local waited=0
while [ "${waited}" -lt "${timeout}" ]; do
if /bin/ps -ef | grep '[k]ube-controller-manager' >/dev/null 2>&1; then
# Extra small delay to allow full startup
sleep 5
return
fi
sleep 5
waited=$((waited + 5))
done
echo "WARNING: kube-controller-manager process not detected after ${timeout} seconds." >&2
}

verify_process_flag() {
echo "Verifying that kube-controller-manager is running with ${FLAG_VALUE} ..."
if /bin/ps -ef | grep '[k]ube-controller-manager' | grep -q "${FLAG_VALUE}"; then
echo "PASS: kube-controller-manager process includes ${FLAG_VALUE}"
/bin/ps -ef | grep '[k]ube-controller-manager'
return 0
else
echo "FAIL: kube-controller-manager process does not include ${FLAG_VALUE}" >&2
/bin/ps -ef | grep '[k]ube-controller-manager' || true
return 1
fi
}

main() {
require_root
check_files
backup_manifest
ensure_flag_in_manifest

echo "Manifest updated (or already compliant). kubelet will restart the static pod automatically."
# Give kubelet some time to notice and restart; then verify.
wait_for_pod_restart
verify_process_flag
}

main "$@"

Additional Reading: