etcd 2379 TLS Without client-cert-auth Is Theatre
A cluster can present valid TLS certificates on etcd's 2379 listener while still accepting any client that completes the handshake, because encryption and client authentication are configured independently. Anyone who reaches the port over the pod network can run etcdctl and read every Secret in plaintext, with no RBAC audit trail.
At a glance
- Unsafe setting
- etcd's --client-cert-auth flag left false while server-side TLS certificates are still configured on the 2379 listener.
- Failure trigger
- Any workload or process with pod-network reachability to port 2379 completes the TLS handshake without presenting a client certificate.
- Blast radius
- Every Secret, ServiceAccount token and ConfigMap in the cluster becomes readable via etcdctl with no RBAC check and no audit log entry.
- Recommended control
- Set --client-cert-auth=true with a trusted CA file and validate enforcement with an unauthenticated etcdctl read attempt.
The Trap
etcd serving TLS on port 2379 with --client-cert-auth=false, leaving encryption in transit configured but client identity verification switched off.
The Default State
Most kubeadm and self-managed etcd deployments set --cert-file, --key-file and --trusted-ca-file to satisfy CIS benchmark scanners checking for TLS presence, but leave --client-cert-auth unset or explicitly false. The flag only governs whether etcd demands and validates a client certificate against the trusted CA; it does not follow automatically from enabling server-side TLS. Operators who copy a kubeadm-generated manifest into a bespoke etcd cluster, or who run etcd outside kubeadm’s managed lifecycle for backup tooling, frequently drop this flag when regenerating static pod manifests, because the TLS handshake still succeeds without it and nothing in the health check output flags the gap.
The Blast Radius
Kubernetes stores every Secret, ServiceAccount token and ConfigMap unencrypted inside etcd unless EncryptionConfiguration is separately applied. With client-cert-auth disabled, any workload or attacker with pod-network reachability to 2379 can run etcdctl --endpoints=https://127.0.0.1:2379 get /registry/secrets --prefix using nothing but the server’s own CA bundle for transport encryption, no client certificate required. This bypasses kube-apiserver entirely, so RBAC, admission controllers and audit logging never see the read. Every namespace’s Secrets, including kube-system service account tokens and any stored TLS private keys, become retrievable in one command, and the compromise leaves no trace in the Kubernetes audit log.
The Lead Mechanic Fix
Set --client-cert-auth=true on every etcd member alongside --trusted-ca-file, then confirm kube-apiserver’s --etcd-certfile and --etcd-keyfile point to certificates signed by that CA. Verify enforcement directly: etcdctl --endpoints=https://127.0.0.1:2379 --cacert=ca.crt get /registry/secrets --prefix without --cert/--key must return a permission-denied error, not data. Pair this with EncryptionConfiguration using aescbc or a KMS provider so Secrets remain unreadable even from an authenticated etcd snapshot.
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.
Fix commands and configuration
--client-cert-auth=true--trusted-ca-file--etcd-certfileVerify, 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.