3

When using gRPC over plain TCP the client establishes a channel with the server like this (in ruby):

stub = Helloworld::Greeter::Stub.new(service_url, :this_channel_is_insecure)

but then, when I implement TLS on the server and put in my LetsEncrypt certificate on the server, the client must establish a secure connection like this (in ruby):

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

That code with the comment is taken from the official gRPC docs.

My question is, why does the client need a certificate here? I thought that it was the server that needed a certificate, and the client makes sure it's valid. When my browser connects to secure web sites, does it bring it's own certificate to the table? If not, why does the gRPC client need one?

From my reading the client knows about some trusted authorities, to one of which the server's certificate chain links. And the comment in the code above refers to a "CA roots file" Which might be the certificate of the authority at the top of the chain. So maybe the gRPC client compares the certificate at the root of the server's chain to it's own - but if that's the case, what happens if it gets the wrong CA?

All of the docs and posts explaining how to establish a secure connection from a gRPC client say to read the certificate from a local file, but none of them say what that certificate is, or where it comes from.

What am I missing?

EDIT:

I discovered on my computer a directory with a bunch of what appears to be CA root certificates. /etc/ssl/certs/ One of them appear to be the authority that verifies LetsEncrypt, which I use on the server, so I tried reading that certificate in the client like this:

GRPC::Core::ChannelCredentials.new(File.read('/etc/ssl/certs/ISRG_Root_X1.pem'))

but it only resulted in this error

Handshake failed with fatal error SSL_ERROR_SSL: error:1000007d:SSL routines:OPENSSL_internal:CERTIFICATE_VERIFY_FAILED.

Toby 1 Kenobi
  • 247
  • 2
  • 12
  • Does the host element of `service_url` match your Let's Encrypt _Subject Name_ or one of the _Subject Alternative Name_ entries? – garethTheRed May 22 '19 at 08:16
  • @garethTheRed good question! I just checked and the certificate is not covering my subdomain! – Toby 1 Kenobi May 22 '19 at 12:08
  • Then that may well explain your error. If you're on a *nix device you can add a local name to IP resolution in `/etc/hosts` for testing. On Windows, it's `C:\Windows\System32\drivers\etc\hosts`. – garethTheRed May 22 '19 at 12:20

1 Answers1

3

gRPC is primarily intended for connecting services by calling remote procedures, e.g. for microservices. In contrast to the unilateral trust relationship between a web server and multiple browser clients, both partners involved must explicitly trust each other to avoid man-in-the-middle attacks. gRPC enforces this by design for TLS-secured connections.

In case of an insecure (no TLS-secured) connection (intended for testing purposes only), for the gRPC server the parameter :this_port_is_insecure is passed to the (GRPC::RpcServer.new).add_http2_port method and for every involved gRPC client the parameter channel_args: :this_channel_is_insecure is passed to the Core::Stub.new method.

In contrast, in case of an secured connection via TLS, GRPC::Core::ServerCredentials.new client_ca_pem, [{private_key: server_key_pem, cert_chain: server_cert_pem}], true must be passed for the gRPC server and GRPC::Core::ChannelCredentials.new server_ca_pem, client_key_pem, client_cert_pem must be passed for every client.

For all *_pem variables load the content via File.read from the respective PEM files, given by your trust center or maybe self generated before:

  • server_ca_pem is the certificate authority who has signed the server_cert_pem
  • server_cert_pem is the server certificate chain, proved by the client via server_ca_pem
  • server_key_pem is the private key of the server
  • client_ca_pem is the certificate authority who has signed the client_cert_pem
  • client_cert_pem is the client certificate chain, proved by the server via client_ca_pem
  • client_key_pem is the private key of the client

server_ca_pem and client_ca_pem may or may not be the same. Use additional GRPC::Core::CallCredentials if you need to secure the service-client relationship at call level.

gRPC Authentication Guide:

https://grpc.io/docs/guides/auth/

Ruby code examples:

https://github.com/grpc/grpc/blob/master/src/ruby/spec/channel_credentials_spec.rb

SkatEddy
  • 146
  • 1
  • 1
  • 3
  • 1
    "In contrast to the unilateral trust relationship between a web server and multiple browser clients, both partners involved must explicitly trust each other to avoid man-in-the-middle attacks." There are other ways to ensure the client identity, for example, creating a login method taking a username and password. Managing client certificates is a pain. – anti_ml Jul 29 '20 at 13:11