Skip to main content

Ensure Client Cert Auth Argument Is Enabled

More Info:​

Enable client authentication on etcd service.

Risk Level​

Medium

Address​

Security

Compliance Standards​

  • CIS Kubernetes

Triage and Remediation​

Remediation​

Manual Steps
  1. On every etcd (control plane) node, open the etcd static pod manifest for editing:

    sudo vi /etc/kubernetes/manifests/etcd.yaml
  2. In the spec.containers[0].command (or args) list for the etcd container, add or update the client auth flag so it is exactly:

    - --client-cert-auth=true

    Ensure there is no conflicting --client-cert-auth entry with a different value.

  3. Save and exit the editor. The kubelet will automatically detect the change to /etc/kubernetes/manifests/etcd.yaml and restart the etcd static pod; expect a brief etcd interruption while it restarts.

  4. Wait for etcd to come back up and confirm the etcd pod is running (on a control plane node that has crictl or docker, as appropriate):

    sudo crictl ps | grep etcd || sudo docker ps | grep etcd
  5. Verify that the etcd process is now running with --client-cert-auth=true on each etcd node:

    /bin/ps -ef | /bin/grep etcd | /bin/grep -v grep

    Confirm the output includes --client-cert-auth=true and no occurrence of --client-cert-auth=false.

Using kubectl

kubectl cannot modify the etcd static pod manifest or its process flags; this setting must be changed directly on each etcd node by editing /etc/kubernetes/manifests/etcd.yaml. Refer to the Manual Steps section for the required host-level changes and verification commands.

Automation
#!/usr/bin/env bash
#
# Enable etcd --client-cert-auth="true" in /etc/kubernetes/manifests/etcd.yaml
# Scope: run on every etcd (control plane) node
# Impact: editing the static pod manifest will cause the etcd pod to restart.

set -euo pipefail

ETCD_MANIFEST="/etc/kubernetes/manifests/etcd.yaml"
TMP_MANIFEST="/tmp/etcd.yaml.$$"

echo "[INFO] Starting etcd --client-cert-auth remediation"

if [[ ! -f "${ETCD_MANIFEST}" ]]; then
echo "[ERROR] ${ETCD_MANIFEST} not found on this node; is this an etcd/control-plane node?"
exit 1
fi

# Backup once per host (idempotent: do not overwrite an existing backup)
BACKUP="${ETCD_MANIFEST}.bak"
if [[ ! -f "${BACKUP}" ]]; then
cp -p "${ETCD_MANIFEST}" "${BACKUP}"
echo "[INFO] Backup created at ${BACKUP}"
else
echo "[INFO] Backup already exists at ${BACKUP}, not overwriting"
fi

# Check if argument already present with value true
if grep -qE -- '--client-cert-auth(=|: )"?true"?' "${ETCD_MANIFEST}"; then
echo "[INFO] --client-cert-auth already set to true; no change required"
else
echo "[INFO] Ensuring --client-cert-auth=\"true\" in ${ETCD_MANIFEST}"

# Work on a temp copy
cp "${ETCD_MANIFEST}" "${TMP_MANIFEST}"

if grep -q -- '--client-cert-auth' "${TMP_MANIFEST}"; then
# Replace existing flag value (covers --client-cert-auth=false or different formatting)
sed -i -E 's#(--client-cert-auth)(=|: )"?[^" ]*"?#\1="true"#' "${TMP_MANIFEST}"
else
# Insert the flag under the etcd container args list
# This assumes a typical kubeadm-style manifest with 'containers:' and 'name: etcd'
# and an 'args:' list. If not found, we fall back to appending to args list line.
if grep -qE 'name: etcd' "${TMP_MANIFEST}" && grep -qE 'args:' "${TMP_MANIFEST}"; then
awk '
/name:[[:space:]]*etcd/ { in_etcd=1 }
in_etcd && /args:/ { in_args=1 }
in_args && /^\s*-[[:space:]]/ { last_arg_line=NR }
{ lines[NR]=$0 }
END {
for (i=1; i<=NR; i++) {
print lines[i]
if (i==last_arg_line) {
# indent to match other args (2 spaces more than "- " line is typical)
split(lines[i],a,"- ")
indent=index(lines[i],"-")-1
pad=""
for (j=0;j<indent;j++) pad=pad " "
print pad "- --client-cert-auth=\"true\""
}
}
}
' "${TMP_MANIFEST}" > "${TMP_MANIFEST}.new" && mv "${TMP_MANIFEST}.new" "${TMP_MANIFEST}"
else
echo "[WARN] Could not reliably locate etcd args list; attempting generic append"
# Generic append: add a new arg line under the first 'args:' we see
awk '
/args:/ && !done { print; print " - --client-cert-auth=\"true\""; done=1; next }
{ print }
' "${TMP_MANIFEST}" > "${TMP_MANIFEST}.new" && mv "${TMP_MANIFEST}.new" "${TMP_MANIFEST}"
fi
fi

# Move temp manifest into place (this will trigger kubelet to restart the etcd static pod)
mv "${TMP_MANIFEST}" "${ETCD_MANIFEST}"
echo "[INFO] Updated ${ETCD_MANIFEST} with --client-cert-auth=\"true\""
fi

# Verification: wait for etcd to be running, then check process args
echo "[INFO] Verifying etcd process has --client-cert-auth=true"

# Give kubelet some time in case the pod restarted
sleep 10

if /bin/ps -ef | /bin/grep etcd | /bin/grep -v grep | /bin/grep -q -- '--client-cert-auth=true'; then
echo "[SUCCESS] etcd is running with --client-cert-auth=true"
exit 0
fi

echo "[WARN] --client-cert-auth=true not yet visible in etcd process args; retrying verification..."
sleep 10

if /bin/ps -ef | /bin/grep etcd | /bin/grep -v grep | /bin/grep -q -- '--client-cert-auth=true'; then
echo "[SUCCESS] etcd is running with --client-cert-auth=true"
exit 0
else
echo "[ERROR] Verification failed: etcd process not showing --client-cert-auth=true"
/bin/ps -ef | /bin/grep etcd | /bin/grep -v grep || true
exit 2
fi

Additional Reading:​