How to setup and test TLS in gRPC/gRPC-Web
Tags: testing, programming, grpc, api
Published: 2022-08-29 19:54:41

This article has samples in Node.JS but can be helpful for other languages, so lets start. All working examples you can find [here](https://github.com/getezy/ezy/tree/master/__tests__/tls-service).

---

## How TLS works?
I recommend to read the best comic-article about [how TLS works](https://howhttps.works/).

## gRPC connection types
There are three types for gRPC connections you can use:

1. Insecure — all data transmitted without encryption.
2. Server-Side TLS — browser like encryption, where only the server provides TLS certificate to the client.
3. Mutual TLS — most secure, both the server and the client provides there certificates to each other.

---

## Creating Self-Signed certificates
This is optional step if you don’t have certificate from any CA’s or want to work with TLS on localhost.

_Note that in production environment is mostly recommended to choose CA’s certificate for more security, for example you can get free certificate from [Let’s Encrypt](https://letsencrypt.org/)._

{% embed https://gist.github.com/notmedia/c508048fa3526f5713914cf3bfffbff1 %}

---

## Preparation
Let’s start from implementing our gRPC service.

First, we will define our service [Protocol Buffers](https://developers.google.com/protocol-buffers) file.

{% embed https://gist.github.com/notmedia/8fb4c1ca087950900fb38cf85c934978 %}

Second, we need to generate types, service and client definitions.
For TypeScript I prefer to use [ts-proto](https://github.com/stephenh/ts-proto), but you can choose any tool you like depends on your language.

```bash
protoc --plugin=./node_modules/.bin/protoc-gen-ts_proto --ts_proto_opt=env=node,outputServices=grpc-js --ts_proto_out=./src/generated ./proto/tls_service.proto
```

Now we can implement our server running on Node.js.

---

## gRPC
We will use an official [@grpc/grpc-js](https://github.com/grpc/grpc-node/tree/master/packages/grpc-js) package both for server and client side here.

---

### Server-Side TLS
Server-Side TLS requires only the server certificate and it’s private key.

**Server**
{% embed https://gist.github.com/notmedia/25bb4d183a5c62084e0b9d795585f75e %}

**Client**

Now we can implement the client side.

_Note here that if your TLS certificate signed with CA (not Self-Signed) you don’t need to provide this certificate on the client, it should automatically works._

{% embed https://gist.github.com/notmedia/929bacbab8a5e9afce6087bc91b287a4 %}

---

### Mutual TLS
Mutual TLS requires the root certificate, server certificate and it’s private key.
Root certificate would be used here for checking that client certificate is signed and the server can trust to the client.

**Server**

Like in Server-Side section, changed only `getServerCredentials` function.

{% embed https://gist.github.com/notmedia/70e2f2e3dc073be26c40912929d5a70b %}

**Client**

Like in Server-Side section, changed only `getChannelCredentials` function.

{% embed https://gist.github.com/notmedia/9af957f2109887723cb5ee4330f2c64d %}

---

### Override SSL target name
@grpc/grpc-js package additionally provides some useful [channel options](https://github.com/grpc/grpc-node/tree/master/packages/grpc-js#supported-channel-options) that you can set. You can read about each of them [here](https://grpc.github.io/grpc/core/group__grpc__arg__keys.html).

`grpc.ssl_target_name_override` — would be helpful for us when the actual server behind the proxy and CN don’t match.

To setup channel options you need to pass them into client constructor’s third argument.

---

## gRPC-Web

Read the grpc.io blog post about [the state of gRPC-Web](https://grpc.io/blog/state-of-grpc-web/).

TL;DR
The things you should know:

1. Working scheme:
Client ↔ Proxy [HTTP(S) gRPC-Web] ↔ Server (gRPC)

2. There are two implementations — [official gRPC-Web](https://github.com/grpc/grpc-web) and [@improbable-eng/grpc-web](https://github.com/improbable-eng/grpc-web).

3. Right now gRPC-Web supports only Unary and Server-Streaming requests over HTTP(S).  
_Additionally [@improbable-eng/grpc-web](https://github.com/improbable-eng/grpc-web) supports client-side and bi-directional streaming with an experimental websocket transport. This is not part of the gRPC-Web spec, and is not recommended for production use._

4. There are two proxies — Envoy with [gRPC-Web filter](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/grpc_web_filter) from official gRPC-Web and [grpcwebproxy](https://github.com/improbable-eng/grpc-web/tree/master/go/grpcwebproxy) from @improbable-eng.

5. You can use either client with either proxy.

6. Clients have different communication transports.  
_Official gRPC-web supports only XMLHttpRequest.
@improbable-eng/grpc-web additionally support Fetch (using it if available) and can be extended with custom transport, for example Node.js._

In this example we will use Envoy proxy running in docker.

---

### Server-Side TLS

**Server**

As you already noticed we need to start our gRPC service behind the proxy. So there is nothing to change, simply start service with Server-Side TLS we discuss earlier.

After that we need to setup envoy and start it.

{% embed https://gist.github.com/notmedia/af09455c770f283908b59e7da068c415 %}

{% embed https://gist.github.com/notmedia/13591d52db1c04152381618ad0af6369 %}

```bash
docker-compose up envoy-server
```

That’s it. Your gRPC-Web proxy with Server-Side TLS will be available at `https://0.0.0.0:8080`.

### Mutual TLS
**Server**

The hack here is to start gRPC service with **Server-Side TLS** but on envoy side check the client certificate signed by trusted CA.

{% embed https://gist.github.com/notmedia/1f1b6190b2f0fe3a087f4c5a62d16d29 %}

{% embed https://gist.github.com/notmedia/c00d9068aa33045600b9ebe022790e58 %}

```bash
docker-compose up envoy-mutual
```

Great! Your gRPC-Web proxy with Mutual TLS will be available at `https://0.0.0.0:8080`.

---

## Testing both gRPC and gRPC-Web requests

Recently I released a multi-platform desktop gRPC / gRPC-Web client called [ezy](https://github.com/getezy/ezy). I’m working with gRPC every day and there is no any fully featured, UI/UX-perfect clients for gRPC testing, so I tried to create one.

{% embed https://vimeo.com/744325213 %}

This client has fully featured support of gRPC / gRPC-Web. I would be appreciate if you try it.

🙏 If you have any feedback or ideas feel free to open [discussion](https://github.com/getezy/ezy/discussions).