{"record":{"id":"8ba097cd18bdcc37","repo":"grpc/grpc-go","slug":"xds-received-sans-dnsnames-v-emailaddresses","errorCode":null,"errorMessage":"xds: received SANs {DNSNames: %v, EmailAddresses: %v, IPAddresses: %v, URIs: %v} do not match any of the accepted SANs","messagePattern":"xds: received SANs (.+?) do not match any of the accepted SANs","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/credentials/xds/handshake_info.go","lineNumber":318,"sourceCode":"\t\t// If XDSSNIEnabled and AutoSNISANValidation are both true and the SNI is\n\t\t// non-empty, validate only DNS SANs against the SNI. Otherwise, fallback to\n\t\t// validating all received SANs against the control plane provided SAN\n\t\t// matchers.\n\t\tif envconfig.XDSSNIEnabled && hi.validateSANUsingSNI && sni != \"\" {\n\t\t\t// Verify SAN of leaf certificate with SNI using exact DNS matcher.\n\t\t\tfor _, san := range certs[0].DNSNames {\n\t\t\t\tif dnsMatch(sni, san) {\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn fmt.Errorf(\"xds: received DNS SANs: %v do not match the SNI: %s\", certs[0].DNSNames, sni)\n\t\t}\n\t\t// The SANs sent by the xDS control plane are encoded as SPIFFE IDs. We need to\n\t\t// only look at the SANs on the leaf cert.\n\t\tif cert := certs[0]; !hi.MatchingSANExists(cert) {\n\t\t\t// TODO: Print the complete certificate once the x509 package\n\t\t\t// supports a String() method on the Certificate type.\n\t\t\treturn 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)\n\t\t}\n\t\treturn nil\n\t}\n}\n\n// serverSideTLSConfigInternal constructs a tls.Config to be used in a\n// server-side handshake based on the contents of the HandshakeInfo.\nfunc (hi *HandshakeInfo) serverSideTLSConfigInternal(ctx context.Context) (*tls.Config, error) {\n\tcfg := &tls.Config{\n\t\tClientAuth: tls.NoClientCert,\n\t\tNextProtos: []string{\"h2\"},\n\t}\n\t// On the server side, identityProvider is mandatory. RootProvider is\n\t// optional based on whether the server is doing TLS or mTLS.\n\tif hi.identityProvider == nil {\n\t\treturn nil, errors.New(\"xds: CertificateProvider to fetch identity certificate is missing, cannot perform TLS handshake. Please check configuration on the management server\")\n\t}\n\tif hi.requireClientCert {","sourceCodeStart":300,"sourceCodeEnd":336,"githubUrl":"https://github.com/grpc/grpc-go/blob/0c51461d27177d997e14c642fe18c11668fc09a3/internal/credentials/xds/handshake_info.go#L300-L336","documentation":"During an xDS-managed TLS handshake, the gRPC client/server compares the Subject Alternative Names (SANs) on the peer's leaf certificate against the SAN matchers that the xDS control plane delivered via the security policy. If none of the peer certificate's DNSNames, EmailAddresses, IPAddresses, or URIs satisfy any configured matcher, the handshake is aborted at handshake_info.go:318 inside the custom verification callback. This is a trust/SAN-mismatch failure specific to xDS mTLS (not standard Go x509 verification).","triggerScenarios":"Triggered when HandshakeInfo contains one or more sanMatchers (received from an xDS security policy) and MatchingSANExists(cert) returns false for the peer's leaf cert at handshake_info.go:315. The XDSSNIEnabled / validateSANUsingSNI fast-path at line 304 must be inactive or the SNI must be empty, so execution falls through to the SAN-matcher comparison at line 315.","commonSituations":"The xDS control plane (Istio, Anthos, Envoy-based service mesh) is configured with a security policy whose SAN matchers reference identities/spiffe URIs or DNS names that do not match the certificate actually presented by the workload. Common causes: rotating/cert renewal changed SANs but the policy was not updated, workload identity (K8s service account, SPIFFE ID) drifted from the policy, cluster migration changed DNS names, or the matcher uses a regex/wildcard that does not cover the presented SAN.","solutions":["Inspect the received SANs printed in the error (DNSNames, EmailAddresses, IPAddresses, URIs) and compare them against the SAN matchers defined in your xDS security policy (e.g. Istio PeerAuthentication / RequestAuthentication / DestinationRule).","Update the security policy on the management server so at least one matcher accepts an identity the peer actually presents (e.g. add the SPIFFE URI spiffe://<trust-domain>/<ns>/<sa> or the correct DNS name).","Verify the workload's certificate was issued by the expected CA and contains the expected SANs (e.g. istioctl proxy-config secret <pod>.<ns> --validate or openssl x509 -text on the leaf).","Confirm the control plane has propagated the updated security policy to the client (check xDS ACK/NACK and config version) and retry the RPC."],"exampleFix":"// before: xDS security policy only allows\n//   spiffe://example.org/old-ns/old-sa\n// but the pod now runs as ns=payments, sa=worker\n\n// after (Istio PeerAuthentication / DestinationRule):\napiVersion: security.istio.io/v1\nkind: DestinationRule\nmetadata:\n  name: allow-new-identity\nspec:\n  host: my-svc\n  trafficPolicy:\n    tls:\n      mode: MUTUAL\n      subjectAltNames:\n        - spiffe://example.org/payments/worker","handlingStrategy":"validation","validationCode":"// Before relying on xDS security, verify the expected peer identity\n// matches a configured SAN matcher.\npackage main\n\nimport (\n\t\"crypto/x509\"\n\t\"fmt\"\n\t\"strings\"\n)\n\n// acceptedSANPatterns are the identities your xDS policy permits.\nvar acceptedSANPatterns = []string{\n\t\"spiffe://example.org/payments/worker\",\n\t\"payments.svc.cluster.local\",\n}\n\nfunc peerMatchesAcceptedSAN(cert *x509.Certificate) error {\n\tfor _, dns := range cert.DNSNames {\n\t\tfor _, p := range acceptedSANPatterns {\n\t\t\tif strings.EqualFold(dns, p) {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t}\n\tfor _, u := range cert.URIs {\n\t\tfor _, p := range acceptedSANPatterns {\n\t\t\tif u.String() == p {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t}\n\treturn fmt.Errorf(\"peer SANs %v / %v do not match accepted %v\",\n\t\tcert.DNSNames, cert.URIs, acceptedSANPatterns)\n}\n\n// func main() { _ = peerMatchesAcceptedSAN }","typeGuard":"// Narrowing helper for verifying a peer cert before trusting it.\nfunc isValidLeafCert(c *x509.Certificate) bool {\n\treturn c != nil && !c.IsCA && len(c.UnhandledCriticalExtensions) == 0\n}","tryCatchPattern":"// gRPC surfaces xDS handshake failures as the RPC error.\n// Inspect status.Code and status.Message; treat SAN mismatch as non-retryable\n// until policy/certs are corrected.\n//\n//   err := conn.Invoke(ctx, method, req, resp)\n//   if err != nil {\n//       if strings.Contains(status.Message(err), \"do not match any of the accepted SANs\") {\n//           // Do NOT retry blindly; fix the SAN matchers / certificate.\n//       }\n//   }","preventionTips":["Keep the xDS security policy's SAN matchers in sync with the workload identities (SPIFFE IDs, DNS SANs) your CA actually issues.","Run integration tests that perform a real mTLS handshake using the policy against a representative cert.","Alert on xDS NACKs related to security policy so mismatches are caught before they reach the data plane.","Pin the trust domain and identity template in CI so renames trigger test failures, not runtime handshake errors."],"tags":["xds","tls","mtls","security","san","grpc"],"backgroundTag":null,"analyzedSha":"0c51461d27177d997e14c642fe18c11668fc09a3","analyzedAt":"2026-08-11T14:49:15.055Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}