fullstorydev / grpcurl

Like cURL, but for gRPC: Command-line tool for interacting with gRPC servers
MIT License
10.35k stars 497 forks source link

Wrong error message if a server requires client certificates #439

Closed TENX-S closed 3 months ago

TENX-S commented 5 months ago

We can ask a gRPC server to verify client certificates like below (tweaked from grpc-go routeguide example):

        clientCA := data.Path("x509/client_ca_cert.pem")
        b, err := os.ReadFile(clientCA)
        if err != nil {
            log.Fatalf("failed to read client ca cert: %v", err)
        }

        certPool := x509.NewCertPool()
        if !certPool.AppendCertsFromPEM(b) {
            log.Fatalf("failed to load add client CA")
        }

        serverCert, err := tls.LoadX509KeyPair(*certFile, *keyFile)
        if err != nil {
            log.Fatalf("failed to laod serverCert: %v", err)
        }

        config := &tls.Config{
            Certificates: []tls.Certificate{serverCert},
            ClientAuth:   tls.RequireAndVerifyClientCert,
            ClientCAs:    certPool,
        }

        opts = []grpc.ServerOption{grpc.Creds(credentials.NewTLS(config))}

On the client side, if we only load server cert (from grpc-go routeguide example):

        if *caFile == "" {
            *caFile = data.Path("x509/ca_cert.pem")
        }
        creds, err := credentials.NewClientTLSFromFile(*caFile, *serverHostOverride)
        if err != nil {
            log.Fatalf("Failed to create TLS credentials: %v", err)
        }
        opts = append(opts, grpc.WithTransportCredentials(creds))

Then invoking a gRPC request by using the above TLS config will get the following error immediately:

$ go run route_guide/client/client.go -tls
2024/01/19 11:33:31 Getting feature for point (409146138, -746188906)
2024/01/19 11:33:31 client.GetFeature failed: rpc error: code = Unavailable desc = connection error: desc = "error reading server preface: remote error: tls: certificate required"
exit status 1

And if there is nothing wrong with my understanding, the above client call is equivalent to the following command:

$ grpcurl  \
-cacert "D:\Developer\Projects\grpc-go\examples\data\x509\ca_cert.pem" \
-authority "x.test.example.com" \
-d "{\"latitude\": 10,\"longitude\": 10}" \
 localhost:50051 GetFeature

But this command has the following error message after 10 seconds:

Failed to dial target host "localhost:50051": context deadline exceeded

Which is not like the one that gRPC client has returned:

rpc error: code = Unavailable desc = connection error: desc = "error reading server preface: remote error: tls: certificate required"