grpc/grpc-go · error
xds: received DNS SANs
Error message
xds: received DNS SANs: %v do not match the SNI: %s
What it means
Raised in the SNI-based SAN validation branch when envconfig.XDSSNIEnabled and hi.validateSANUsingSNI are both true and sni is non-empty, but none of the leaf certificate's DNS SANs match the SNI value via dnsMatch. The connection is rejected because the server cert does not attest the name the client used for SNI.
Solutions
- Make the SNI value match a DNS SAN on the server certificate (exact or valid wildcard).
- If using useAutoHostSNI, ensure the endpoint hostname is a DNS SAN on the cert, or disable auto-host SNI and set sni to the certified name.
- Re-issue the server certificate to include the name clients dial.
- If SNI/SAN validation should not apply, disable validateSANUsingSNI on the cluster security config (it then falls back to SAN matchers).
Example fix
// before: SNI=api.example.org, cert DNS SANs=[svc.internal] -> error // after: re-issue cert with DNS SAN api.example.org, or set sni="svc.internal"
Defensive patterns
Strategy: validation
Validate before calling
func sniMatchesCertSANs(sni string, dnsSANs []string) bool {
for _, san := range dnsSANs {
if dnsMatch(sni, san) { return true }
}
return false
}
// validate against the expected server cert's DNS SANs before dialing Try / catch
When validateSANUsingSNI is on and the dial fails with this error, catch it at the RPC layer, log the SNI and the cert's DNS SANs, and either correct the SNI/dial target or re-issue the cert; do not silently retry with the same SNI.
Prevention
- Keep the SNI (or auto-host-SNI hostname) within the set of DNS SANs on the server cert.
- Disable useAutoHostSNI if endpoint hostnames are not all covered by the cert.
- Coordinate cert issuance with the names clients dial; add a CI check comparing dial targets to DNS SANs.
When it happens
Trigger: Client sets SNI to `api.example.org` but the server cert only has DNS SAN `svc.internal`, or a wildcard `*.internal` that does not match `api.example.org`; auto-host SNI picked an IP or hostname not covered by the cert; DNS SANs present but on a different domain.
Common situations: Connecting via a VIP or alias not listed in the cert; auto-host SNI enabled so the endpoint hostname becomes SNI, but the cert was issued for the cluster's virtual host; cert rotation to a new domain while clients still dial the old name; wildcard mismatch across domain levels.
Related errors
- xds: no peer certificates presented
- xds: received SANs do not match any of the accepted SANs
- input cert has URIs but should have 1
- invalid spiffeid
- xds: fetching identity certificates from…
AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11).
Data as JSON: /api/errors/f0a5ba988155476f.
Report an issue: GitHub.
Appendix: 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 0c51461d27)