etcd Peer Auth: The Flag Nobody Sets on 2380
A control-plane node reachable on TCP 2380 can join etcd's Raft quorum as a rogue member with no certificate at all, because peer traffic is often encrypted but never authenticated. Client-side TLS on 2379 does nothing to stop it, and no Kubernetes audit log records the replication.
At a glance
- Unsafe setting
- peer-client-cert-auth left disabled on the etcd static pod manifest while client-cert-auth is enforced on 2379.
- Failure trigger
- An attacker with network reach to port 2380 registers a rogue etcd member without presenting any verified certificate.
- Blast radius
- The rogue member joins the Raft quorum and receives the full replication stream of Secrets and cluster state, invisible to Kubernetes audit logging.
- Recommended control
- Enforce peer-client-cert-auth=true with CN-restricted peer certificates and firewall port 2380 to a pinned inventory of known etcd node IPs.
Fix commands and configuration
--peer-client-cert-auth=true--peer-cert-allowed-cn--peer-trusted-ca-fileThe Trap
The --peer-client-cert-auth flag on the etcd static pod manifest, left unset or explicitly false while --client-cert-auth is correctly enforced on port 2379.
The Default State
kubeadm and most manual etcd bootstraps configure --peer-cert-file and --peer-key-file so peer traffic on TCP 2380 is TLS-encrypted, but they frequently stop there. Encryption alone does not require the connecting peer to present a certificate that etcd actually verifies. Without --peer-client-cert-auth=true, etcd will complete a TLS handshake with any node that can reach 2380, self-signed cert or none, because the server never mandates client cert verification on the peer listener. The 2379 client port gets all the attention during hardening reviews; 2380 gets none.
The Blast Radius
An attacker with network reach into the control-plane subnet, via a compromised CNI plugin, a flat VPC peering, or an SSRF pivot from a workload pod, can run etcdctl member add against 2380 and register their own node as a learner. Once it syncs, that rogue member receives the full Raft replication stream: every Secret, every ServiceAccount token, every encryption-at-rest key material object stored in etcd, regardless of what RBAC or --client-cert-auth enforces on 2379. None of this touches the API server’s audit log, because replication happens beneath the apiserver entirely. The rogue member can then force a leader election, inject writes directly into the keyspace, and have those writes reappear as legitimate-looking Kubernetes objects on the next apiserver read, with zero admission control ever invoked.
The Lead Mechanic Fix
Set --peer-client-cert-auth=true on every etcd static pod manifest, add --peer-cert-allowed-cn restricted to the exact CN values used by known etcd nodes, and point --peer-trusted-ca-file at a CA issued solely for peer identities, never reused for client certs. Firewall 2380 to the known etcd node IPs only, then verify continuously with etcdctl member list --write-out=table cross-checked against a pinned node inventory on a scheduled job, alerting on any member ID not present in that baseline.