networking ssl_tls ai_generated true

mTLS handshake failed: certificate verify failed (no client certificate)

ID: networking/mtls-handshake-no-client-cert

Also available as: JSON · Markdown
85%Fix Rate
84%Confidence
38Evidence
2022-01-01First Seen

Version Compatibility

VersionStatusIntroducedDeprecatedNotes
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.

generic

Workarounds

  1. 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.crt

    Sources: https://www.openssl.org/docs/man3.0/man1/openssl-x509.html https://www.openssl.org/docs/man3.0/man1/openssl-req.html

  2. 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.local

    Sources: https://istio.io/latest/docs/reference/config/security/peer_authentication/ https://istio.io/latest/docs/concepts/security/#mutual-tls-authentication

  3. 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

    Sources: https://cert-manager.io/docs/usage/certificate/

Dead Ends

Common approaches that don't work:

  1. 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.

  2. 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.

  3. 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.

Error Chain

Leads to:
Preceded by:
Frequently confused with: