Reputation: 1084
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.
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))
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}]}"
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);
}
}
with grpc.insecure_channel('https://localhost:5001') as channel:
). When doing this I get the following error message: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}]}]}"
GRPC_DNS_RESOLVER
to native
. Sadly the error stayed the samefrom os import environ
environ["GRPC_DNS_RESOLVER"] = "native"
127.0.0.1
, 0.0.0.0
with and without https://
scheme instead of localhost
dns://
as a default. I was able to confirm this by enabling environ["GRPC_TRACE"] = "api,client_channel_routing,cares_resolver"
and environ["GRPC_VERBOSITY"] = "debug"
https
is the one that I should focus on resolving since dns://
might be resolvable but doesn't make any sense in the first place.I hope I am just missing something obvious thanks for reading and considering to help.
Upvotes: 5
Views: 28883
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:
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)
}
}
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