Adding support for TLS connections for gRPC clients

This patch uses BoringSSL to negotiate TLS sessions under the hood.

Change-Id: I1495479ff33654f9cffe04d61f293c3e923b8aeb
diff --git a/core/store/dist/BUILD b/core/store/dist/BUILD
index e3e702c..fb42f8d 100644
--- a/core/store/dist/BUILD
+++ b/core/store/dist/BUILD
@@ -3,10 +3,10 @@
     "//utils/rest:onlab-rest",
     "//core/store/serializers:onos-core-serializers",
     "@io_netty_netty_transport//jar",
-    "@io_netty_netty_codec//jar",
-    "@io_netty_netty_handler//jar",
     "@io_netty_netty_transport_native_epoll//jar",
     "@io_netty_netty_transport_native_unix_common//jar",
+    "@io_netty_netty_codec//jar",
+    "@io_netty_netty_handler//jar",
     "@io_netty_netty_resolver//jar",
     "@commons_math3//jar",
 ]
diff --git a/lib/deps.json b/lib/deps.json
index 06fedf4..1535dc7 100644
--- a/lib/deps.json
+++ b/lib/deps.json
@@ -240,6 +240,7 @@
     "io_netty_netty_codec-http2": "mvn:io.netty:netty-codec-http2:4.1.32.Final",
     "io_netty_netty_codec-http": "mvn:io.netty:netty-codec-http:4.1.32.Final",
     "io_netty_netty_codec-socks": "mvn:io.netty:netty-codec-socks:4.1.32.Final",
+    "io_netty_netty_tcnative_boringssl": "mvn:io.netty:netty-tcnative-boringssl-static:2.0.20.Final",
     "objenesis": "mvn:org.objenesis:objenesis:2.6",
     "openflowj": "mvn:org.onosproject:openflowj:3.2.1.onos",
     "org.osgi.util.function": "mvn:org.osgi:org.osgi.util.function:1.1.0",
diff --git a/protocols/grpc/ctl/BUILD b/protocols/grpc/ctl/BUILD
index 11f98c0..a467398 100644
--- a/protocols/grpc/ctl/BUILD
+++ b/protocols/grpc/ctl/BUILD
@@ -6,6 +6,7 @@
     "@io_grpc_grpc_java//protobuf-lite",
     "@com_google_protobuf//:protobuf_java",
     "@com_google_api_grpc_proto_google_common_protos//jar",
