mTLS handshake failed: certificate verify failed (no client certificate)
ID: networking/mtls-handshake-no-client-cert
Version Compatibility
| Version | Status | Introduced | Deprecated | Notes |
|---|---|---|---|---|
| any | active | — | — | — |
Root Cause
The server requires mutual TLS (mTLS) authentication, meaning the client must present a valid certificate signed by the server's trusted CA during the TLS handshake. The client either has no certificate configured, the certificate is not signed by a CA the server trusts, or the certificate/key pair is mismatched. This is common in zero-trust architectures, service meshes (Istio, Linkerd), and API gateways that enforce client certificate authentication.
genericWorkarounds
-
88% success Configure the client with the correct certificate and private key signed by the server's trusted CA
1. Generate a client key and CSR: openssl genrsa -out client.key 4096 openssl req -new -key client.key -out client.csr -subj '/CN=my-service/O=my-org' 2. Sign with the CA the server trusts: openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt -days 365 -sha256 3. Configure the client (curl example): curl --cert client.crt --key client.key --cacert ca.crt https://mtls-server.example.com 4. Python requests example: import requests resp = requests.get('https://mtls-server.example.com', cert=('client.crt', 'client.key'), verify='ca.crt') 5. Verify the chain: openssl verify -CAfile ca.crt client.crtSources: https://www.openssl.org/docs/man3.0/man1/openssl-x509.html https://www.openssl.org/docs/man3.0/man1/openssl-req.html
-
85% success In Kubernetes/Istio, configure PeerAuthentication and DestinationRule for proper mTLS
For Istio service mesh mTLS: # Enable strict mTLS for the namespace apiVersion: security.istio.io/v1beta1 kind: PeerAuthentication metadata: name: default namespace: my-namespace spec: mtls: mode: STRICT # Configure destination rule to use mTLS apiVersion: networking.istio.io/v1beta1 kind: DestinationRule metadata: name: my-service namespace: my-namespace spec: host: my-service.my-namespace.svc.cluster.local trafficPolicy: tls: mode: ISTIO_MUTUAL Verify: istioctl proxy-config secret <pod-name> -n my-namespace Check: istioctl authn tls-check <pod-name> my-service.my-namespace.svc.cluster.localSources: https://istio.io/latest/docs/reference/config/security/peer_authentication/ https://istio.io/latest/docs/concepts/security/#mutual-tls-authentication
-
82% success Use cert-manager to automate client certificate lifecycle in Kubernetes
Install cert-manager and create a Certificate resource: apiVersion: cert-manager.io/v1 kind: Certificate metadata: name: my-service-client-cert namespace: my-namespace spec: secretName: my-service-client-tls issuerRef: name: internal-ca-issuer kind: ClusterIssuer commonName: my-service usages: - client auth - digital signature duration: 8760h renewBefore: 720h Mount the secret in your pod: volumes: - name: client-tls secret: secretName: my-service-client-tls volumeMounts: - name: client-tls mountPath: /etc/tls/client readOnly: true
Dead Ends
Common approaches that don't work:
-
Disabling server-side client certificate verification to skip mTLS
85% fail
Disabling client certificate verification (e.g., setting ssl_verify_client to 'off' in nginx or removing ClientAuth from Go TLS config) completely removes the mTLS security layer. This defeats the purpose of mutual authentication and violates zero-trust principles. In service mesh environments (Istio, Linkerd), you cannot disable mTLS without modifying mesh-wide policy, which affects all services.
-
Using a self-signed client certificate not signed by the server's trusted CA
90% fail
The server validates the client certificate against its configured CA bundle. A self-signed certificate or one signed by a different CA will fail verification even if the certificate format and key are correct. The server's trust store must contain the CA that signed the client certificate. In Kubernetes/Istio environments, certificates are managed by the mesh CA (e.g., istiod) and manually created certs are rejected.
-
Setting only the client certificate without the private key
95% fail
During the TLS handshake, the client must prove possession of the private key corresponding to the client certificate by signing a CertificateVerify message. Without the private key, the handshake fails after the certificate is sent. Both the certificate and matching private key must be configured together.