runtime: gRPC client failed to establish TLS connection to grpc service

Is there an existing issue for this?

  • I have searched the existing issues

Describe the bug

var httpHandler = new HttpClientHandler
{
    ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator,
    //ServerCertificateCustomValidationCallback = (request, cert, chain, errors) => { return true; },
    SslProtocols = SslProtocols.Tls13
};
// Return `true` to allow certificates that are untrusted/invalid
//AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
using var channel = GrpcChannel.ForAddress(_grpcConfig.Endpoint, new GrpcChannelOptions { HttpHandler = httpHandler });
MyServiceClient client = new MyServiceClient(channel);
response = await client.MyRpcAsync(request);

Expected Behavior

Successfully complete gRPC call to https endpoint.

Steps To Reproduce

syntax = "proto3";
option csharp_namespace = "MyService.Ping";
package Ping;
import "google/protobuf/empty.proto";
service SvcPing {
  rpc Ping (google.protobuf.Empty) returns (Pong);
}

message Pong {
  string message = 1;
}

Deploy the gRPC service to production behind load balancer.

Create a console application and invoke the gRPC:

$ export DOTNET_ENVIRONMENT=Production&& ./Console ping
Environment: Production, server: https://mydomain.com
Press ENTER when the gRPC server @https://mydomain.com is ready

Exception! Grpc.Core.RpcException: Status(StatusCode="Internal", Detail="Error starting gRPC call. HttpRequestException: The SSL connection could not be established, see inner exception. AuthenticationException: Authentication failed, see inner exception. SslException: SSL Handshake failed with OpenSSL error - SSL_ERROR_ZERO_RETURN.", DebugException="System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception.")
 ---> System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception.
 ---> System.Security.Authentication.AuthenticationException: Authentication failed, see inner exception.
 ---> Interop+OpenSsl+SslException: SSL Handshake failed with OpenSSL error - SSL_ERROR_ZERO_RETURN.
   at Interop.OpenSsl.DoSslHandshake(SafeSslHandle context, ReadOnlySpan`1 input, Byte[]& sendBuf, Int32& sendCount)
   at System.Net.Security.SslStreamPal.HandshakeInternal(SafeDeleteSslContext& context, ReadOnlySpan`1 inputBuffer, Byte[]& outputBuffer, SslAuthenticationOptions sslAuthenticationOptions, SelectClientCertificate clientCertificateSelectionCallback)
   --- End of inner exception stack trace ---
   at System.Net.Security.SslStream.ForceAuthenticationAsync[TIOAdapter](Boolean receiveFirst, Byte[] reAuthenticationData, CancellationToken cancellationToken)
   at System.Net.Http.ConnectHelper.EstablishSslConnectionAsync(SslClientAuthenticationOptions sslOptions, HttpRequestMessage request, Boolean async, Stream stream, CancellationToken cancellationToken)
   --- End of inner exception stack trace ---
   at System.Net.Http.ConnectHelper.EstablishSslConnectionAsync(SslClientAuthenticationOptions sslOptions, HttpRequestMessage request, Boolean async, Stream stream, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.ConnectAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.AddHttp2ConnectionAsync(QueueItem queueItem)
   at System.Threading.Tasks.TaskCompletionSourceWithCancellation`1.WaitWithCancellationAsync(CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.HttpConnectionWaiter`1.WaitForConnectionAsync(Boolean async, CancellationToken requestCancellationToken)
   at System.Net.Http.HttpConnectionPool.SendWithVersionDetectionAndRetryAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken)
   at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at Grpc.Net.Client.Balancer.Internal.BalancerHttpHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
   at Grpc.Net.Client.Internal.GrpcCall`2.RunCall(HttpRequestMessage request, Nullable`1 timeout)
   --- End of inner exception stack trace ---
   at Program.PingCommand() in /usr/src/myapp/Program.cs:line 238

Exceptions (if any)

Exception! Grpc.Core.RpcException: Status(StatusCode="Internal", Detail="Error starting gRPC call. HttpRequestException: The SSL connection could not be established, see inner exception. AuthenticationException: Authentication failed, see inner exception. SslException: SSL Handshake failed with OpenSSL error - SSL_ERROR_ZERO_RETURN.", DebugException="System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception.")
 ---> System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception.
 ---> System.Security.Authentication.AuthenticationException: Authentication failed, see inner exception.
 ---> Interop+OpenSsl+SslException: SSL Handshake failed with OpenSSL error - SSL_ERROR_ZERO_RETURN.
   at Interop.OpenSsl.DoSslHandshake(SafeSslHandle context, ReadOnlySpan`1 input, Byte[]& sendBuf, Int32& sendCount)
   at System.Net.Security.SslStreamPal.HandshakeInternal(SafeDeleteSslContext& context, ReadOnlySpan`1 inputBuffer, Byte[]& outputBuffer, SslAuthenticationOptions sslAuthenticationOptions, SelectClientCertificate clientCertificateSelectionCallback)
   --- End of inner exception stack trace ---
   at System.Net.Security.SslStream.ForceAuthenticationAsync[TIOAdapter](Boolean receiveFirst, Byte[] reAuthenticationData, CancellationToken cancellationToken)
   at System.Net.Http.ConnectHelper.EstablishSslConnectionAsync(SslClientAuthenticationOptions sslOptions, HttpRequestMessage request, Boolean async, Stream stream, CancellationToken cancellationToken)
   --- End of inner exception stack trace ---
   at System.Net.Http.ConnectHelper.EstablishSslConnectionAsync(SslClientAuthenticationOptions sslOptions, HttpRequestMessage request, Boolean async, Stream stream, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.ConnectAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.AddHttp2ConnectionAsync(QueueItem queueItem)
   at System.Threading.Tasks.TaskCompletionSourceWithCancellation`1.WaitWithCancellationAsync(CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.HttpConnectionWaiter`1.WaitForConnectionAsync(Boolean async, CancellationToken requestCancellationToken)
   at System.Net.Http.HttpConnectionPool.SendWithVersionDetectionAndRetryAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken)
   at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at Grpc.Net.Client.Balancer.Internal.BalancerHttpHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
   at Grpc.Net.Client.Internal.GrpcCall`2.RunCall(HttpRequestMessage request, Nullable`1 timeout)
   --- End of inner exception stack trace ---

.NET Version

7.0.111

Anything else?

No response

About this issue

  • Original URL
  • State: closed
  • Created 9 months ago
  • Comments: 18 (13 by maintainers)

Most upvoted comments

Reproduction? Here you go!

Not sure where to begin

  • The repro is not self-contained (it depends on a certificate being manually generated to /tmp/localhost.pfx)
  • There seems to be a dockerfile, but it does not build the necessary project, only consumes binaries built from a separate step
  • The build.sh script is not helpful either since it just runs docker build and push
  • Web.Api depends on Redis running somewhere, there is no docker-compose file to set up all relevant dependencies, requiring more manual work to get repro running (none of which are mentioned in the Readme)
  • After commenting out use of Redis (and ElasticSearch), we finally get to a point where the Web.Api starts via provided Dockerfile
  • The issue does not reproduce.

All in all, 2 hours spent and we are not single a step closer to identifying the issue.

To move this forward, the issue you mentioned suggests that this may be specific to AWS, and not necessarily issue of .NET, I suggest that you either

  • provide us with packet captures when the problem occurs, as wfurt asked 4 days ago
  • setup a public endpoint which can be reached by the console app to trigger the exception.