Ensure secure communication between private services.
Hey folks, welcome back! In this video we're going to look at how to secure our gRPC connections using TLS. There are three different types of connection in gRPC: there is insecure, which is what we've been using so far up to now in this course. This basically means that connection between the client and server is in plain text so it's not encrypted at all, and this is fine for simple examples but shouldn't be used in production.
Next we have server-side TLS, which is what we'll look at in this video. This is where communication is encrypted between the client and server, but only the server's identity is verified. The final one is MTLS, where communication between the client and server is encrypted but also both the server identity and the client identity is verified.
For today's video, we're going to take the hello world service that we've built in previous modules and adapt that to add TLS so the communication is secure. One thing we'll need is some certificates generated. In the Makefile, I've provided some make commands to generate all the certs that are required. I created this gen-certs command that calls three other commands in the Makefile.
The first one is gen-CA-certs, which will generate a certificate authority that basically acts as a third party to confirm the identities of all the parties involved. This will first make a cert directory in our root directory of our project, then create a certificate authority certificate and key, which we'll use to sign both the server and client certs. The next command is to generate the server certificates, creating the server cert and key just for localhost, which will then be signed by our certificate authority. Finally, we have the client cert command which does the same but for the client certs.
After running the gen-cert command, we can see we have all our client and server certs, as well as our certificate authority certs. Looking through this project, you'll notice that the code is just the same as what we've implemented before - it's just a very simple hello world handler that checks the name is not empty and returns a message in the response with the name from the request.
To enable TLS, we need to make changes on both the client and server. On the server side, we'll need to load in the TLS certs, and on the client side, we just need to configure the TLS config. We'll start by implementing the server side part of this. What we need to do before we create our gRPC server is load in our TLS certs using the credentials package in gRPC. This package is part of the Google gRPC library and provides a NewServerTLSFromFile function that takes a path to the cert file and the key file.
The function will load these files and create a TLS credentials object which we can then use when initializing our new server. We'll handle any potential "failed to load TLS credentials" errors. Once we have loaded the credentials successfully, we can use the server option called Creds.TLS, which takes a transport credentials object - exactly what our NewServerTLSFromFile function returns.
To prove this is working, we'll run the server first and then the client. We should get an error because the client is currently trying to communicate in plain text. You can see the server's up and running, which means these files have been loaded successfully. If they hadn't loaded or couldn't be found, the function would have returned an error and we would have errored out of our run function and closed down the server.
The implementation can differ depending on whether you're using a self-signed certificate, a private certificate authority, or a public one like Let's Encrypt. For this example, we're using a private one and self-signing our certificates. If we were using a public certificate authority, it would be simpler - we'd just use credentials.NewTLS function with a TLS config.
When using a private certificate authority, we need to load the CA certs and append them to our cert pool using the crypto/x509 package's NewCertPool function. After loading our CA cert file, we use certPool.AppendCertsFromPEM to add our certificate authority cert to the pool. This allows the client to use that CA to verify the certificate that is passed from the server.
Finally, we create our TLS credentials object using the credentials package's NewClientTLSFromCert function, passing our cert pool and an empty server name override string. We can then pass our TLS credentials into the dial option. After configuring both the client and server side, running the client again shows we get a successful response.
This is the easiest form of TLS because we don't require client certs in this example and we're not verifying client certs. In many use cases this will be sufficient, but there are some more secure use cases where MTLS is required to verify the identity of both the client and the server - that's what we'll look at in the next video.