{"record":{"id":"4eee442ec3c8a06f","repo":"grpc/grpc-go","slug":"server-handshake-is-not-supported-by-xds-client-tl","errorCode":null,"errorMessage":"server handshake is not supported by xDS client TLS credentials","messagePattern":"server handshake is not supported by xDS client TLS credentials","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/xds/bootstrap/tlscreds/bundle.go","lineNumber":157,"sourceCode":"\t\t}\n\t}\n\treturn credentials.NewTLS(config).ClientHandshake(ctx, authority, rawConn)\n}\n\nfunc (c *reloadingCreds) Info() credentials.ProtocolInfo {\n\treturn credentials.ProtocolInfo{SecurityProtocol: \"tls\"}\n}\n\nfunc (c *reloadingCreds) Clone() credentials.TransportCredentials {\n\treturn &reloadingCreds{provider: c.provider}\n}\n\nfunc (c *reloadingCreds) OverrideServerName(string) error {\n\treturn errors.New(\"overriding server name is not supported by xDS client TLS credentials\")\n}\n\nfunc (c *reloadingCreds) ServerHandshake(net.Conn) (net.Conn, credentials.AuthInfo, error) {\n\treturn nil, nil, errors.New(\"server handshake is not supported by xDS client TLS credentials\")\n}\n\nfunc buildSPIFFEVerifyFunc(spiffeBundleMap map[string]*spiffebundle.Bundle) func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {\n\treturn func(rawCerts [][]byte, _ [][]*x509.Certificate) error {\n\t\trawCertList := make([]*x509.Certificate, len(rawCerts))\n\t\tfor i, asn1Data := range rawCerts {\n\t\t\tcert, err := x509.ParseCertificate(asn1Data)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"spiffe: verify function could not parse input certificate: %v\", err)\n\t\t\t}\n\t\t\trawCertList[i] = cert\n\t\t}\n\t\tif len(rawCertList) == 0 {\n\t\t\treturn fmt.Errorf(\"spiffe: verify function has no valid input certificates\")\n\t\t}\n\t\tleafCert := rawCertList[0]\n\t\troots, err := spiffe.GetRootsFromSPIFFEBundleMap(spiffeBundleMap, leafCert)\n\t\tif err != nil {","sourceCodeStart":139,"sourceCodeEnd":175,"githubUrl":"https://github.com/grpc/grpc-go/blob/0c51461d27177d997e14c642fe18c11668fc09a3/internal/xds/bootstrap/tlscreds/bundle.go#L139-L175","documentation":"The `reloadingCreds` in internal/xds/bootstrap/tlscreds/bundle.go are client-side-only mTLS credentials (gRFC A65). They implement ClientHandshake but explicitly reject ServerHandshake at bundle.go:157, returning this error. The bundle is designed to authenticate a gRPC client to an xDS management server, not to terminate TLS on an incoming connection.","triggerScenarios":"Triggered when the xDS-bootstrap TLS credentials are mistakenly used as server-side transport credentials (e.g. passed to `grpc.Creds()` on a `grpc.NewServer`). When the server attempts to accept a connection it calls ServerHandshake, which hits bundle.go:156-158 and returns the error.","commonSituations":"Accidentally passing the same `credentials.Bundle` returned by `tlscreds.NewBundle` to both the gRPC client and a gRPC server; copy-paste of credentials wiring from a client into a server constructor; using xDS bootstrap creds for an inbound listener.","solutions":["Use separate, server-appropriate transport credentials (e.g. `credentials.NewTLS` with a `tls.Config` holding `Certificates`) for any grpc.Server.","Do not pass the xDS-bootstrap bundle's TransportCredentials to grpc.NewServer — it is client-only.","Audit code paths that hand the bootstrap bundle to any server constructor or to a Listener-wrapping helper."],"exampleFix":"// before (wrong)\nbundle, _, _ := tlscreds.NewBundle(cfg)\nsrv := grpc.NewServer(grpc.Creds(bundle.TransportCredentials()))\n// -> server handshake is not supported ...\n\n// after\nserverTLS := credentials.NewTLS(&tls.Config{Certificates: []tls.Certificate{cert}})\nsrv := grpc.NewServer(grpc.Creds(serverTLS))","handlingStrategy":"validation","validationCode":"// Reject bootstrap creds before they reach a server constructor.\nfunc isClientOnlyCreds(c credentials.TransportCredentials) bool {\n    return strings.Contains(fmt.Sprintf(\"%T\", c), \"reloadingCreds\")\n}\n\nfunc newServer(creds credentials.TransportCredentials) (*grpc.Server, error) {\n    if isClientOnlyCreds(creds) {\n        return nil, errors.New(\"xDS bootstrap creds cannot be used on a grpc.Server\")\n    }\n    return grpc.NewServer(grpc.Creds(creds)), nil\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never pass the same credentials.Bundle to both grpc.Dial and grpc.NewServer.","Keep client and server credential construction in separate functions to make the distinction obvious.","Add a unit test asserting ServerHandshake fails for your bootstrap-derived creds to lock in the contract."],"tags":["grpc","xds","tls","server","credentials","misuse"],"backgroundTag":null,"analyzedSha":"0c51461d27177d997e14c642fe18c11668fc09a3","analyzedAt":"2026-08-11T14:49:15.055Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}