Kubernetes etcd on 2379: No Client Cert, No Cluster
etcd's client port 2379 accepts unauthenticated connections when -client-cert-auth is false or unset, leaving only transport encryption in place. Anyone reaching the port dumps every Secret, ServiceAccount token, and RBAC object, then writes forged ClusterRoleBindings directly into the Raft log, bypassing the API server and audit trail entirely.
Operational summary
At a glance
- Symptom
- Broad security group or flat pod network reaches port 2379, allowing unauthenticated etcdctl calls against the datastore
- Likely cause
- etcd client listener without --client-cert-auth=true, bound to 0.0.0.0 instead of a scoped private interface
- Impact
- Full Secret and RBAC exfiltration plus forged ClusterRoleBindings written straight into Raft, invisible to API server audit logs.
- Verification signal
- Re-run the detection test and a controlled negative-path test.
- Safe correction
- Enable --client-cert-auth and --peer-client-cert-auth, bind listeners to private interfaces, restrict CIDR, and enable EncryptionConfiguration for Secrets.
- Rollback or recovery
- Restore the exported configuration if the new control blocks required production traffic, then narrow the policy before redeployment.
The Trap
etcd exposed on port 2379 without –client-cert-auth enabled, meaning transport is TLS-encrypted but not mutually authenticated, so any TCP connection reaching the listener can issue raw etcd API calls against the cluster’s backing store.
The Default State
kubeadm-bootstrapped clusters generate etcd certificates automatically, but self-managed installs using bare binaries, kops, kubespray templates, or bring-your-own-etcd Helm charts frequently leave –client-cert-auth=false or omit the flag, relying only on –cert-file and –key-file for encryption without mutual auth. Because –listen-client-urls defaults to binding all interfaces rather than the node’s private address, and cloud security groups are often opened for a broad “control plane” CIDR rather than scoped to the three API server IPs, the client port ends up reachable from the pod network, and sometimes from the VPC at large.
The Blast Radius
An attacker with reach to 2379 runs etcdctl –insecure-skip-tls-verify get / –prefix –keys-only and enumerates every key in the cluster: Secrets in base64 (unencrypted by default without an EncryptionConfiguration resource), ServiceAccount tokens, TLS private keys stored as Secrets, and the full RBAC graph. From there they mint a kubeconfig from a captured cluster-admin-bound token, or write directly into etcd with etcdctl put to fabricate a ClusterRoleBinding, bypassing the API server’s admission chain and audit log entirely. Writes land straight in the Raft log, and kube-apiserver picks up the forged object on its next watch cycle with no webhook check, no PodSecurity evaluation, and no entry beyond etcd’s own logs, which most SOC pipelines never ingest.
The Lead Mechanic Fix
Enforce mutual TLS on every etcd member: set –client-cert-auth=true, –trusted-ca-file, –cert-file, –key-file for the client listener, and –peer-client-cert-auth=true with matching peer flags for inter-member traffic. Bind –listen-client-urls to the node’s private interface only, never 0.0.0.0, and restrict the security group or NetworkPolicy to the exact API server IPs on 2379 and 2380. Apply an EncryptionConfiguration using aescbc or a KMS provider so Secrets are encrypted at the API server layer as a second control, and rotate the etcd CA if any exposure window existed. Confirm with etcdctl –insecure-skip-tls-verify endpoint health run from outside the allowed CIDR; it must fail closed with connection refused, not a TLS handshake.
Apply the safer control
Before you change production
Confirm the affected scope, export the current configuration, and test the replacement control in a non-production environment first.
Enable --client-cert-auth and --peer-client-cert-auth, bind listeners to private interfaces, restrict CIDR, and enable EncryptionConfiguration for Secrets.
Validate the vendor-specific syntax in official documentation before applying it.
Verify, roll back or escalate
Verify
Re-run the detection test and a controlled negative-path test. Confirm the unsafe behaviour is blocked while approved traffic still succeeds.
Rollback
Restore the exported configuration if the new control blocks required production traffic, then narrow the policy before redeployment.
Escalate
Escalate when the blast radius is uncertain, the control cannot be tested safely, or remediation requires an outage or security exception.