+    "@io_netty_netty_handler//jar",
 ]
 
 osgi_jar(
diff --git a/protocols/grpc/ctl/src/main/java/org/onosproject/grpc/ctl/AbstractGrpcClientController.java b/protocols/grpc/ctl/src/main/java/org/onosproject/grpc/ctl/AbstractGrpcClientController.java
index dc7ae9d..c0a3c6b 100644
--- a/protocols/grpc/ctl/src/main/java/org/onosproject/grpc/ctl/AbstractGrpcClientController.java
+++ b/protocols/grpc/ctl/src/main/java/org/onosproject/grpc/ctl/AbstractGrpcClientController.java
@@ -19,8 +19,11 @@
 import com.google.common.collect.Maps;
 import com.google.common.util.concurrent.Striped;
 import io.grpc.ManagedChannel;
-import io.grpc.ManagedChannelBuilder;
+import io.grpc.netty.GrpcSslContexts;
 import io.grpc.netty.NettyChannelBuilder;
+import io.netty.handler.ssl.NotSslRecordException;
+import io.netty.handler.ssl.SslContext;
+import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
 import org.onosproject.event.AbstractListenerManager;
 import org.onosproject.event.Event;
 import org.onosproject.event.EventListener;
@@ -36,11 +39,13 @@
 import org.osgi.service.component.annotations.ReferenceCardinality;
 import org.slf4j.Logger;
 
+import javax.net.ssl.SSLException;
 import java.util.Map;
 import java.util.concurrent.locks.Lock;
 import java.util.function.Supplier;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
 import static java.lang.String.format;
 import static org.slf4j.LoggerFactory.getLogger;
 
@@ -91,11 +96,15 @@
     @Override
     public boolean createClient(K clientKey) {
         checkNotNull(clientKey);
-        return withDeviceLock(() -> doCreateClient(clientKey), clientKey.deviceId());
+        /*
+            FIXME we might want to move "useTls" and "fallback" to properties of the netcfg and clientKey
+                  For now, we will first try to connect with TLS (accepting any cert), then fall back to
+                  plaintext for every device
+         */
+        return withDeviceLock(() -> doCreateClient(clientKey, true, true), clientKey.deviceId());
     }
 
-
-    private boolean doCreateClient(K clientKey) {
+    private boolean doCreateClient(K clientKey, boolean useTls, boolean fallbackToPlainText) {
         DeviceId deviceId = clientKey.deviceId();
         String serverAddr = clientKey.serverAddr();
         int serverPort = clientKey.serverPort();
@@ -113,18 +122,60 @@
             }
         }
 
-        log.info("Creating client for {} (server={}:{})...",
-                deviceId, serverAddr, serverPort);
+        log.info("Creating client for {} (server={}:{})...", deviceId, serverAddr, serverPort);
+
+        SslContext sslContext = null;
+        if (useTls) {
+            try {
+                // Accept any server certificate; this is insecure and should not be used in production
+                sslContext = GrpcSslContexts.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build();
+            } catch (SSLException e) {
+                log.error("Failed to build SSL Context", e);
+                return false;
+            }
+        }
+
         GrpcChannelId channelId = GrpcChannelId.of(clientKey.toString());
-        ManagedChannelBuilder channelBuilder = NettyChannelBuilder
+        NettyChannelBuilder channelBuilder = NettyChannelBuilder
                 .forAddress(serverAddr, serverPort)
-                .maxInboundMessageSize(DEFAULT_MAX_INBOUND_MSG_SIZE * MEGABYTES)
-                .usePlaintext();
+                .maxInboundMessageSize(DEFAULT_MAX_INBOUND_MSG_SIZE * MEGABYTES);
+        if (sslContext != null) {
+            log.debug("Using SSL for gRPC connection to {}", deviceId);
+            channelBuilder
+                    .sslContext(sslContext)
+                    .useTransportSecurity();
+        } else {
+            checkState(!useTls,
+                    "Not authorized to use plaintext for gRPC connection to {}", deviceId);
+            log.debug("Using plaintext TCP for gRPC connection to {}", deviceId);
+            channelBuilder.usePlaintext();
+        }
 
         final ManagedChannel channel;
 
-        channel = grpcChannelController.connectChannel(channelId, channelBuilder);
-
+        try {
+            channel = grpcChannelController.connectChannel(channelId, channelBuilder);
+        } catch (Throwable e) {
+            for (Throwable cause = e; cause != null; cause = cause.getCause()) {
+                if (useTls && cause instanceof NotSslRecordException) {
+                    // Likely root cause is that server is using plaintext
+                    log.info("Failed to connect to server (device={}) using TLS", deviceId);
+                    log.debug("TLS connection exception", e);
+                    if (fallbackToPlainText) {
+                        log.info("Falling back to plaintext for connection to {}", deviceId);
+                        return doCreateClient(clientKey, false, false);
+                    }
+                }
+                if (!useTls && "Connection reset by peer".equals(cause.getMessage())) {
+                    // Not a great signal, but could indicate the server is expected a TLS connection
+                    log.error("Failed to connect to server (device={}) using plaintext TCP; is the server using TLS?",
+                            deviceId);
+                    break;
+                }
+            }
+            log.warn("Unable to connect to gRPC server for {}", deviceId, e);
+            return false;
+        }
 
         final C client;
         try {
diff --git a/tools/build/bazel/generate_workspace.bzl b/tools/build/bazel/generate_workspace.bzl
index d9ceb5d..edd8a31 100644
--- a/tools/build/bazel/generate_workspace.bzl
+++ b/tools/build/bazel/generate_workspace.bzl
@@ -1,4 +1,4 @@
-# ***** This file was auto-generated at Thu, 14 Feb 2019 20:57:40 GMT. Do not edit this file manually. *****
+# ***** This file was auto-generated at Sat, 23 Feb 2019 01:30:12 GMT. Do not edit this file manually. *****
 # ***** Use onos-lib-gen *****
 
 load("//tools/build/bazel:variables.bzl", "ONOS_GROUP_ID", "ONOS_VERSION")
@@ -802,6 +802,12 @@
             jar_sha256 = "fe2f2e97d6c65dc280623dcfd24337d8a5c7377049c120842f2c59fb83d7408a",
             licenses = ["notice"],
             jar_urls = ["http://repo1.maven.org/maven2/io/netty/netty-codec-socks/4.1.32.Final/netty-codec-socks-4.1.32.Final.jar"],        )
+    if "io_netty_netty_tcnative_boringssl" not in native.existing_rules():
+        java_import_external(
+            name = "io_netty_netty_tcnative_boringssl",
+            jar_sha256 = "c0bbfcb116ae9928ebb17cbfbdd80ee51980ad228a4fffb0cb3137ac91b1bc09",
+            licenses = ["notice"],
+            jar_urls = ["http://repo1.maven.org/maven2/io/netty/netty-tcnative-boringssl-static/2.0.20.Final/netty-tcnative-boringssl-static-2.0.20.Final.jar"],        )
     if "objenesis" not in native.existing_rules():
         java_import_external(
             name = "objenesis",
@@ -1390,6 +1396,7 @@
 artifact_map["@io_netty_netty_codec_http2//:io_netty_netty_codec_http2"] = "mvn:io.netty:netty-codec-http2:jar:4.1.32.Final"
 artifact_map["@io_netty_netty_codec_http//:io_netty_netty_codec_http"] = "mvn:io.netty:netty-codec-http:jar:4.1.32.Final"
 artifact_map["@io_netty_netty_codec_socks//:io_netty_netty_codec_socks"] = "mvn:io.netty:netty-codec-socks:jar:4.1.32.Final"
+artifact_map["@io_netty_netty_tcnative_boringssl//:io_netty_netty_tcnative_boringssl"] = "mvn:io.netty:netty-tcnative-boringssl-static:jar:2.0.20.Final"
 artifact_map["@objenesis//:objenesis"] = "mvn:org.objenesis:objenesis:jar:2.6"
 artifact_map["@openflowj//:openflowj"] = "mvn:org.onosproject:openflowj:jar:3.2.1.onos"
 artifact_map["@org_osgi_util_function//:org_osgi_util_function"] = "mvn:org.osgi:org.osgi.util.function:jar:1.1.0"
diff --git a/tools/package/etc/org.apache.karaf.features.cfg b/tools/package/etc/org.apache.karaf.features.cfg
index 515c2a8..ce01413 100644
--- a/tools/package/etc/org.apache.karaf.features.cfg
+++ b/tools/package/etc/org.apache.karaf.features.cfg
@@ -52,8 +52,8 @@
     webconsole/4.2.3, \
     scr/4.2.3, \
     war/4.2.3), \
-    (onos-netty/$ONOS_VERSION, \
-    onos-api/$ONOS_VERSION, \
+    (onos-thirdparty-base/$ONOS_VERSION), \
+    (onos-api/$ONOS_VERSION, \
     onos-core/$ONOS_VERSION, \
     onos-cli/$ONOS_VERSION, \
     onos-rest/$ONOS_VERSION, \
diff --git a/tools/package/features/BUILD b/tools/package/features/BUILD
index 6f3532c4..382bee5 100644
--- a/tools/package/features/BUILD
+++ b/tools/package/features/BUILD
@@ -20,13 +20,14 @@
         "@io_netty_netty//jar",
         "@io_netty_netty_common//jar",
         "@io_netty_netty_buffer//jar",
-        "@io_netty_netty_transport//jar",
         "@io_netty_netty_handler//jar",
+        "@io_netty_netty_tcnative_boringssl//jar",
         "@io_netty_netty_codec//jar",
         "@io_netty_netty_codec_http//jar",
         "@io_netty_netty_codec_http2//jar",
         "@io_netty_netty_codec_socks//jar",
         "@io_netty_netty_handler_proxy//jar",
+        "@io_netty_netty_transport//jar",
         "@io_netty_netty_transport_native_epoll//jar",
         "@io_netty_netty_transport_native_unix_common//jar",
         "@io_netty_netty_resolver//jar",