You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by sp...@apache.org on 2015/06/04 14:37:07 UTC

[2/6] incubator-tinkerpop git commit: Simplified the SSL configuration for Gremlin Server.

Simplified the SSL configuration for Gremlin Server.


Project: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/commit/21033372
Tree: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/tree/21033372
Diff: http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/diff/21033372

Branch: refs/heads/master
Commit: 21033372db8535eef89e7e42a5595ec7cda19c50
Parents: 3027d6f
Author: Stephen Mallette <sp...@genoprime.com>
Authored: Thu Jun 4 07:50:40 2015 -0400
Committer: Stephen Mallette <sp...@genoprime.com>
Committed: Thu Jun 4 07:51:05 2015 -0400

----------------------------------------------------------------------
 .../gremlin/server/AbstractChannelizer.java     | 90 ++++++++++----------
 .../tinkerpop/gremlin/server/Settings.java      | 35 ++++++--
 2 files changed, 73 insertions(+), 52 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/21033372/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/AbstractChannelizer.java
----------------------------------------------------------------------
diff --git a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/AbstractChannelizer.java b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/AbstractChannelizer.java
index baa4551..2970dac 100644
--- a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/AbstractChannelizer.java
+++ b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/AbstractChannelizer.java
@@ -18,6 +18,13 @@
  */
 package org.apache.tinkerpop.gremlin.server;
 
+import io.netty.handler.codec.spdy.SpdyOrHttpChooser;
+import io.netty.handler.ssl.ApplicationProtocolConfig;
+import io.netty.handler.ssl.SslContext;
+import io.netty.handler.ssl.SslContextBuilder;
+import io.netty.handler.ssl.SslProvider;
+import io.netty.handler.ssl.util.SelfSignedCertificate;
+import org.apache.ivy.util.FileUtil;
 import org.apache.tinkerpop.gremlin.driver.MessageSerializer;
 import org.apache.tinkerpop.gremlin.groovy.engine.GremlinExecutor;
 import org.apache.tinkerpop.gremlin.server.handler.IteratorHandler;
@@ -26,19 +33,19 @@ import org.apache.tinkerpop.gremlin.server.handler.OpSelectorHandler;
 import io.netty.channel.ChannelInitializer;
 import io.netty.channel.ChannelPipeline;
 import io.netty.channel.socket.SocketChannel;
-import io.netty.handler.ssl.SslHandler;
 import org.javatuples.Pair;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import javax.net.ssl.KeyManagerFactory;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLEngine;
-import javax.net.ssl.TrustManager;
+import javax.net.ssl.SSLException;
 import javax.net.ssl.TrustManagerFactory;
+import java.io.File;
 import java.io.FileInputStream;
 import java.io.InputStream;
+import java.nio.file.Files;
 import java.security.KeyStore;
+import java.security.cert.CertificateException;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Optional;
@@ -61,7 +68,7 @@ public abstract class AbstractChannelizer extends ChannelInitializer<SocketChann
     private static final Logger logger = LoggerFactory.getLogger(AbstractChannelizer.class);
     protected Settings settings;
     protected GremlinExecutor gremlinExecutor;
-    protected Optional<SSLEngine> sslEngine;
+    protected Optional<SslContext> sslContext;
     protected Graphs graphs;
     protected ExecutorService gremlinExecutorService;
     protected ScheduledExecutorService scheduledExecutorService;
@@ -105,7 +112,10 @@ public abstract class AbstractChannelizer extends ChannelInitializer<SocketChann
         // and fail the server startup
         configureSerializers();
 
-        this.sslEngine = settings.optionalSsl().isPresent() && settings.ssl.enabled ? Optional.ofNullable(createSslEngine()) : Optional.empty();
+        // configure ssl if present
+        sslContext = settings.optionalSsl().isPresent() && settings.ssl.enabled ?
+                Optional.ofNullable(createSSLContext(settings)) : Optional.empty();
+        if (sslContext.isPresent()) logger.info("SSL enabled");
 
         // these handlers don't share any state and can thus be initialized once per pipeline
         this.opSelectorHandler = new OpSelectorHandler(settings, graphs, gremlinExecutor, scheduledExecutorService);
