Amelia B
Amelia B

Reputation: 1084

gRPC failed to connect to all addresses or DNS resolution failed for service

I am currently trying to get an example for gRPC working. I am using a C# Asp.NET Core WebApi as the server and I try to connect to it via a Python client.

Ressources

My proto file:

syntax = "proto3";

service Example {
  rpc Insert (InsertRequest) returns (Empty);
  rpc Update (UpdateRequest) returns (Empty);
  rpc Delete (DeleteRequest) returns (Empty);
}

message InsertRequest {
    int32 Value = 1;
}

message UpdateRequest {
  string Id = 1;
  int32 Value = 2;
}

message DeleteRequest {
  string Id = 1;
}

message Empty { }

The Python Client:

import grpc

import example_pb2
import example_pb2_grpc

with grpc.insecure_channel('localhost:5001') as channel:
    stub = example_pb2_grpc.ExampleStub(channel)
    stub.Insert(example_pb2.InsertRequest(Value = 155))

Problem

When I try to run my Python client I get the following error:

Traceback (most recent call last): File "gRPCExampleClient.py", line 10, in stub.Insert(example_pb2.InsertRequest(Value = 155)) File "C:\Python38\lib\site-packages\grpc_channel.py", line 923, in call return _end_unary_response_blocking(state, call, False, None) File "C:\Python38\lib\site-packages\grpc_channel.py", line 826, in _end_unary_response_blocking raise _InactiveRpcError(state) grpc._channel._InactiveRpcError: <_InactiveRpcError of RPC that terminated with: status = StatusCode.UNAVAILABLE details = "failed to connect to all addresses" debug_error_string = "{"created":"@1612810257.299000000","description":"Failed to pick subchannel","file":"src/core/ext/filters/client_channel/client_channel.cc","file_line":5391,"referenced_errors":[{"created":"@1612810257.299000000","description":"failed to connect to all addresses","file":"src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc","file_line":398,"grpc_status":14}]}"

What I tried so far

    class Program
    {
        static void Main(string[] args)
        {
            var channel = GrpcChannel.ForAddress("https://localhost:5001");
            var client = new Example.ExampleClient(channel);

            client.Insert(new InsertRequest() { Value = 155 });

            Console.ReadKey(true);
        }
    }

Traceback (most recent call last): File "gRPCExampleClient.py", line 10, in stub.Insert(example_pb2.InsertRequest(Value = 155)) File "C:\Python38\lib\site-packages\grpc_channel.py", line 923, in call return _end_unary_response_blocking(state, call, False, None) File "C:\Python38\lib\site-packages\grpc_channel.py", line 826, in _end_unary_response_blocking raise _InactiveRpcError(state) grpc._channel._InactiveRpcError: <_InactiveRpcError of RPC that terminated with: status = StatusCode.UNAVAILABLE details = "DNS resolution failed for service: https://localhost:5001" debug_error_string = "{"created":"@1612809227.889000000","description":"Resolver transient failure","file":"src/core/ext/filters/client_channel/client_channel.cc","file_line":2141,"referenced_errors":[{"created":"@1612809227.889000000","description":"DNS resolution failed for service: https://localhost:5001","file":"src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc","file_line":201,"grpc_status":14,"referenced_errors":[{"created":"@1612809227.889000000","description":"OS Error","file":"src/core/lib/iomgr/resolve_address_windows.cc","file_line":95,"os_error":"No such host is known.\r\n","syscall":"getaddrinfo","wsa_error":11001}]}]}"

from os import environ

environ["GRPC_DNS_RESOLVER"] = "native"

Misc infos

I hope I am just missing something obvious thanks for reading and considering to help.

Upvotes: 5

Views: 28883

Answers (1)

Amelia B
Amelia B

Reputation: 1084

I finally figured it out. The underlying problem was a mismatch in my server configuration. Since the C# client automagically detects the ASP.NET core development certificates set by dotnet it will be able to connect securely to the server. All other scripting languages like python and go will not be able to do this and refuse to connect since the certificate can not be verified. (Which is completely correct it still is just a self-signed cert intended for debugging)

In short: You can not connect insecurely to a gRPC server unless it is explicitly set up to serve insecure http requests over HTTP/2.

So we now have two options:

  1. Explicitly give our script clients the certificates public key so they know they can trust our server
  2. Start our server in non-SSL mode using HTTP/2

1

To export my asp.net core development certificate I used certmgr. With this tool, we can export the public key portion of the dev cert as a .cer file. After this we just have to tell our client to establish a secure connection and verify against the public key we just exported:

with open('../dev-cert.cer', 'rb') as f: #manually import public key of localhost ASP.NET Core dev certioficate (exported with certmgr)
        credentials = grpc.ssl_channel_credentials(f.read())
with grpc.secure_channel('localhost:5001', credentials) as channel:
    stub = example_pb2_grpc.ExampleStub(channel)
    stub.Insert(example_pb2.InsertRequest(Value = 155))

I also did test all this in go (to verify it isn't just a python library issue)

func main() {
    creds, err := credentials.NewClientTLSFromFile("../dev-cert.cer", "")
    if err != nil {
        log.Fatalf("could not process the credentials: %v", err)
    }

    conn, err := grpc.Dial("localhost:5001", grpc.WithTransportCredentials(creds))

    if err != nil {
        log.Fatalf("did not connect: %s", err)
    }
    defer conn.Close()

    request := example_grpc_client.InsertRequest{
        Value: 123,
    }

    client := example_grpc_client.NewExampleClient(conn)

    _, err = client.Insert(context.Background(), &request)
    if err != nil {
        log.Fatalf("error sending message: %s", err)
    }
}

2

This is the first solution I found but I do not recommend using this as it is merely a workaround. I was able to get an insecure connection working by configuring kestrel to launch in HTTP/2 with insecure connection support by configuring my sever application:

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.ConfigureKestrel(options => 
                    {
                        options.ListenLocalhost(5001, o => o.Protocols = HttpProtocols.Http2);
                    });

                    webBuilder.UseStartup<Startup>();
                });

Upvotes: 8

Related Questions