Ensure Etcd Cert-File And Key-File Arguments Are Set
More Info:
The etcd server must be configured with the --cert-file and --key-file arguments so that client-to-server traffic is served over TLS. Without them, etcd traffic carrying all cluster state and secrets is unencrypted.
Risk Level
Critical
Address
Security
Compliance Standards
- CIS Kubernetes
Triage and Remediation
- Remediation
Remediation
Manual Steps
-
On every etcd (control plane) node, confirm the static pod manifest location and current etcd flags:
sudo ls -l /etc/kubernetes/manifests/etcd.yamlps -ef | grep etcd | grep -v grep -
On every etcd node, ensure a certificate and key exist for the etcd server (adjust CN/SANs as needed for your environment):
sudo mkdir -p /etc/etcd/pkicd /etc/etcd/pkisudo openssl req -newkey rsa:4096 -nodes -keyout etcd.key \-x509 -days 365 -out etcd.crt \-subj "/CN=etcd.local"sudo chmod 600 /etc/etcd/pki/etcd.keysudo chmod 644 /etc/etcd/pki/etcd.crt -
On every etcd node, back up the existing etcd static pod manifest:
sudo cp -a /etc/kubernetes/manifests/etcd.yaml /etc/kubernetes/manifests/etcd.yaml.bak -
On every etcd node, edit
/etc/kubernetes/manifests/etcd.yamlto add the--cert-fileand--key-filearguments to the etcd container command. For example, in thespec.containers[0].commandlist, ensure lines like the following are present (paths must match where you stored the cert and key):- --cert-file=/etc/etcd/pki/etcd.crt- --key-file=/etc/etcd/pki/etcd.keySave the file. The kubelet will automatically restart the etcd static pod when the manifest changes.
-
On every etcd node, confirm the etcd pod has restarted successfully:
sudo crictl ps | grep etcd || sudo docker ps | grep etcd -
On every etcd node, verify that the running etcd process includes the required flags:
/bin/ps -ef | /bin/grep etcd | /bin/grep -v grepEnsure the output shows both
--cert-file=/etc/etcd/pki/etcd.crtand--key-file=/etc/etcd/pki/etcd.key(or your chosen paths).
Using kubectl
kubectl cannot modify the etcd static pod manifest or its process flags; this finding must be fixed directly on each etcd node by editing /etc/kubernetes/manifests/etcd.yaml. See the Manual Steps section for the exact host-level changes required.
Automation
#!/usr/bin/env bash
#
# Remediate: Ensure Etcd --cert-file and --key-file Arguments Are Set
#
# Usage (on each etcd/control-plane node):
# sudo ETCD_CERT_FILE=/etc/kubernetes/pki/etcd/server.crt \
# ETCD_KEY_FILE=/etc/kubernetes/pki/etcd/server.key \
# bash ./fix-etcd-cert-key.sh
#
# Idempotent: safe to re-run; only adds/updates args if needed.
# This script edits /etc/kubernetes/manifests/etcd.yaml; kubelet
# will automatically restart the etcd static pod.
set -euo pipefail
ETCD_MANIFEST="/etc/kubernetes/manifests/etcd.yaml"
# --------- Configuration (must be set via environment) ---------
CERT_FILE="${ETCD_CERT_FILE:-}"
KEY_FILE="${ETCD_KEY_FILE:-}"
if [[ -z "$CERT_FILE" || -z "$KEY_FILE" ]]; then
echo "ERROR: ETCD_CERT_FILE and ETCD_KEY_FILE environment variables must be set."
echo "Example:"
echo " sudo ETCD_CERT_FILE=/etc/kubernetes/pki/etcd/server.crt \\"
echo " ETCD_KEY_FILE=/etc/kubernetes/pki/etcd/server.key \\"
echo " bash $0"
exit 1
fi
if [[ ! -f "$CERT_FILE" ]]; then
echo "ERROR: Certificate file not found: $CERT_FILE"
exit 1
fi
if [[ ! -f "$KEY_FILE" ]]; then
echo "ERROR: Key file not found: $KEY_FILE"
exit 1
fi
if [[ ! -f "$ETCD_MANIFEST" ]]; then
echo "ERROR: etcd manifest not found at $ETCD_MANIFEST"
exit 1
fi
# --------- Backup ---------
BACKUP="${ETCD_MANIFEST}.$(date +%Y%m%d%H%M%S).bak"
cp "$ETCD_MANIFEST" "$BACKUP"
echo "Backup created: $BACKUP"
# --------- Ensure args present/updated ---------
# This is done by:
# - removing any existing --cert-file= / --key-file= entries
# - appending the desired ones under the etcd container args
tmpfile="$(mktemp)"
trap 'rm -f "$tmpfile"' EXIT
awk -v cert="$CERT_FILE" -v key="$KEY_FILE" '
BEGIN { in_args=0; done_cert=0; done_key=0 }
{
# track when we are inside the etcd container args list
if ($0 ~ /^[[:space:]]*- name:[[:space:]]*etcd[[:space:]]*$/) {
in_etcd=1
}
if (in_etcd && $0 ~ /^[[:space:]]*args:[[:space:]]*$/) {
in_args=1
}
# If we hit another container or end of containers, stop args tracking
if (in_etcd && $0 ~ /^[[:space:]]*- name:[[:space:]]*[A-Za-z0-9_-]+[[:space:]]*$/ && $0 !~ /- name: *etcd *$/) {
in_args=0
in_etcd=0
}
# Filter out existing cert/key args
if (in_args && $0 ~ /--cert-file=/) next
if (in_args && $0 ~ /--key-file=/) next
print $0
# After we print the args: line, ensure our desired args exist once
if (in_args && $0 ~ /^[[:space:]]*args:[[:space:]]*$/) {
# args: line itself, we will append items later
next
}
# Detect end of args list by a line not starting with "- " but only if we were in args
if (in_args && $0 !~ /^[[:space:]]*-[[:space:]]*"/ && $0 !~ /^[[:space:]]*-[[:space:]]*--/ && $0 !~ /^[[:space:]]*-[[:space:]]*$/) {
if (!done_cert) {
print " - \"--cert-file=" cert "\""
done_cert=1
}
if (!done_key) {
print " - \"--key-file=" key "\""
done_key=1
}
in_args=0
}
}
' "$ETCD_MANIFEST" > "$tmpfile"
# If awk failed to inject (e.g., unusual manifest structure), fall back to a simpler patch:
if ! grep -q -- "--cert-file=${CERT_FILE}" "$tmpfile" || ! grep -q -- "--key-file=${KEY_FILE}" "$tmpfile"; then
echo "Notice: Applying fallback patch logic for args section."
# Fallback: insert/replace within the etcd container args block using sed
# 1. Remove existing --cert-file/--key-file lines
sed -i '/--cert-file=/d' "$ETCD_MANIFEST"
sed -i '/--key-file=/d' "$ETCD_MANIFEST"
# 2. Append desired args after the `args:` line for the etcd container
# This is heuristic but safe to re-run (duplicates are removed first)
sed -i "/- name: etcd/{n;/[[:space:]]*args:/{
a\ - \"--cert-file=${CERT_FILE}\"\n - \"--key-file=${KEY_FILE}\"
}}" "$ETCD_MANIFEST"
tmpfile="$ETCD_MANIFEST"
else
mv "$tmpfile" "$ETCD_MANIFEST"
fi
echo "Updated $ETCD_MANIFEST with:"
echo " --cert-file=${CERT_FILE}"
echo " --key-file=${KEY_FILE}"
echo "kubelet will restart the etcd static pod automatically."
# --------- Verification ---------
echo "Waiting for etcd process to reflect new arguments..."
sleep 10
echo "Current etcd process command line:"
/bin/ps -ef | /bin/grep etcd | /bin/grep -v grep || {
echo "WARNING: etcd process not found. Check pod status with:"
echo " crictl ps | grep etcd # or docker ps | grep etcd"
exit 1
}
if /bin/ps -ef | /bin/grep etcd | /bin/grep -v grep | /bin/grep -q -- "--cert-file=${CERT_FILE}" &&
/bin/ps -ef | /bin/grep etcd | /bin/grep -v grep | /bin/grep -q -- "--key-file=${KEY_FILE}"; then
echo "Verification SUCCESS: etcd is running with --cert-file and --key-file set."
else
echo "Verification FAILED: etcd process does not show the expected --cert-file/--key-file."
echo "Inspect /etc/kubernetes/manifests/etcd.yaml and the etcd pod logs."
exit 1
fi