diff --git a/docs/src/main/tut/ssl-tls.md b/docs/src/main/tut/ssl-tls.md index 1e6063cd3..07a7669ee 100644 --- a/docs/src/main/tut/ssl-tls.md +++ b/docs/src/main/tut/ssl-tls.md @@ -18,11 +18,150 @@ As we mentioned in the [Quickstart](/docs/rpc/quickstart) section, we can choose On the server and client side, we will need two files to configure the `SslContext` in `gRPC`: -* Server certificate file: Small data files that digitally bind a cryptographic key to an organization’s details. This file could be generated or obtained from a third company. +* Server/Client certificate file: Small data files that digitally bind a cryptographic key to an organization’s details. This file could be generated or obtained from a third company. -* Server private key file: The private key is a separate file that is used in the encryption of data sent between your server and the clients. All SSL certificates require a private key to work. +* Server/Client private key file: The private key is a separate file that is used in the encryption of data sent between your server and the clients. All SSL certificates require a private key to work. ## Usage -The first step to do, in order to start to secure our service is use `frees-rpc-netty-ssl` and `frees-rpc-client-netty`. +The first step to do, in order to start to secure ours `freestyle-rpc` services, is use the artifacts `frees-rpc-netty-ssl` and `frees-rpc-client-netty` in our `build.sbt`. +In second place, we will have to move to our `resources` folder both server/client certificates and private keys. + +In our tests, the certificates have been extracted from [here](https://github.com/grpc/grpc-java/tree/master/testing/src/main/resources/certs). It could be a good point to start to check that our application works meanwhile we generate or obtain our own certificates. + +Thirdly, let's see a piece of code where we will explain line by line, what we are doing on the server side. + +We are going to ignore the explanations related to create the `RPCService`, `ServerRPCService` and runtime implicits. If you have any doubt about it, please, take a look at the [Patterns](/docs/rpc/patterns) section. + +```tut:silent +import java.io.File +import java.security.cert.X509Certificate + +import cats.effect.Effect +import freestyle.rpc.common._ +import freestyle.rpc.protocol._ +import freestyle.rpc.server.netty.SetSslContext +import freestyle.rpc.server.{AddService, GrpcConfig, ServerW} +import io.grpc.internal.testing.TestUtils +import io.grpc.netty.GrpcSslContexts +import io.netty.handler.ssl.{ClientAuth, SslContext, SslProvider} + +object service { + + @service + trait RPCService[F[_]] { + @rpc(Avro) def unary(a: A): F[C] + } + +} + +object handlers { + + class ServerRPCService[F[_]: Effect] extends RPCService[F] { + def unary(a: A): F[C] = Effect[F].delay(c1) + } + +} + +trait Runtime { + + import service._ + import handlers._ + + implicit val freesRPCHandler: ServerRPCService[ConcurrentMonad] = + new ServerRPCService[ConcurrentMonad] + + // First of all, we have to load the certs into files. These files has to be locally in our + // module in the resources folder. + + val serverCertFile: File = TestUtils.loadCert("server1.pem") + val serverPrivateKeyFile: File = TestUtils.loadCert("server1.key") + val serverTrustedCaCerts: Array[X509Certificate] = Array(TestUtils.loadX509Cert("ca.pem")) + + // We have to build the SslContext passing our server certificates, configuring the OpenSSL + // and requiring the client auth. + + val serverSslContext: SslContext = + GrpcSslContexts + .configure( + GrpcSslContexts.forServer(serverCertFile, serverPrivateKeyFile), + SslProvider.OPENSSL) + .trustManager(serverTrustedCaCerts: _*) + .clientAuth(ClientAuth.REQUIRE) + .build() + + // Adding to the GrpConfig our SslContext with SetSslContext, when we will create implicitly our + // server with ServerW.netty, our server will require SSL encryption in the client side. + + val grpcConfigs: List[GrpcConfig] = List( + SetSslContext(serverSslContext), + AddService(RPCService.bindService[ConcurrentMonad]) + ) + + // Important. We have to create the server with Netty. OkHttp is not supported for the Ssl + // encryption in frees-rpc. + + implicit val serverW: ServerW = ServerW.netty(SC.port, grpcConfigs) + +} + +object implicits extends Runtime + +``` + + +Lastly, as we did before with the server side, let's see what happens on the client side. + +```tut:silent + +import freestyle.rpc.client.OverrideAuthority +import freestyle.rpc.client.netty.{ + NettyChannelInterpreter, + NettyNegotiationType, + NettySslContext, + NettyUsePlaintext +} + +object client { + + // First of all, we have to load the certs into files. + + val clientCertChainFile: File = TestUtils.loadCert("client.pem") + val clientPrivateKeyFile: File = TestUtils.loadCert("client.key") + val clientTrustedCaCerts: Array[X509Certificate] = Array(TestUtils.loadX509Cert("ca.pem")) + + // We have to create the SslContext for the client like as we did in the server. + + val clientSslContext: SslContext = + GrpcSslContexts.forClient + .keyManager(clientCertChainFile, clientPrivateKeyFile) + .trustManager(clientTrustedCaCerts: _*) + .build() +} + +object MainApp { + + import client._ + + // Important, the channel interpreter have to be NettyChannelInterpreter. + + // In this case, we are creating the channel interpreter with a specifics ManagedChannelConfig + // These configs allow us encrypted the connection with the server. + + val channelInterpreter: NettyChannelInterpreter = new NettyChannelInterpreter( + ChannelForAddress("localhost", "8080"), + List( + OverrideAuthority(TestUtils.TEST_SERVER_HOST), + NettyUsePlaintext(false), + NettyNegotiationType(NegotiationType.TLS), + NettySslContext(clientSslContext) + ) + ) + + val freesRPCServiceClient: RPCService.Client[ConcurrentMonad] = + RPCService.clientFromChannel[ConcurrentMonad](channelInterpreter.build) + +} + +``` \ No newline at end of file