diff options
author | Nicholas DiPiazza <nicholas.dipiazza@lucidworks.com> | 2018-02-06 19:33:32 -0600 |
---|---|---|
committer | Eric Anderson <ejona@google.com> | 2018-02-06 17:33:32 -0800 |
commit | 14ed692974c6b00ca91e966e8001f1cec307805a (patch) | |
tree | 6dc8ce146280d3ab5be9a63afc93c1e7316cdd9e /examples | |
parent | 512e55444e4176602f27fa39c41223e5b6074d59 (diff) | |
download | grpc-grpc-java-14ed692974c6b00ca91e966e8001f1cec307805a.tar.gz |
examples: Add a "hello-world" with TLS configured
Diffstat (limited to 'examples')
-rw-r--r-- | examples/BUILD.bazel | 28 | ||||
-rw-r--r-- | examples/README.md | 78 | ||||
-rw-r--r-- | examples/build.gradle | 20 | ||||
-rw-r--r-- | examples/pom.xml | 6 | ||||
-rw-r--r-- | examples/src/main/java/io/grpc/examples/helloworldtls/HelloWorldClientTls.java | 140 | ||||
-rw-r--r-- | examples/src/main/java/io/grpc/examples/helloworldtls/HelloWorldServerTls.java | 136 |
6 files changed, 401 insertions, 7 deletions
diff --git a/examples/BUILD.bazel b/examples/BUILD.bazel index eca28b134..9a97cc669 100644 --- a/examples/BUILD.bazel +++ b/examples/BUILD.bazel @@ -72,8 +72,10 @@ java_library( "@com_google_protobuf//:protobuf_java", "@com_google_protobuf//:protobuf_java_util", "@grpc_java//core", + "@grpc_java//netty", "@grpc_java//protobuf", "@grpc_java//stub", + "@io_netty_netty_handler//jar", ], ) @@ -83,7 +85,6 @@ java_binary( main_class = "io.grpc.examples.helloworld.HelloWorldClient", runtime_deps = [ ":examples", - "@grpc_java//netty", ], ) @@ -93,7 +94,6 @@ java_binary( main_class = "io.grpc.examples.helloworld.HelloWorldServer", runtime_deps = [ ":examples", - "@grpc_java//netty", ], ) @@ -103,7 +103,6 @@ java_binary( main_class = "io.grpc.examples.routeguide.RouteGuideClient", runtime_deps = [ ":examples", - "@grpc_java//netty", ], ) @@ -113,7 +112,6 @@ java_binary( main_class = "io.grpc.examples.routeguide.RouteGuideServer", runtime_deps = [ ":examples", - "@grpc_java//netty", ], ) @@ -123,7 +121,6 @@ java_binary( main_class = "io.grpc.examples.manualflowcontrol.ManualFlowControlClient", runtime_deps = [ ":examples", - "@grpc_java//netty", ], ) @@ -133,6 +130,25 @@ java_binary( main_class = "io.grpc.examples.manualflowcontrol.ManualFlowControlServer", runtime_deps = [ ":examples", - "@grpc_java//netty", + ], +) + +java_binary( + name = "hello-world-tls-client", + testonly = 1, + main_class = "io.grpc.examples.helloworldtls.HelloWorldClientTls", + runtime_deps = [ + ":examples", + "@io_netty_netty_tcnative_boringssl_static//jar", + ], +) + +java_binary( + name = "hello-world-tls-server", + testonly = 1, + main_class = "io.grpc.examples.helloworldtls.HelloWorldServerTls", + runtime_deps = [ + ":examples", + "@io_netty_netty_tcnative_boringssl_static//jar", ], ) diff --git a/examples/README.md b/examples/README.md index 051382ee1..97435086d 100644 --- a/examples/README.md +++ b/examples/README.md @@ -15,7 +15,8 @@ To build the examples, run in this directory: $ ./gradlew installDist ``` -This creates the scripts `hello-world-server`, `hello-world-client`, +This creates the scripts `hello-world-server`, `hello-world-client`, +`hello-world-tls-server`, `hello-world-tls-client`, `route-guide-server`, and `route-guide-client` in the `build/install/examples/bin/` directory that run the examples. Each example requires the server to be running before starting the client. @@ -32,6 +33,81 @@ And in a different terminal window run: $ ./build/install/examples/bin/hello-world-client ``` +### Hello World with TLS + +Running the hello world with TLS is the same as the normal hello world, but takes additional args: + +**hello-world-tls-server**: + +```text +USAGE: HelloWorldServerTls host port certChainFilePath privateKeyFilePath [clientCertChainFilePath] + Note: You only need to supply clientCertChainFilePath if you want to enable Mutual TLS. +``` + +**hello-world-tls-client**: + +```text +USAGE: HelloWorldClientTls host port [trustCertCollectionFilePath] [clientCertChainFilePath] [clientPrivateKeyFilePath] + Note: clientCertChainFilePath and clientPrivateKeyFilePath are only needed if mutual auth is desired. And if you specify clientCertChainFilePath you must also specify clientPrivateKeyFilePath +``` + +#### Generating self-signed certificates for use with grpc + +You can use the following script to generate self-signed certificates for grpc-java including the hello world with TLS examples: + +```bash +# Changes these CN's to match your hosts in your environment if needed. +SERVER_CN=localhost +CLIENT_CN=localhost # Used when doing mutual TLS + +echo Generate CA key: +openssl genrsa -passout pass:1111 -des3 -out ca.key 4096 +echo Generate CA certificate: +# Generates ca.crt which is the trustCertCollectionFile +openssl req -passin pass:1111 -new -x509 -days 365 -key ca.key -out ca.crt -subj "/CN=${SERVER_CN}" +echo Generate server key: +openssl genrsa -passout pass:1111 -des3 -out server.key 4096 +echo Generate server signing request: +openssl req -passin pass:1111 -new -key server.key -out server.csr -subj "/CN=${SERVER_CN}" +echo Self-signed server certificate: +# Generates server.crt which is the certChainFile for the server +openssl x509 -req -passin pass:1111 -days 365 -in server.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out server.crt +echo Remove passphrase from server key: +openssl rsa -passin pass:1111 -in server.key -out server.key +echo Generate client key +openssl genrsa -passout pass:1111 -des3 -out client.key 4096 +echo Generate client signing request: +openssl req -passin pass:1111 -new -key client.key -out client.csr -subj "/CN=${CLIENT_CN}" +echo Self-signed client certificate: +# Generates client.crt which is the clientCertChainFile for the client (need for mutual TLS only) +openssl x509 -passin pass:1111 -req -days 365 -in client.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out client.crt +echo Remove passphrase from client key: +openssl rsa -passin pass:1111 -in client.key -out client.key +echo Converting the private keys to X.509: +# Generates client.pem which is the clientPrivateKeyFile for the Client (needed for mutual TLS only) +openssl pkcs8 -topk8 -nocrypt -in client.key -out client.pem +# Generates server.pem which is the privateKeyFile for the Server +openssl pkcs8 -topk8 -nocrypt -in server.key -out server.pem +``` + +#### Hello world example with TLS (no mutual auth): + +```bash +# Server +./build/install/examples/bin/hello-world-server-tls mate 50440 ~/Downloads/sslcert/server.crt ~/Downloads/sslcert/server.pem +# Client +./build/install/examples/bin/hello-world-client-tls mate 50440 ~/Downloads/sslcert/ca.crt +``` + +#### Hello world example with TLS with mutual auth: + +```bash +# Server +./build/install/examples/bin/hello-world-server-tls mate 54440 ~/Downloads/sslcert/server.crt ~/Downloads/sslcert/server.pem ~/Downloads/sslcert/client.crt +# Client +./build/install/examples/bin/hello-world-client-tls mate 54440 ~/Downloads/sslcert/ca.crt ~/Downloads/sslcert/client.crt ~/Downloads/sslcert/client.pem +``` + That's it! Please refer to gRPC Java's [README](../README.md) and diff --git a/examples/build.gradle b/examples/build.gradle index e2c637932..10e1871ee 100644 --- a/examples/build.gradle +++ b/examples/build.gradle @@ -23,6 +23,7 @@ repositories { // Feel free to delete the comment at the next line. It is just for safely // updating the version in our release process. def grpcVersion = '1.11.0-SNAPSHOT' // CURRENT_GRPC_VERSION +def nettyTcNativeVersion = '2.0.7.Final' dependencies { compile "com.google.api.grpc:proto-google-common-protos:1.0.0" @@ -30,6 +31,9 @@ dependencies { compile "io.grpc:grpc-protobuf:${grpcVersion}" compile "io.grpc:grpc-stub:${grpcVersion}" + // Used for TLS in HelloWorldServerTls + compile "io.netty:netty-tcnative-boringssl-static:${nettyTcNativeVersion}" + testCompile "io.grpc:grpc-testing:${grpcVersion}" testCompile "junit:junit:4.12" testCompile "org.mockito:mockito-core:1.9.5" @@ -97,6 +101,20 @@ task helloWorldClient(type: CreateStartScripts) { classpath = jar.outputs.files + project.configurations.runtime } +task helloWorldTlsServer(type: CreateStartScripts) { + mainClassName = 'io.grpc.examples.helloworldtls.HelloWorldServerTls' + applicationName = 'hello-world-tls-server' + outputDir = new File(project.buildDir, 'tmp') + classpath = jar.outputs.files + project.configurations.runtime +} + +task helloWorldTlsClient(type: CreateStartScripts) { + mainClassName = 'io.grpc.examples.helloworldtls.HelloWorldClientTls' + applicationName = 'hello-world-tls-client' + outputDir = new File(project.buildDir, 'tmp') + classpath = jar.outputs.files + project.configurations.runtime +} + task compressingHelloWorldClient(type: CreateStartScripts) { mainClassName = 'io.grpc.examples.experimental.CompressingHelloWorldClient' applicationName = 'compressing-hello-world-client' @@ -109,6 +127,8 @@ applicationDistribution.into('bin') { from(routeGuideClient) from(helloWorldServer) from(helloWorldClient) + from(helloWorldTlsServer) + from(helloWorldTlsClient) from(compressingHelloWorldClient) fileMode = 0755 } diff --git a/examples/pom.xml b/examples/pom.xml index 836bd0769..3f0922b31 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -12,6 +12,7 @@ <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <grpc.version>1.11.0-SNAPSHOT</grpc.version><!-- CURRENT_GRPC_VERSION --> + <netty.tcnative.version>2.0.7.Final</netty.tcnative.version> </properties> <dependencies> <dependency> @@ -36,6 +37,11 @@ <scope>test</scope> </dependency> <dependency> + <groupId>io.netty</groupId> + <artifactId>netty-tcnative-boringssl-static</artifactId> + <version>${netty.tcnative.version}</version> + </dependency> + <dependency> <groupId>com.google.api.grpc</groupId> <artifactId>proto-google-common-protos</artifactId> <version>1.0.0</version> diff --git a/examples/src/main/java/io/grpc/examples/helloworldtls/HelloWorldClientTls.java b/examples/src/main/java/io/grpc/examples/helloworldtls/HelloWorldClientTls.java new file mode 100644 index 000000000..b335bd15d --- /dev/null +++ b/examples/src/main/java/io/grpc/examples/helloworldtls/HelloWorldClientTls.java @@ -0,0 +1,140 @@ +/* + * Copyright 2015, gRPC Authors All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.grpc.examples.helloworldtls; + +import io.grpc.ManagedChannel; +import io.grpc.StatusRuntimeException; +import io.grpc.examples.helloworld.GreeterGrpc; +import io.grpc.examples.helloworld.HelloReply; +import io.grpc.examples.helloworld.HelloRequest; +import io.grpc.examples.helloworld.HelloWorldServer; +import io.grpc.netty.GrpcSslContexts; +import io.grpc.netty.NegotiationType; +import io.grpc.netty.NettyChannelBuilder; +import io.netty.handler.ssl.SslContext; +import io.netty.handler.ssl.SslContextBuilder; + +import javax.net.ssl.SSLException; +import java.io.File; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * A simple client that requests a greeting from the {@link HelloWorldServer} with TLS. + */ +public class HelloWorldClientTls { + private static final Logger logger = Logger.getLogger(HelloWorldClientTls.class.getName()); + + private final ManagedChannel channel; + private final GreeterGrpc.GreeterBlockingStub blockingStub; + + private static SslContext buildSslContext(String trustCertCollectionFilePath, + String clientCertChainFilePath, + String clientPrivateKeyFilePath) throws SSLException { + SslContextBuilder builder = GrpcSslContexts.forClient(); + if (trustCertCollectionFilePath != null) { + builder.trustManager(new File(trustCertCollectionFilePath)); + } + if (clientCertChainFilePath != null && clientPrivateKeyFilePath != null) { + builder.keyManager(new File(clientCertChainFilePath), new File(clientPrivateKeyFilePath)); + } + return builder.build(); + } + + /** + * Construct client connecting to HelloWorld server at {@code host:port}. + */ + public HelloWorldClientTls(String host, + int port, + SslContext sslContext) throws SSLException { + + this(NettyChannelBuilder.forAddress(host, port) + .negotiationType(NegotiationType.TLS) + .sslContext(sslContext) + .build()); + } + + /** + * Construct client for accessing RouteGuide server using the existing channel. + */ + HelloWorldClientTls(ManagedChannel channel) { + this.channel = channel; + blockingStub = GreeterGrpc.newBlockingStub(channel); + } + + public void shutdown() throws InterruptedException { + channel.shutdown().awaitTermination(5, TimeUnit.SECONDS); + } + + /** + * Say hello to server. + */ + public void greet(String name) { + logger.info("Will try to greet " + name + " ..."); + HelloRequest request = HelloRequest.newBuilder().setName(name).build(); + HelloReply response; + try { + response = blockingStub.sayHello(request); + } catch (StatusRuntimeException e) { + logger.log(Level.WARNING, "RPC failed: {0}", e.getStatus()); + return; + } + logger.info("Greeting: " + response.getMessage()); + } + + /** + * Greet server. If provided, the first element of {@code args} is the name to use in the + * greeting. + */ + public static void main(String[] args) throws Exception { + + if (args.length < 2 || args.length == 4 || args.length > 5) { + System.out.println("USAGE: HelloWorldClientTls host port [trustCertCollectionFilePath] " + + "[clientCertChainFilePath] [clientPrivateKeyFilePath]\n Note: clientCertChainFilePath and " + + "clientPrivateKeyFilePath are only needed if mutual auth is desired. And if you specify " + + "clientCertChainFilePath you must also specify clientPrivateKeyFilePath"); + System.exit(0); + } + + HelloWorldClientTls client; + switch (args.length) { + case 2: + client = new HelloWorldClientTls(args[0], Integer.parseInt(args[1]), + buildSslContext(null, null, null)); + break; + case 3: + client = new HelloWorldClientTls(args[0], Integer.parseInt(args[1]), + buildSslContext(args[2], null, null)); + break; + default: + client = new HelloWorldClientTls(args[0], Integer.parseInt(args[1]), + buildSslContext(args[2], args[3], args[4])); + } + + try { + /* Access a service running on the local machine on port 50051 */ + String user = "world"; + if (args.length > 0) { + user = args[0]; /* Use the arg as the name to greet if provided */ + } + client.greet(user); + } finally { + client.shutdown(); + } + } +} diff --git a/examples/src/main/java/io/grpc/examples/helloworldtls/HelloWorldServerTls.java b/examples/src/main/java/io/grpc/examples/helloworldtls/HelloWorldServerTls.java new file mode 100644 index 000000000..47c663da9 --- /dev/null +++ b/examples/src/main/java/io/grpc/examples/helloworldtls/HelloWorldServerTls.java @@ -0,0 +1,136 @@ +/* + * Copyright 2015, gRPC Authors All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.grpc.examples.helloworldtls; + +import io.grpc.Server; +import io.grpc.examples.helloworld.GreeterGrpc; +import io.grpc.examples.helloworld.HelloReply; +import io.grpc.examples.helloworld.HelloRequest; +import io.grpc.netty.GrpcSslContexts; +import io.grpc.netty.NettyServerBuilder; +import io.grpc.stub.StreamObserver; +import io.netty.handler.ssl.ClientAuth; +import io.netty.handler.ssl.SslContextBuilder; +import io.netty.handler.ssl.SslProvider; + +import java.io.File; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.util.logging.Logger; + +/** + * Server that manages startup/shutdown of a {@code Greeter} server with TLS enabled. + */ +public class HelloWorldServerTls { + private static final Logger logger = Logger.getLogger(HelloWorldServerTls.class.getName()); + + private Server server; + + private final String host; + private final int port; + private final String certChainFilePath; + private final String privateKeyFilePath; + private final String clientCertChainFilePath; + + public HelloWorldServerTls(String host, + int port, + String certChainFilePath, + String privateKeyFilePath, + String clientCertChainFilePath) { + this.host = host; + this.port = port; + this.certChainFilePath = certChainFilePath; + this.privateKeyFilePath = privateKeyFilePath; + this.clientCertChainFilePath = clientCertChainFilePath; + } + + private SslContextBuilder getSslContextBuilder() { + SslContextBuilder sslClientContextBuilder = SslContextBuilder.forServer(new File(certChainFilePath), + new File(privateKeyFilePath)); + if (clientCertChainFilePath != null) { + sslClientContextBuilder.trustManager(new File(clientCertChainFilePath)); + sslClientContextBuilder.clientAuth(ClientAuth.OPTIONAL); + } + return GrpcSslContexts.configure(sslClientContextBuilder, + SslProvider.OPENSSL); + } + + private void start() throws IOException { + server = NettyServerBuilder.forAddress(new InetSocketAddress(host, port)) + .addService(new GreeterImpl()) + .sslContext(getSslContextBuilder().build()) + .build() + .start(); + logger.info("Server started, listening on " + port); + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() { + // Use stderr here since the logger may have been reset by its JVM shutdown hook. + System.err.println("*** shutting down gRPC server since JVM is shutting down"); + HelloWorldServerTls.this.stop(); + System.err.println("*** server shut down"); + } + }); + } + + private void stop() { + if (server != null) { + server.shutdown(); + } + } + + /** + * Await termination on the main thread since the grpc library uses daemon threads. + */ + private void blockUntilShutdown() throws InterruptedException { + if (server != null) { + server.awaitTermination(); + } + } + + /** + * Main launches the server from the command line. + */ + public static void main(String[] args) throws IOException, InterruptedException { + + if (args.length < 4 || args.length > 5) { + System.out.println( + "USAGE: HelloWorldServerTls host port certChainFilePath privateKeyFilePath " + + "[clientCertChainFilePath]\n Note: You only need to supply clientCertChainFilePath if you want " + + "to enable Mutual TLS."); + System.exit(0); + } + + final HelloWorldServerTls server = new HelloWorldServerTls(args[0], + Integer.parseInt(args[1]), + args[2], + args[3], + args.length == 5 ? args[4] : null); + server.start(); + server.blockUntilShutdown(); + } + + static class GreeterImpl extends GreeterGrpc.GreeterImplBase { + + @Override + public void sayHello(HelloRequest req, StreamObserver<HelloReply> responseObserver) { + HelloReply reply = HelloReply.newBuilder().setMessage("Hello " + req.getName()).build(); + responseObserver.onNext(reply); + responseObserver.onCompleted(); + } + } +} |