← News
/PLAYBOOK · 2026-08-21

etcd Security Hardening in Practice: TLS Encryption and Access-Control Configuration Guide

Default etcd is world-readable. Compromise in a Kubernetes cluster leaks every secret. This guide hardens etcd with mutual TLS, RBAC, and network isolation.

etcd hardening is core to Kubernetes and distributed-system defense. etcd is the cluster KV hub: ServiceAccount secrets, ConfigMaps, Pod specs, all sensitive data. No auth, and one curl dumps the cluster. This guide covers mutual TLS, RBAC, and network isolation.

Use Cases

Prerequisites

How It Works

etcd security is three layers: TLS encrypts client↔etcd and peer traffic; `--client-cert-auth` forces a CA-issued client cert (mutual TLS); RBAC then gates read/write by user and role. Goal: no cert, no entry; cert without grants, no extra rights. kubeadm clusters enable peer certs by default; client side may still be one-way TLS. Complete it with this guide.

Procedure

1. Generate CA and certs

mkdir -p /etc/etcd/pki && cd /etc/etcd/pki
# 1) Create a local CA
openssl genrsa -out ca.key 2048
openssl req -x509 -new -nodes -key ca.key -days 3650 -subj "/CN=etcd-ca" -out ca.crt
# 2) Issue the server certificate (set IP to this host; SAN must include the domain/IP clients use to connect)
openssl genrsa -out server.key 2048
cat > server.cnf <<'EOF'
[req]
distinguished_name=dn
req_extensions=v3_req
[dn]
[alt_names]
IP.1 = 127.0.0.1
IP.2 = 10.0.0.10
EOF
openssl req -new -key server.key -subj "/CN=etcd-server" -out server.csr
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial   -days 3650 -out server.crt -extensions v3_req -extfile server.cnf
# 3) Issue the peer certificate (one per etcd node; CN is the node name)
openssl genrsa -out peer.key 2048
openssl req -new -key peer.key -subj "/CN=etcd-node1" -out peer.csr
openssl x509 -req -in peer.csr -CA ca.crt -CAkey ca.key -CAcreateserial   -days 3650 -out peer.crt
chmod 600 *.key

2. etcd startup flags (mutual TLS)

etcd \
  --name etcd-node1 \
  --data-dir /var/lib/etcd \
  --listen-client-urls https://10.0.0.10:2379,https://127.0.0.1:2379 \
  --advertise-client-urls https://10.0.0.10:2379 \
  --client-cert-auth --trusted-ca-file=/etc/etcd/pki/ca.crt \
  --cert-file=/etc/etcd/pki/server.crt --key-file=/etc/etcd/pki/server.key \
  --listen-peer-urls https://10.0.0.10:2380 \
  --peer-client-cert-auth --peer-trusted-ca-file=/etc/etcd/pki/ca.crt \
  --peer-cert-file=/etc/etcd/pki/peer.crt --peer-key-file=/etc/etcd/pki/peer.key

Critical flags: client-cert-auth for client cert checks, peer-client-cert-auth for peer cert checks. Both required.

3. Verify unauthorized access is denied

# Access without a certificate should fail
curl --cacert /etc/etcd/pki/ca.crt https://10.0.0.10:2379/version
# Expected: certificate error or handshake failure
# Access with a certificate succeeds
curl --cacert /etc/etcd/pki/ca.crt --cert /etc/etcd/pki/server.crt --key /etc/etcd/pki/server.key \
     https://10.0.0.10:2379/version

4. Enable RBAC users and roles

export ETCDCTL_API=3
alias ec="etcdctl --endpoints=https://10.0.0.10:2379 --cacert=/etc/etcd/pki/ca.crt --cert=/etc/etcd/pki/server.crt --key=/etc/etcd/pki/server.key"
# Create the root user and enable authentication
ec user add root
ec auth enable
# Create a read-only user and role
ec role add read-only
ec role grant-permission read-only read / --prefix=true
ec user add viewer
ec user grant-role viewer read-only
# Test: viewer can read but not write
ec --user viewer:password get / --prefix=true --limit=5
ec --user viewer:password put /test x
# Expected: write returns 13 (permission denied)

After auth enable, calls with no user return 401. Expected.

5. Firewall 2379 sources

# Allow only kube-apiserver nodes and the ops network segment to access 2379
firewall-cmd --permanent --add-rich-rule='rule family=ipv4 source address=10.0.0.20 port port=2379 protocol=tcp accept'
firewall-cmd --permanent --add-rich-rule='rule family=ipv4 port port=2379 protocol=tcp reject'
firewall-cmd --reload

6. Kubernetes: sync kube-apiserver

# kube-apiserver startup flags (Kubeadm: edit /etc/kubernetes/manifests/kube-apiserver.yaml)
--etcd-cafile=/etc/kubernetes/pki/etcd/ca.crt
--etcd-certfile=/etc/kubernetes/pki/apiserver-etcd-client.crt
--etcd-keyfile=/etc/kubernetes/pki/apiserver-etcd-client.key
--etcd-servers=https://10.0.0.10:2379

Verification

FAQ

FAQ 1: Certs about to expire. How to rotate?

Re-issue longer-lived server/peer certs from the same CA, replace, restart etcd. No CA rebuild. If the CA itself expired, re-issue for every etcd node and every client (kube-apiserver, etcdctl). Major change. Roll in a maintenance window: peers first, clients second.

FAQ 2: etcdctl returns 401 after RBAC. Is data corrupt?

No. Auth is blocking. Re-auth as root: `etcdctl user get root`, or check etcd logs for the 401 source. Do not delete root or lose the password; recovery is `--auth-token` or snapshot restore.

FAQ 3: Cloud-managed etcd (ACK, EKS). Still harden?

Control-plane etcd is the vendor's job. On the customer side: confirm apiserver RBAC is on, do not grant `system:masters` casually, restrict write on kube-system, re-run kube-bench on etcd items.

Summary

etcd hardening is encrypt, authenticate, authorize, isolate: mutual TLS kills plaintext and forged clients; RBAC minimizes privilege; firewall shrinks 2379 to trusted nets. Production clusters must finish TLS and RBAC before exposing 2379. Put cert rotation on the ops calendar. That closes the cluster data-leak path at the root.