@@ -117,7 +127,7 @@ public abstract class AbstractChannelizer extends ChannelInitializer<SocketChann
     public void initChannel(final SocketChannel ch) throws Exception {
         final ChannelPipeline pipeline = ch.pipeline();
 
-        sslEngine.ifPresent(ssl -> pipeline.addLast(PIPELINE_SSL, new SslHandler(ssl)));
+        if (sslContext.isPresent()) pipeline.addLast(PIPELINE_SSL, sslContext.get().newHandler(ch.alloc()));
 
         // the implementation provides the method by which Gremlin Server will process requests.  the end of the
         // pipeline must decode to an incoming RequestMessage instances and encode to a outgoing ResponseMessage
@@ -155,7 +165,7 @@ public abstract class AbstractChannelizer extends ChannelInitializer<SocketChann
         }).filter(Optional::isPresent).map(Optional::get).flatMap(serializer ->
                         Stream.of(serializer.mimeTypesSupported()).map(mimeType -> Pair.with(mimeType, serializer))
         ).forEach(pair -> {
-            final String mimeType = pair.getValue0().toString();
+            final String mimeType = pair.getValue0();
             final MessageSerializer serializer = pair.getValue1();
             if (serializers.containsKey(mimeType))
                 logger.warn("{} already has {} configured.  It will not be replaced by {}. Check configuration for serializer duplication or other issues.",
@@ -172,46 +182,40 @@ public abstract class AbstractChannelizer extends ChannelInitializer<SocketChann
         }
     }
 
-    private SSLEngine createSslEngine() {
-        try {
-            logger.info("SSL was enabled.  Initializing SSLEngine instance...");
-            final SSLEngine engine = createSSLContext(settings).createSSLEngine();
-            engine.setUseClientMode(false);
-            logger.info("SSLEngine was properly configured and initialized.");
-            return engine;
-        } catch (Exception ex) {
-            logger.warn("SSL could not be enabled.  Check the ssl section of the configuration file.", ex);
-            return null;
-        }
-    }
-
-    private SSLContext createSSLContext(final Settings settings) throws Exception {
+    private SslContext createSSLContext(final Settings settings)  {
         final Settings.SslSettings sslSettings = settings.ssl;
+        final SslProvider provider = SslProvider.JDK;
 
-        TrustManager[] managers = null;
-        if (sslSettings.trustStoreFile != null) {
-            final KeyStore ts = KeyStore.getInstance(Optional.ofNullable(sslSettings.trustStoreFormat).orElseThrow(() -> new IllegalStateException("The trustStoreFormat is not set")));
-            try (final InputStream trustStoreInputStream = new FileInputStream(Optional.ofNullable(sslSettings.trustStoreFile).orElseThrow(() -> new IllegalStateException("The trustStoreFile is not set")))) {
-                ts.load(trustStoreInputStream, sslSettings.trustStorePassword.toCharArray());
-            }
+        final SslContextBuilder builder;
 
-            final String trustStoreAlgorithm = Optional.ofNullable(sslSettings.trustStoreAlgorithm).orElse(TrustManagerFactory.getDefaultAlgorithm());
-            final TrustManagerFactory tmf = TrustManagerFactory.getInstance(trustStoreAlgorithm);
-            tmf.init(ts);
-            managers = tmf.getTrustManagers();
-        }
-
-        final KeyStore ks = KeyStore.getInstance(Optional.ofNullable(sslSettings.keyStoreFormat).orElseThrow(() -> new IllegalStateException("The keyStoreFormat is not set")));
-        try (final InputStream keyStoreInputStream = new FileInputStream(Optional.ofNullable(sslSettings.keyStoreFile).orElseThrow(() -> new IllegalStateException("The keyStoreFile is not set")))) {
-            ks.load(keyStoreInputStream, sslSettings.keyStorePassword.toCharArray());
+        // if the config doesn't contain a cert or key then use a self signed cert - not suitable for production
+        if (null == sslSettings.keyCertChainFile || null == sslSettings.keyFile) {
+            try {
+                logger.warn("Enabling SSL with self-signed certificate (NOT SUITABLE FOR PRODUCTION)");
+                final SelfSignedCertificate ssc = new SelfSignedCertificate();
+                builder = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey());
+            } catch (CertificateException ce) {
+                logger.error("There was an error creating the self-signed certificate for SSL - SSL is not enabled", ce);
+                return null;
+            }
+        } else {
+            final File keyCertChainFile = new File(sslSettings.keyCertChainFile);
+            final File keyFile = new File(sslSettings.keyFile);
+            final File trustCertChainFile = null == sslSettings.trustCertChainFile ? null : new File(sslSettings.trustCertChainFile);
+
+            // note that keyPassword may be null here if the keyFile is not password-protected. passing null to
+            // trustManager is also ok (default will be used)
+            builder = SslContextBuilder.forServer(keyCertChainFile, keyFile, sslSettings.keyPassword)
+                    .trustManager(trustCertChainFile);
         }
 
-        final String keyManagerAlgorithm = Optional.ofNullable(sslSettings.keyManagerAlgorithm).orElse(KeyManagerFactory.getDefaultAlgorithm());
-        final KeyManagerFactory kmf = KeyManagerFactory.getInstance(keyManagerAlgorithm);
-        kmf.init(ks, Optional.ofNullable(sslSettings.keyManagerPassword).orElseThrow(() -> new IllegalStateException("The keyManagerPassword is not set")).toCharArray());
+        builder.sslProvider(provider);
 
-        final SSLContext serverContext = SSLContext.getInstance("TLS");
-        serverContext.init(kmf.getKeyManagers(), managers, null);
-        return serverContext;
+        try {
+            return builder.build();
+        } catch (SSLException ssle) {
+            logger.error("There was an error enabling SSL", ssle);
+            return null;
+        }
     }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-tinkerpop/blob/21033372/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/Settings.java
----------------------------------------------------------------------
diff --git a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/Settings.java b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/Settings.java
index c878b5a..4f3cb2c 100644
--- a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/Settings.java
+++ b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/Settings.java
@@ -277,16 +277,33 @@ public class Settings {
      * Settings to configure SSL support.
      */
     public static class SslSettings {
+        /**
+         * Enables SSL.  Other settings will be ignored unless this is set to true. By default a self-signed
+         * certificate is used (not suitable for production) for SSL.  To override this setting, be sure to set
+         * the {@link #keyCertChainFile} and the {@link #keyFile}.
+         */
         public boolean enabled = false;
-        public String keyManagerAlgorithm = "SunX509";
-        public String keyStoreFormat = "JKS";
-        public String keyStoreFile = null;
-        public String keyStorePassword = null;
-        public String keyManagerPassword = null;
-        public String trustStoreFile = null;
-        public String trustStoreFormat = null;
-        public String trustStorePassword = null;
-        public String trustStoreAlgorithm = null;
+
+        /**
+         * The X.509 certificate chain file in PEM format.
+         */
+        public String keyCertChainFile = null;
+
+        /**
+         * The PKCS#8 private key file in PEM format.
+         */
+        public String keyFile = null;
+
+        /**
+         * The password of the {@link #keyFile}, or {@code null} if it's not password-protected
+         */
+        public String keyPassword = null;
+
+        /**
+         * Trusted certificates for verifying the remote endpoint's certificate. The file should
+         * contain an X.509 certificate chain in PEM format. {@code null} uses the system default.
+         */
+        public String trustCertChainFile = null;
     }
 
     /**