Skip to main content

Ensure A Unique Certificate Authority Is Used For Etcd

More Info:

A dedicated --trusted-ca-file should be used for etcd so it does not share a certificate authority with the wider cluster. Using a separate CA limits the blast radius if another components CA is compromised.

Risk Level

High

Address

Security

Compliance Standards

  • CIS Kubernetes

Triage and Remediation

Remediation

Manual Steps
  1. On every etcd (control plane) node, identify the current etcd manifest and TLS settings:

    sudo cat /etc/kubernetes/manifests/etcd.yaml

    Note any existing --trusted-ca-file=... flag under the etcd container command: section and any other TLS flags such as --cert-file, --key-file, and --peer-trusted-ca-file.

  2. On every etcd node, create a dedicated CA for etcd (do NOT reuse the cluster CA). Example using OpenSSL:

    sudo mkdir -p /etc/etcd/pki
    cd /etc/etcd/pki

    sudo openssl genrsa -out etcd-ca.key 4096
    sudo openssl req -x509 -new -nodes -key etcd-ca.key -subj "/CN=etcd-ca" -days 3650 -out etcd-ca.crt

    Adjust subject, key size, and duration to match your security policy.

  3. On every etcd node, issue an etcd server certificate signed by this dedicated CA and restrict permissions:

    cd /etc/etcd/pki

    sudo openssl genrsa -out etcd-server.key 4096
    sudo openssl req -new -key etcd-server.key -subj "/CN=etcd" -out etcd-server.csr

    sudo openssl x509 -req -in etcd-server.csr -CA etcd-ca.crt -CAkey etcd-ca.key \
    -CAcreateserial -out etcd-server.crt -days 3650 -extensions v3_req -extfile <(cat <<EOF

[ v3_req ] basicConstraints = CA:FALSE keyUsage = digitalSignature,keyEncipherment extendedKeyUsage = serverAuth,clientAuth subjectAltName = @alt_names

[ alt_names ] DNS.1 = localhost DNS.2 = etcd IP.1 = 127.0.0.1 EOF ) sudo chmod 600 /etc/etcd/pki/etcd-ca.key /etc/etcd/pki/etcd-server.key sudo chown root:root /etc/etcd/pki/*


4. On every etcd node, edit `/etc/kubernetes/manifests/etcd.yaml` to use the dedicated CA and certificates. Open the file:
```bash
sudo vi /etc/kubernetes/manifests/etcd.yaml

Under the etcd container command: list, ensure you add or update the following flags to point to the dedicated CA and certs:

- --cert-file=/etc/etcd/pki/etcd-server.crt
- --key-file=/etc/etcd/pki/etcd-server.key
- --trusted-ca-file=/etc/etcd/pki/etcd-ca.crt

Also, if peer TLS is enabled, align peer CA/certs to this same dedicated CA:

- --peer-cert-file=/etc/etcd/pki/etcd-server.crt
- --peer-key-file=/etc/etcd/pki/etcd-server.key
- --peer-trusted-ca-file=/etc/etcd/pki/etcd-ca.crt

Save and exit. Editing this static pod manifest will cause the kubelet to restart the etcd pod automatically; brief etcd/control-plane disruption may occur.

  1. On every etcd node, confirm the etcd pod has restarted and is healthy:

    # from a machine with kubectl access
    kubectl get pods -n kube-system -o wide | grep etcd
    kubectl logs -n kube-system <etcd-pod-name> | tail -n 20

    Verify there are no TLS or CA-related errors in the logs.

  2. Verification (on every etcd node): ensure the etcd process is running with the dedicated --trusted-ca-file flag and that it points to the etcd-specific CA path:

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

    Confirm the command line includes:

    --trusted-ca-file=/etc/etcd/pki/etcd-ca.crt

    and that this CA is not the general Kubernetes cluster CA used by other components.

Using kubectl

kubectl cannot modify the etcd static pod manifest or its TLS configuration. This finding must be fixed directly on each etcd node by editing /etc/kubernetes/manifests/etcd.yaml and the underlying CA files; follow the Manual Steps section for the required host-level changes.

Automation
#!/usr/bin/env bash
#
# Automation: Ensure a dedicated --trusted-ca-file is configured for etcd
#
# Runs on: every etcd node (typically every control plane node)
# Requirements:
# - This script must be run as root on each etcd node.
# - A dedicated etcd CA file must already exist at /etc/kubernetes/pki/etcd/ca.crt
# (or adjust ETCD_CA_FILE below to your chosen dedicated etcd CA path).
#
# Behavior:
# - Backs up /etc/kubernetes/manifests/etcd.yaml once.
# - Ensures --trusted-ca-file=<ETCD_CA_FILE> is present in etcd command args.
# - Is safe to re-run.
# - Triggers an etcd restart via kubelet because the static pod manifest changes.
# - Verifies configuration via container inspection and process listing.
/usr/bin/id -u | /usr/bin/grep -q '^0$' || {
echo "ERROR: This script must be run as root." >&2
exit 1
}

ETCD_MANIFEST="/etc/kubernetes/manifests/etcd.yaml"
BACKUP_MANIFEST="/etc/kubernetes/manifests/etcd.yaml.cis-2.7.bak"
# Path to the dedicated etcd CA. Adjust if your CA is elsewhere.
ETCD_CA_FILE="/etc/kubernetes/pki/etcd/ca.crt"

set -euo pipefail

echo "==> Verifying prerequisites"

if [ ! -f "$ETCD_MANIFEST" ]; then
echo "ERROR: etcd manifest not found at $ETCD_MANIFEST. Are you on an etcd/control plane node?" >&2
exit 1
fi

if [ ! -f "$ETCD_CA_FILE" ]; then
echo "ERROR: etcd CA file not found at $ETCD_CA_FILE." >&2
echo "Create a dedicated etcd CA per etcd documentation, then re-run this script." >&2
exit 1
fi

echo "==> Backing up etcd manifest (one-time)"

if [ ! -f "$BACKUP_MANIFEST" ]; then
cp -p "$ETCD_MANIFEST" "$BACKUP_MANIFEST"
echo "Backup created at $BACKUP_MANIFEST"
else
echo "Backup already exists at $BACKUP_MANIFEST; not overwriting."
fi

echo "==> Ensuring --trusted-ca-file is configured for etcd"

# If the correct --trusted-ca-file is already present, no change is needed.
if /usr/bin/grep -q -- "--trusted-ca-file=${ETCD_CA_FILE}" "$ETCD_MANIFEST"; then
echo "etcd manifest already includes --trusted-ca-file=${ETCD_CA_FILE}; no update required."
else
# Remove any existing --trusted-ca-file argument lines to avoid conflicts.
# This is conservative and idempotent.
TMP_MANIFEST="$(mktemp)"
/usr/bin/grep -v -- "--trusted-ca-file=" "$ETCD_MANIFEST" > "$TMP_MANIFEST"

# Insert the correct argument. We do a simple append to the command args section.
# This assumes etcd is run as a static pod with a 'command:' list
# which is the default for kubeadm-based clusters.
if /usr/bin/grep -q 'command:' "$TMP_MANIFEST"; then
# Append the argument as a new list item after the 'command:' section.
# This awk preserves all existing content and adds the flag in the command args list.
awk -v ca_arg=" - --trusted-ca-file=${ETCD_CA_FILE}" '
/command:/ && in_cmd==0 {
print $0
in_cmd=1
next
}
in_cmd==1 && /^\s*- / {
print $0
last_cmd_line=NR
next
}
in_cmd==1 && !/^\s*- / {
if (!inserted) {
print ca_arg
inserted=1
}
in_cmd=0
print $0
next
}
{
print $0
}
END {
if (in_cmd==1 && !inserted) {
print ca_arg
}
}
' "$TMP_MANIFEST" > "${TMP_MANIFEST}.new"
mv "${TMP_MANIFEST}.new" "$TMP_MANIFEST"
else
# If no explicit command list found, just append comment + flag for manual adjustment.
echo "# Added by CIS 2.7 automation: ensure etcd uses a dedicated trusted CA" >> "$TMP_MANIFEST"
echo "# NOTE: Add the following to the etcd container command/args if not picked up automatically:" >> "$TMP_MANIFEST"
echo "# --trusted-ca-file=${ETCD_CA_FILE}" >> "$TMP_MANIFEST"
fi

mv "$TMP_MANIFEST" "$ETCD_MANIFEST"
echo "Updated $ETCD_MANIFEST with --trusted-ca-file=${ETCD_CA_FILE}"
echo "NOTE: Because this is a static pod manifest, kubelet will restart the etcd pod automatically."
fi

echo "==> Waiting for etcd pod to restart (up to 60 seconds)"
# Give kubelet a short time window to restart etcd after manifest change.
sleep 10

echo "==> Verification step 1: confirm etcd process has the correct --trusted-ca-file flag"
/bin/ps -ef | /bin/grep etcd | /bin/grep -v grep || {
echo "WARNING: etcd process not found in ps output. Check kubelet and etcd pod status." >&2
}

if /bin/ps -ef | /bin/grep etcd | /bin/grep -v grep | /bin/grep -q -- "--trusted-ca-file=${ETCD_CA_FILE}"; then
echo "Verification OK: etcd process is using --trusted-ca-file=${ETCD_CA_FILE}"
else
echo "WARNING: etcd process does not show --trusted-ca-file=${ETCD_CA_FILE} in ps output." >&2
echo "Inspect the etcd pod and container args to ensure the flag is being applied." >&2
fi

echo "==> Verification step 2: show current etcd process command line for manual review"
/bin/ps -ef | /bin/grep etcd | /bin/grep -v grep

echo "Automation for CIS Kubernetes 2.7 completed on this node."