Ensure Client Ca File Argument Is Set Appropriate
More Info:
Enable Kubelet authentication using certificates.
Risk Level
Low
Address
Security
Compliance Standards
- APRA CPS 234 (Australia)
- BSI C5 (Germany)
- Brazil LGPD
- CCPA / CPRA (California)
- CIS Critical Security Controls v8
- CIS EKS
- CMMC 2.0
- CSA Cloud Controls Matrix v4
- DPDPA
- Digital Operational Resilience Act (EU)
- Essential 8
- ISO/IEC 27017
- ISO/IEC 27018
- ISO/IEC 27701
- KSA PDPL
- MAS Technology Risk Management (Singapore)
- MITRE ATT&CK (Cloud)
- NIS2 Directive
- NIST SP 800-171
- NYDFS 23 NYCRR 500
- SWIFT Customer Security Controls Framework
- Sarbanes-Oxley IT General Controls
- UK NCSC Cyber Assessment Framework
Triage and Remediation
- Remediation
Remediation
Manual Steps
-
On every worker node, inspect the current kubelet configuration file to see if
clientCAFileis already set:sudo grep -A5 'authentication:' /var/lib/kubelet/config.yaml || sudo sed -n '1,120p' /var/lib/kubelet/config.yaml -
On every worker node, identify the correct client CA file path (this is an operational/security decision; typically the same CA used by the API server for client auth, e.g.
/etc/kubernetes/pki/ca.crt). Confirm the file exists and is readable by kubelet:sudo ls -l /etc/kubernetes/pki/ca.crtsudo stat /etc/kubernetes/pki/ca.crt -
On every worker node, edit
/var/lib/kubelet/config.yamlto configure x509 client auth with the chosen CA file. For example, usingvi:sudo vi /var/lib/kubelet/config.yamlEnsure you have (add or modify as needed):
authentication:anonymous:enabled: falsewebhook:enabled: truex509:clientCAFile: "/etc/kubernetes/pki/ca.crt"Keep indentation consistent with the rest of the file.
-
If the kubelet is also started with command‑line flags that might override this setting, review the systemd drop‑in to ensure no conflicting
--client-ca-fileflag is present or that it matches the same path:sudo cat /etc/systemd/system/kubelet.service.d/10-kubeadm.confIf needed, edit:
sudo vi /etc/systemd/system/kubelet.service.d/10-kubeadm.confand either remove any
--client-ca-file=...argument or set it to:--client-ca-file=/etc/kubernetes/pki/ca.crt -
On every worker node, reload systemd and restart kubelet (this will temporarily disrupt workloads managed by that kubelet while it reconnects):
sudo systemctl daemon-reloadsudo systemctl restart kubelet.service -
Verification on every worker node: confirm the kubelet process is running with the intended config and/or flag. First, check the process:
/bin/ps -fC kubeletThen confirm the effective configuration contains the
clientCAFileyou set:sudo grep -A5 'authentication:' /var/lib/kubelet/config.yaml
Using kubectl
kubectl cannot modify kubelet host-level configuration such as /var/lib/kubelet/config.yaml or the kubelet systemd units. To address this finding, make the changes directly on every worker node as described in the Manual Steps section.
Automation
#!/usr/bin/env bash
#
# Configure kubelet to use an X.509 client CA file for authentication
# Target: every worker node
#
# This script is idempotent and safe to re-run.
# REQUIREMENT: You must set CLIENT_CA_FILE to an existing readable CA file.
set -euo pipefail
CLIENT_CA_FILE="/etc/kubernetes/pki/ca.crt" # <-- ADJUST IF NEEDED
if [[ ! -r "${CLIENT_CA_FILE}" ]]; then
echo "ERROR: CLIENT_CA_FILE '${CLIENT_CA_FILE}' does not exist or is not readable."
echo "Update CLIENT_CA_FILE in this script to the correct CA path, then re-run."
exit 1
fi
echo "Using client CA file: ${CLIENT_CA_FILE}"
KUBELET_CONFIG_FILE="/var/lib/kubelet/config.yaml"
KUBELET_SYSTEMD_DROPIN="/etc/systemd/system/kubelet.service.d/10-kubeadm.conf"
changed=0
############################################
# 1. Ensure Kubelet config file has clientCAFile
############################################
if [[ -f "${KUBELET_CONFIG_FILE}" ]]; then
echo "Found kubelet config file at ${KUBELET_CONFIG_FILE}"
# Ensure top-level 'authentication:' block exists
if ! grep -qE '^[[:space:]]*authentication:' "${KUBELET_CONFIG_FILE}"; then
echo "Adding 'authentication:' block to ${KUBELET_CONFIG_FILE}"
cat <<'EOF' >> "${KUBELET_CONFIG_FILE}"
authentication:
anonymous:
enabled: false
x509:
clientCAFile: ""
EOF
changed=1
fi
# Ensure 'authentication: x509: clientCAFile' is set to desired path
if grep -qE '^[[:space:]]*authentication:[[:space:]]*$' "${KUBELET_CONFIG_FILE}"; then
# Use python (if available) to do yaml-safe update; otherwise fall back to sed
if command -v python3 >/dev/null 2>&1; then
python3 <<PYEOF
import sys, yaml, pathlib
cfg_path = pathlib.Path("${KUBELET_CONFIG_FILE}")
data = yaml.safe_load(cfg_path.read_text()) or {}
auth = data.setdefault("authentication", {})
x509 = auth.setdefault("x509", {})
if x509.get("clientCAFile") != "${CLIENT_CA_FILE}":
x509["clientCAFile"] = "${CLIENT_CA_FILE}"
cfg_path.write_text(yaml.safe_dump(data, default_flow_style=False))
PYEOF
changed=$((changed + 1))
else
# Fallback: naive update (assumes existing x509 block or appends one)
if grep -qE '^[[:space:]]*clientCAFile:' "${KUBELET_CONFIG_FILE}"; then
sed -i "s#^\([[:space:]]*clientCAFile:\).*#\1 ${CLIENT_CA_FILE}#" "${KUBELET_CONFIG_FILE}"
else
awk -v ca="${CLIENT_CA_FILE}" '
{print}
/^[[:space:]]*authentication:[[:space:]]*$/ {
print " x509:"
print " clientCAFile: " ca
}
' "${KUBELET_CONFIG_FILE}" > "${KUBELET_CONFIG_FILE}.tmp"
mv "${KUBELET_CONFIG_FILE}.tmp" "${KUBELET_CONFIG_FILE}"
fi
changed=$((changed + 1))
fi
fi
else
echo "No kubelet config file at ${KUBELET_CONFIG_FILE}; skipping file-based configuration."
fi
############################################
# 2. Ensure systemd drop-in has --client-ca-file
############################################
if [[ -f "${KUBELET_SYSTEMD_DROPIN}" ]]; then
echo "Found kubelet systemd drop-in at ${KUBELET_SYSTEMD_DROPIN}"
if grep -q 'KUBELET_AUTHZ_ARGS' "${KUBELET_SYSTEMD_DROPIN}"; then
# Update or add --client-ca-file within existing KUBELET_AUTHZ_ARGS
if grep -q -- '--client-ca-file=' "${KUBELET_SYSTEMD_DROPIN}"; then
sed -i "s#--client-ca-file=[^\" ]*#--client-ca-file=${CLIENT_CA_FILE}#g" "${KUBELET_SYSTEMD_DROPIN}"
else
sed -i 's/^\(Environment="KUBELET_AUTHZ_ARGS=.*\)"/\1 --client-ca-file='"${CLIENT_CA_FILE}"'"/' "${KUBELET_SYSTEMD_DROPIN}"
fi
else
# Add a new KUBELET_AUTHZ_ARGS environment line
sed -i '/^\[Service\]/a Environment="KUBELET_AUTHZ_ARGS=--client-ca-file='"${CLIENT_CA_FILE}"'"' "${KUBELET_SYSTEMD_DROPIN}"
fi
changed=$((changed + 1))
else
echo "No kubelet systemd drop-in at ${KUBELET_SYSTEMD_DROPIN}; skipping flag-based configuration."
fi
############################################
# 3. Reload systemd and restart kubelet if anything changed
############################################
if [[ "${changed}" -gt 0 ]]; then
echo "Configuration updated; reloading systemd and restarting kubelet..."
systemctl daemon-reload
systemctl restart kubelet.service
else
echo "No changes made; kubelet restart not required."
fi
############################################
# 4. Verification (equivalent to audit)
############################################
echo "Verifying kubelet process arguments and config..."
/bin/ps -fC kubelet || {
echo "ERROR: kubelet process not found."
exit 1
}
echo
echo "Kubelet process line:"
/bin/ps -fC kubelet | sed -n '2p'
echo
echo "Searching for '--client-ca-file' in kubelet args:"
if /bin/ps -fC kubelet | grep -q -- '--client-ca-file='; then
/bin/ps -fC kubelet | sed -n '2p' | grep -- '--client-ca-file='
else
echo "No --client-ca-file flag found in kubelet process; relying on config file (if present)."
fi
if [[ -f "${KUBELET_CONFIG_FILE}" ]]; then
echo
echo "Effective clientCAFile from ${KUBELET_CONFIG_FILE}:"
grep -n 'clientCAFile' "${KUBELET_CONFIG_FILE}" || echo "clientCAFile not found in kubelet config."
fi
echo
echo "Done."