Skip to main content

Ensure Etcd Client-Cert-Auth Argument Is Set To True

More Info:

The --client-cert-auth argument must be set to true so etcd requires valid client certificates for all client connections. If disabled, any client that can reach etcd can read or modify all cluster state and secrets.

Risk Level

Critical

Address

Security

Compliance Standards

  • CIS Kubernetes

Triage and Remediation

Remediation

Manual Steps
  1. On every etcd (control plane) node, back up the current manifest and open it for editing:

    sudo cp -a /etc/kubernetes/manifests/etcd.yaml /etc/kubernetes/manifests/etcd.yaml.bak.$(date +%F-%H%M%S)
    sudo vi /etc/kubernetes/manifests/etcd.yaml
  2. In the spec.containers[0].command (or args) section for the etcd container, ensure the --client-cert-auth flag is present and set to true. For example, add or modify this line:

    - --client-cert-auth=true

    Make sure there is no other --client-cert-auth flag with a different value in the list.

  3. Save the file and exit the editor. The kubelet will automatically detect the change to /etc/kubernetes/manifests/etcd.yaml and restart the etcd static pod. This will temporarily restart the etcd process on that control plane node.

  4. Wait for the etcd pod to be recreated and become Running on that node:

    # Run on any machine with kubectl access
    kubectl -n kube-system get pods -o wide | grep etcd

    Confirm the etcd pod for this node is in Running status.

  5. Verify on that node that etcd is now running with --client-cert-auth=true:

    ps -ef | grep etcd | grep -v grep

    Confirm the etcd process arguments include --client-cert-auth=true and do not include --client-cert-auth=false.

  6. Repeat steps 1–5 on every etcd (control plane) node in the cluster.

Using kubectl

kubectl cannot modify the etcd 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/etcd.yaml directly on every etcd (control plane) node as described in the Manual Steps section.

Automation
#!/usr/bin/env bash
#
# Remediation: Ensure Etcd --client-cert-auth is set to true
# Scope: Run on every etcd/control-plane node (with root privileges)
#
# This script:
# - Backs up /etc/kubernetes/manifests/etcd.yaml (once per run)
# - Ensures the etcd container has "--client-cert-auth=true" in its args
# - Triggers kubelet to restart the etcd static pod by updating the manifest
# - Verifies the flag is in effect using the audit command pattern

set -euo pipefail

ETCD_MANIFEST="/etc/kubernetes/manifests/etcd.yaml"
BACKUP_DIR="/etc/kubernetes/manifests/backup-etcd-client-cert-auth"
TIMESTAMP="$(date +%Y%m%d-%H%M%S)"

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

if [[ ! -f "$ETCD_MANIFEST" ]]; then
echo "ERROR: Etcd manifest not found at $ETCD_MANIFEST; nothing to update on this node." >&2
exit 1
fi

mkdir -p "$BACKUP_DIR"

# Backup current manifest
cp -p "$ETCD_MANIFEST" "$BACKUP_DIR/etcd.yaml.$TIMESTAMP"

# Idempotent update of --client-cert-auth in the etcd container args.
# Handles three cases:
# 1) Flag already present and set to true -> no change
# 2) Flag present and set to false/other -> normalized to true
# 3) Flag absent -> inserted as a new arg
#
# This uses a small Python helper to safely edit the YAML.

python3 >/tmp/etcd.yaml.tmp << 'PYEOF'
import sys
import yaml

manifest_path = "/etc/kubernetes/manifests/etcd.yaml"

with open(manifest_path, "r") as f:
data = yaml.safe_load(f)

if not isinstance(data, dict):
print("ERROR: Unexpected YAML structure in etcd manifest.", file=sys.stderr)
sys.exit(1)

spec = data.get("spec") or {}
containers = spec.get("containers") or []
updated = False

for c in containers:
if c.get("name") != "etcd":
continue
args = c.get("args") or []
new_args = []
found = False
for arg in args:
if isinstance(arg, str) and arg.startswith("--client-cert-auth"):
# Normalize to --client-cert-auth=true
new_args.append("--client-cert-auth=true")
found = True
else:
new_args.append(arg)
if not found:
new_args.append("--client-cert-auth=true")
c["args"] = new_args
updated = True

if not updated:
print("WARNING: No etcd container found in manifest; no changes made.", file=sys.stderr)

with open(manifest_path, "w") as f:
yaml.safe_dump(data, f, default_flow_style=False)

PYEOF

# Move the temporary file into place only if Python wrote it successfully
if [[ -f /tmp/etcd.yaml.tmp ]]; then
mv /tmp/etcd.yaml.tmp "$ETCD_MANIFEST"
fi

echo "Updated $ETCD_MANIFEST to ensure --client-cert-auth=true is set."
echo "Kubelet will automatically restart the etcd static pod due to the manifest change."
echo "Waiting 30 seconds for etcd to restart..."
sleep 30

echo "Verification: checking etcd process for --client-cert-auth=true"
if /bin/ps -ef | /bin/grep etcd | /bin/grep -v grep | /bin/grep -q -- "--client-cert-auth=true"; then
echo "PASS: etcd is running with --client-cert-auth=true"
exit 0
fi

echo "FAIL: etcd process not showing --client-cert-auth=true. Full process list for etcd:"
/bin/ps -ef | /bin/grep etcd | /bin/grep -v grep || true
exit 1