grpc/grpc-go · error

xds: no peer certificates presented

Error message

xds: no peer certificates presented

What it means

Raised inside the custom peer-cert verifier (buildVerifyFunc) when the rawCerts slice passed by the TLS stack is empty — the peer presented no certificate at all. With InsecureSkipVerify set and a custom verifier expecting a peer cert, this is fatal.

Solutions

  1. Require the peer to present a certificate: server-side use tls.RequireAnyClientCert (or RequireAndVerifyClientCert) so the handshake fails early if absent.
  2. Ensure the client is configured with an identity provider when mTLS is required.
  3. Remove any TLS-terminating proxy between the peers, or configure it to forward client certificates.
  4. If using SPIFFE bundle verification on the server, confirm ClientAuth is dropped to RequireAnyClientCert as the code does in that branch.

Example fix

// before: cfg.ClientAuth = tls.NoClientCert // peer sends nothing, verifier errors
// after: cfg.ClientAuth = tls.RequireAnyClientCert
Defensive patterns

Strategy: validation

Validate before calling

func requirePeerCerts(cfg *tls.Config) {
    // for a server expecting mTLS/SPIFFE verification
    cfg.ClientAuth = tls.RequireAnyClientCert // or RequireAndVerifyClientCert
}

Try / catch

In VerifyPeerCertificate, if len(rawCerts)==0 return a clear 'peer did not present a certificate' error and close the connection; log it as a policy violation. Do not fall back to unverified.

Prevention

When it happens

Trigger: Server-side: requireClientCert was effectively lowered such that a client connected without a cert and the verifier still ran; client-side: the server sent no certificate chain during the handshake; a TLS terminator upstream stripped the cert.

Common situations: ClientAuth misconfigured (e.g. tls.RequireAnyClientCert not set when SPIFFE bundle verification is active, or a plain TLS client hitting an mTLS-expecting path); a load balancer terminating TLS and forwarding plain; network middlebox stripping client certs.

Understand the failure class

Related errors


AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11). Data as JSON: /api/errors/d0e88f88e78e519f. Report an issue: GitHub.

Appendix: source

Thrown at internal/credentials/xds/handshake_info.go:256

	if hi.identityProvider != nil {
		km, err := hi.identityProvider.KeyMaterial(ctx)
		if err != nil {
			return nil, fmt.Errorf("xds: fetching identity certificates from CertificateProvider failed: %v", err)
		}
		cfg.Certificates = km.Certs
	}

	if envconfig.XDSSNIEnabled && sni != "" {
		cfg.ServerName = sni
	}
	return cfg, nil
}

func (hi *HandshakeInfo) buildVerifyFunc(km *certprovider.KeyMaterial, isClient bool, sni string) func(rawCerts [][]byte, _ [][]*x509.Certificate) error {
	return func(rawCerts [][]byte, _ [][]*x509.Certificate) error {
		if len(rawCerts) == 0 {
			return fmt.Errorf("xds: no peer certificates presented")
		}
		// Parse all raw certificates presented by the peer.
		var certs []*x509.Certificate
		for _, rc := range rawCerts {
			cert, err := x509.ParseCertificate(rc)
			if err != nil {
				return err
			}
			certs = append(certs, cert)
		}

		// Build the intermediates list and verify that the leaf certificate is
		// signed by one of the root certificates. If a SPIFFE Bundle Map is
		// configured, it is used to get the root certs. Otherwise, the
		// configured roots in the root provider are used.
		intermediates := x509.NewCertPool()
		for _, cert := range certs[1:] {
			intermediates.AddCert(cert)

View on GitHub (pinned to 0c51461d27)