Raghwendra Singh
Raghwendra Singh

Reputation: 2234

Ruby's grpc(v1.3.2) gem SSL/TLS connection issue with grpc server built entirely in golang

Recently, I was trying to use rubygem grpc version 1.3.2 as a clinet and connect to a grpc server which is built from golang. I went through the documentation at GRPC.IO and used it in my code as it.

    irb(main):017:0> GRPC::Core::Credentials.new(File.read(CA_FILE_PATH))
NameError: uninitialized constant GRPC::Core::Credentials
        from (irb):17
        from /usr/local/share/gems/gems/railties-4.2.1/lib/rails/commands/console.rb:110:in `start'
        from /usr/local/share/gems/gems/railties-4.2.1/lib/rails/commands/console.rb:9:in `start'
        from /usr/local/share/gems/gems/railties-4.2.1/lib/rails/commands/commands_tasks.rb:68:in `console'
        from /usr/local/share/gems/gems/railties-4.2.1/lib/rails/commands/commands_tasks.rb:39:in `run_command!'
        from /usr/local/share/gems/gems/railties-4.2.1/lib/rails/commands.rb:17:in `<top (required)>'
        from bin/rails:4:in `require'
        from bin/rails:4:in `<main>'

However their documentation specifically says,

creds = GRPC::Core::Credentials.new(load_certs)  # load_certs typically loads a CA roots file
stub = Helloworld::Greeter::Stub.new('myservice.example.com', creds)

Then I came across ChannelCredentials and the creds is supposed to be either ChannelCredentials object or a symbol(e.g. :this_channel_is_insecure). Hence, I gave it a try as well.

I've taken the following function from the grpc gem's source code itself. This function was called in rspec test cases for loading the certs:

def load_certs
      data_dir = "#{Rails.root}/certs"
      files = ['ca.pem', 'server.key', 'server.pem']
      files.map { |f| File.open(File.join(data_dir, f)).read }
end

Then I gave it a try with,

channel_creds = GRPC::Core::ChannelCredentials.new(load_certs)
stub = Helloworld::Greeter::Stub.new('myservice.example.com', channel_creds)

But the above failed with

E0619 09:59:10.410575570   14208 ssl_transport_security.c:601] Could not load any root certificate.
E0619 09:59:10.410604954   14208 ssl_transport_security.c:1315] Cannot load server root certificates.
E0619 09:59:10.410622519   14208 security_connector.c:837]   Handshaker factory creation failed with TSI_INVALID_ARGUMENT.

I also tried:

channel_creds = GRPC::Core::ChannelCredentials.new(File.read(CA_FILE_PATH))
stub = Helloworld::Greeter::Stub.new('myservice.example.com', creds)

But all I got was error from the logs or rpc server:

2017/06/16 10:52:34 transport: http2Server.HandleStreams failed to receive the preface from client: EOF
2017/06/16 10:53:35 transport: http2Server.HandleStreams failed to receive the preface from client: EOF
2017/06/16 10:53:59 transport: http2Server.HandleStreams failed to receive the preface from client: EOF
2017/06/16 10:55:06 transport: http2Server.HandleStreams failed to receive the preface from client: EOF

Has anyone successfully tried this Ruby client Golang server combination with SSL/TLS enabled?

Upvotes: 6

Views: 2468

Answers (2)

webgen
webgen

Reputation: 183

I can confirm this works.

channel_creds = GRPC::Core::ChannelCredentials.new(File.read("/home/user/.lnd/tls.cert"))
stub = Lnrpc::Lightning::Stub.new("127.0.0.1:10009", channel_creds)
obj = Lnrpc::GetInfoRequest.new
pp stub.get_info(obj)

Upvotes: 0

apolcyn
apolcyn

Reputation: 124

creds is supposed to be either ChannelCredentials object or a symbol

Yes the second argument of a client stub constructor (the creds argument), should be either a GRPC::Core::ChannelCredentials object or specifically the ::this_channel_is_insecure symbol (if the latter is passed, an insecure connection will be used).

I'd note that the test that uses

def load_certs
  data_dir = "#{Rails.root}/certs"
  files = ['ca.pem', 'server.key', 'server.pem']
  files.map { |f| File.open(File.join(data_dir, f)).read }
end

might actually be misleading, since it only makes sense to construct channel credentials with the client's private key and certificate chain (that specific test I believe doesn't use the key and cert chain).

On the GRPC::Core::ChannelCredentials constructor:

There are three forms that can be used, (there's a comment above the constructor code in https://github.com/grpc/grpc/blob/master/src/ruby/ext/grpc/rb_channel_credentials.c#L128 that goes over them), but the options are:

  • Credentials.new()

  • Credentials.new(pem_root_certs)

  • Credentials.new(pem_root_certs, pem_private_key, pem_cert_chain)

In all cases, the roots file, private key, and cert chain parameters are pem encoded strings.

Note that if no arguments are passed (Credentials.new() is used), then the server root certificates will be found as described in this header comment (see the description for the behavior when server root certs parameter is null). And the last constructor is only needed if you want the client to use a private key and cert chain.

Upvotes: 4

Related Questions