grpc/grpc-go · error
server handshake is not supported by xDS client TLS credenti
Error message
server handshake is not supported by xDS client TLS credentials
What it means
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.
Source
Thrown at internal/xds/bootstrap/tlscreds/bundle.go:157
}
}
return credentials.NewTLS(config).ClientHandshake(ctx, authority, rawConn)
}
func (c *reloadingCreds) Info() credentials.ProtocolInfo {
return credentials.ProtocolInfo{SecurityProtocol: "tls"}
}
func (c *reloadingCreds) Clone() credentials.TransportCredentials {
return &reloadingCreds{provider: c.provider}
}
func (c *reloadingCreds) OverrideServerName(string) error {
return errors.New("overriding server name is not supported by xDS client TLS credentials")
}
func (c *reloadingCreds) ServerHandshake(net.Conn) (net.Conn, credentials.AuthInfo, error) {
return nil, nil, errors.New("server handshake is not supported by xDS client TLS credentials")
}
func buildSPIFFEVerifyFunc(spiffeBundleMap map[string]*spiffebundle.Bundle) func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
return func(rawCerts [][]byte, _ [][]*x509.Certificate) error {
rawCertList := make([]*x509.Certificate, len(rawCerts))
for i, asn1Data := range rawCerts {
cert, err := x509.ParseCertificate(asn1Data)
if err != nil {
return fmt.Errorf("spiffe: verify function could not parse input certificate: %v", err)
}
rawCertList[i] = cert
}
if len(rawCertList) == 0 {
return fmt.Errorf("spiffe: verify function has no valid input certificates")
}
leafCert := rawCertList[0]
roots, err := spiffe.GetRootsFromSPIFFEBundleMap(spiffeBundleMap, leafCert)
if err != nil {View on GitHub (pinned to 0c51461d27)
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.
Example fix
// before (wrong)
bundle, _, _ := tlscreds.NewBundle(cfg)
srv := grpc.NewServer(grpc.Creds(bundle.TransportCredentials()))
// -> server handshake is not supported ...
// after
serverTLS := credentials.NewTLS(&tls.Config{Certificates: []tls.Certificate{cert}})
srv := grpc.NewServer(grpc.Creds(serverTLS)) Defensive patterns
Strategy: validation
Validate before calling
// Reject bootstrap creds before they reach a server constructor.
func isClientOnlyCreds(c credentials.TransportCredentials) bool {
return strings.Contains(fmt.Sprintf("%T", c), "reloadingCreds")
}
func newServer(creds credentials.TransportCredentials) (*grpc.Server, error) {
if isClientOnlyCreds(creds) {
return nil, errors.New("xDS bootstrap creds cannot be used on a grpc.Server")
}
return grpc.NewServer(grpc.Creds(creds)), nil
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- xds: CertificateProvider to fetch identity certificate is mi
- ClientHandshake() is not supported for server credentials
- ServerHandshake is not supported for client credentials
- xds: connection closed or HandshakeInfo dead
- xds: CertificateProvider to fetch trusted roots is missing,
AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11).
Data as JSON: /api/errors/4eee442ec3c8a06f.
Report an issue: GitHub.