grpc/grpc-go · error

xds: received DNS SANs: %v do not match the SNI: %s

Error message

xds: received DNS SANs: %v do not match the SNI: %s

What it means

Returned by the peer verifier when SNI-based SAN validation is enabled (envconfig.XDSSNIEnabled && hi.validateSANUsingSNI && sni != "") and none of the leaf cert's DNS SANs match the SNI value via dnsMatch. dnsMatch supports exact and single-label wildcard matching; if every DNS SAN fails, the connection is rejected. This protects against a server presenting a cert valid for a different hostname than the one requested.

Source

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

		} else {
			opts.KeyUsages = []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}
		}
		if _, err := certs[0].Verify(opts); err != nil {
			return err
		}

		// If XDSSNIEnabled and AutoSNISANValidation are both true and the SNI is
		// non-empty, validate only DNS SANs against the SNI. Otherwise, fallback to
		// validating all received SANs against the control plane provided SAN
		// matchers.
		if envconfig.XDSSNIEnabled && hi.validateSANUsingSNI && sni != "" {
			// Verify SAN of leaf certificate with SNI using exact DNS matcher.
			for _, san := range certs[0].DNSNames {
				if dnsMatch(sni, san) {
					return nil
				}
			}
			return fmt.Errorf("xds: received DNS SANs: %v do not match the SNI: %s", certs[0].DNSNames, sni)
		}
		// The SANs sent by the xDS control plane are encoded as SPIFFE IDs. We need to
		// only look at the SANs on the leaf cert.
		if cert := certs[0]; !hi.MatchingSANExists(cert) {
			// TODO: Print the complete certificate once the x509 package
			// supports a String() method on the Certificate type.
			return fmt.Errorf("xds: received SANs {DNSNames: %v, EmailAddresses: %v, IPAddresses: %v, URIs: %v} do not match any of the accepted SANs", cert.DNSNames, cert.EmailAddresses, cert.IPAddresses, cert.URIs)
		}
		return nil
	}
}

// serverSideTLSConfigInternal constructs a tls.Config to be used in a
// server-side handshake based on the contents of the HandshakeInfo.
func (hi *HandshakeInfo) serverSideTLSConfigInternal(ctx context.Context) (*tls.Config, error) {
	cfg := &tls.Config{
		ClientAuth: tls.NoClientCert,
		NextProtos: []string{"h2"},

View on GitHub (pinned to 03255a9237)

Solutions

  1. Issue a server certificate whose DNS SANs include the hostname(s) clients connect by (or a covering wildcard).
  2. Disable useAutoHostSNI so the control-plane SNI value is used and matches the cert.
  3. Connect via a hostname that is actually present in the server cert's DNS SANs.
  4. Re-check the xDS Cluster configuration for the intended SNI vs. the certificate served by the endpoint.
Defensive patterns

Strategy: validation

Validate before calling

func certCoversSNI(c *x509.Certificate, sni string) bool {
    for _, san := range c.DNSNames {
        if dnsMatch(sni, san) { return true }
    }
    return false
}

func dnsMatch(host, san string) bool {
    host = strings.ToLower(strings.TrimSuffix(host, ".") + ".")
    san = strings.ToLower(strings.TrimSuffix(san, ".") + ".")
    if !strings.Contains(san, "*") { return host == san }
    if san == "*." || !strings.HasPrefix(san, "*.") || strings.Contains(san[1:], "*") { return false }
    if len(host) < len(san) || !strings.HasSuffix(host, san[1:]) { return false }
    return !strings.Contains(strings.TrimSuffix(host, san[1:]), ".")
}

Prevention

When it happens

Trigger: buildVerifyFunc at handshake_info.go:304-311: the client connected with SNI=foo.example.com but the server cert's DNS SANs are only [bar.example.com] or [*.other.com]. AutoHostSNI substituted the endpoint hostname as SNI, and that hostname is not covered by the cert.

Common situations: AutoHostSNI enabled with a load balancer whose cert SANs don't include the per-endpoint hostname; cert renewed with a narrower SAN set; connecting by IP or short name that's not in the cert; SNI propagation broken by a proxy.

Related errors


AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07). Data as JSON: /api/errors/f0a5ba988155476f. Report an issue: GitHub.