From 7cc18226cd15ce5bde0b45c7b780ea2ad1a9d9f1 Mon Sep 17 00:00:00 2001 From: Parag Jain Date: Mon, 9 Oct 2017 15:53:12 -0500 Subject: [PATCH] add more tls configs to enable/disable specific cipher suites and protocols (#4902) * add more tls configs to enable/disable specific cipher suites and protocols * fix doc, allow empty list --- docs/content/operations/tls-support.md | 9 +++ .../initialization/TLSServerConfig.java | 44 +++++++++++- .../jetty/ChatHandlerServerModule.java | 6 +- .../jetty/JettyServerModule.java | 70 +++++++++++++++---- 4 files changed, 111 insertions(+), 18 deletions(-) diff --git a/docs/content/operations/tls-support.md b/docs/content/operations/tls-support.md index af039a85f62..e5eaa07ec44 100644 --- a/docs/content/operations/tls-support.md +++ b/docs/content/operations/tls-support.md @@ -30,8 +30,17 @@ values for the below mentioned configs among others provided by Java implementat |`druid.server.https.keyStoreType`|The type of the key store.|none|yes| |`druid.server.https.certAlias`|Alias of TLS/SSL certificate for the connector.|none|yes| |`druid.server.https.keyStorePassword`|The [Password Provider](../operations/password-provider.html) or String password for the Key Store.|none|yes| + +Following table contains non-mandatory advanced configuration options, use caution. + +|Property|Description|Default|Required| +|--------|-----------|-------|--------| |`druid.server.https.keyManagerFactoryAlgorithm`|Algorithm to use for creating KeyManager, more details [here](https://docs.oracle.com/javase/7/docs/technotes/guides/security/jsse/JSSERefGuide.html#KeyManager).|`javax.net.ssl.KeyManagerFactory.getDefaultAlgorithm()`|no| |`druid.server.https.keyManagerPassword`|The [Password Provider](../operations/password-provider.html) or String password for the Key Manager.|none|no| +|`druid.server.https.includeCipherSuites`|List of cipher suite names to include. You can either use the exact cipher suite name or a regular expression.|Jetty's default include cipher list|no| +|`druid.server.https.excludeCipherSuites`|List of cipher suite names to exclude. You can either use the exact cipher suite name or a regular expression.|Jetty's default exclude cipher list|no| +|`druid.server.https.includeProtocols`|List of exact protocols names to include.|Jetty's default include protocol list|no| +|`druid.server.https.excludeProtocols`|List of exact protocols names to exclude.|Jetty's default exclude protocol list|no| # Druid's internal communication over TLS diff --git a/server/src/main/java/io/druid/server/initialization/TLSServerConfig.java b/server/src/main/java/io/druid/server/initialization/TLSServerConfig.java index 79f01674413..affb32e76c1 100644 --- a/server/src/main/java/io/druid/server/initialization/TLSServerConfig.java +++ b/server/src/main/java/io/druid/server/initialization/TLSServerConfig.java @@ -21,6 +21,8 @@ package io.druid.server.initialization; import com.fasterxml.jackson.annotation.JsonProperty; import io.druid.metadata.PasswordProvider; +import java.util.List; + public class TLSServerConfig { @JsonProperty @@ -32,15 +34,27 @@ public class TLSServerConfig @JsonProperty private String certAlias; - @JsonProperty - private String keyManagerFactoryAlgorithm; - @JsonProperty("keyStorePassword") private PasswordProvider keyStorePasswordProvider; @JsonProperty("keyManagerPassword") private PasswordProvider keyManagerPasswordProvider; + @JsonProperty + private String keyManagerFactoryAlgorithm; + + @JsonProperty + private List includeCipherSuites; + + @JsonProperty + private List excludeCipherSuites; + + @JsonProperty + private List includeProtocols; + + @JsonProperty + private List excludeProtocols; + public String getKeyStorePath() { return keyStorePath; @@ -71,6 +85,26 @@ public class TLSServerConfig return keyManagerFactoryAlgorithm; } + public List getIncludeCipherSuites() + { + return includeCipherSuites; + } + + public List getExcludeCipherSuites() + { + return excludeCipherSuites; + } + + public List getIncludeProtocols() + { + return includeProtocols; + } + + public List getExcludeProtocols() + { + return excludeProtocols; + } + @Override public String toString() { @@ -79,6 +113,10 @@ public class TLSServerConfig ", keyStoreType='" + keyStoreType + '\'' + ", certAlias='" + certAlias + '\'' + ", keyManagerFactoryAlgorithm='" + keyManagerFactoryAlgorithm + '\'' + + ", includeCipherSuites=" + includeCipherSuites + + ", excludeCipherSuites=" + excludeCipherSuites + + ", includeProtocols=" + includeProtocols + + ", excludeProtocols=" + excludeProtocols + '}'; } } diff --git a/server/src/main/java/io/druid/server/initialization/jetty/ChatHandlerServerModule.java b/server/src/main/java/io/druid/server/initialization/jetty/ChatHandlerServerModule.java index b938af1aa1c..b2ebb01d8c7 100644 --- a/server/src/main/java/io/druid/server/initialization/jetty/ChatHandlerServerModule.java +++ b/server/src/main/java/io/druid/server/initialization/jetty/ChatHandlerServerModule.java @@ -120,13 +120,13 @@ public class ChatHandlerServerModule implements Module @RemoteChatHandler TLSServerConfig TLSServerConfig ) { - final Server server = JettyServerModule.makeJettyServer( + return JettyServerModule.makeAndInitializeServer( + injector, + lifecycle, node, config, TLSServerConfig, injector.getExistingBinding(Key.get(SslContextFactory.class)) ); - JettyServerModule.initializeServer(injector, lifecycle, server); - return server; } } diff --git a/server/src/main/java/io/druid/server/initialization/jetty/JettyServerModule.java b/server/src/main/java/io/druid/server/initialization/jetty/JettyServerModule.java index 6c5b7649e1c..078210485ee 100644 --- a/server/src/main/java/io/druid/server/initialization/jetty/JettyServerModule.java +++ b/server/src/main/java/io/druid/server/initialization/jetty/JettyServerModule.java @@ -22,6 +22,7 @@ package io.druid.server.initialization.jetty; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider; import com.fasterxml.jackson.jaxrs.smile.JacksonSmileProvider; +import com.google.common.base.Preconditions; import com.google.common.primitives.Ints; import com.google.inject.Binder; import com.google.inject.Binding; @@ -48,6 +49,7 @@ import io.druid.guice.annotations.JSR311Resource; import io.druid.guice.annotations.Json; import io.druid.guice.annotations.Self; import io.druid.guice.annotations.Smile; +import io.druid.java.util.common.ISE; import io.druid.java.util.common.RE; import io.druid.java.util.common.lifecycle.Lifecycle; import io.druid.java.util.common.logger.Logger; @@ -72,8 +74,10 @@ import org.eclipse.jetty.util.thread.QueuedThreadPool; import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler; import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLEngine; import javax.servlet.ServletException; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Set; @@ -147,14 +151,14 @@ public class JettyServerModule extends JerseyServletModule final TLSServerConfig TLSServerConfig ) { - final Server server = makeJettyServer( + return makeAndInitializeServer( + injector, + lifecycle, node, config, TLSServerConfig, injector.getExistingBinding(Key.get(SslContextFactory.class)) ); - initializeServer(injector, lifecycle, server); - return server; } @Provides @@ -175,7 +179,9 @@ public class JettyServerModule extends JerseyServletModule return provider; } - static Server makeJettyServer( + static Server makeAndInitializeServer( + Injector injector, + Lifecycle lifecycle, DruidNode node, ServerConfig config, TLSServerConfig tlsServerConfig, @@ -216,9 +222,10 @@ public class JettyServerModule extends JerseyServletModule connector.setPort(node.getPlaintextPort()); serverConnectors.add(connector); } + + final SslContextFactory sslContextFactory; if (node.isEnableTlsPort()) { log.info("Creating https connector with port [%d]", node.getTlsPort()); - final SslContextFactory sslContextFactory; if (sslContextFactoryBinding == null) { // Never trust all certificates by default sslContextFactory = new SslContextFactory(false); @@ -229,8 +236,26 @@ public class JettyServerModule extends JerseyServletModule sslContextFactory.setKeyManagerFactoryAlgorithm(tlsServerConfig.getKeyManagerFactoryAlgorithm() == null ? KeyManagerFactory.getDefaultAlgorithm() : tlsServerConfig.getKeyManagerFactoryAlgorithm()); - sslContextFactory.setKeyManagerPassword(tlsServerConfig.getKeyManagerPasswordProvider() == null ? null - : tlsServerConfig.getKeyManagerPasswordProvider().getPassword()); + sslContextFactory.setKeyManagerPassword(tlsServerConfig.getKeyManagerPasswordProvider() == null ? + null : tlsServerConfig.getKeyManagerPasswordProvider().getPassword()); + if (tlsServerConfig.getIncludeCipherSuites() != null) { + sslContextFactory.setIncludeCipherSuites( + tlsServerConfig.getIncludeCipherSuites() + .toArray(new String[tlsServerConfig.getIncludeCipherSuites().size()])); + } + if (tlsServerConfig.getExcludeCipherSuites() != null) { + sslContextFactory.setExcludeCipherSuites( + tlsServerConfig.getExcludeCipherSuites() + .toArray(new String[tlsServerConfig.getExcludeCipherSuites().size()])); + } + if (tlsServerConfig.getIncludeProtocols() != null) { + sslContextFactory.setIncludeProtocols( + tlsServerConfig.getIncludeProtocols().toArray(new String[tlsServerConfig.getIncludeProtocols().size()])); + } + if (tlsServerConfig.getExcludeProtocols() != null) { + sslContextFactory.setExcludeProtocols( + tlsServerConfig.getExcludeProtocols().toArray(new String[tlsServerConfig.getExcludeProtocols().size()])); + } } else { sslContextFactory = sslContextFactoryBinding.getProvider().get(); } @@ -246,6 +271,8 @@ public class JettyServerModule extends JerseyServletModule ); connector.setPort(node.getTlsPort()); serverConnectors.add(connector); + } else { + sslContextFactory = null; } final ServerConnector[] connectors = new ServerConnector[serverConnectors.size()]; @@ -266,11 +293,7 @@ public class JettyServerModule extends JerseyServletModule server.setConnectors(connectors); - return server; - } - - static void initializeServer(Injector injector, Lifecycle lifecycle, final Server server) - { + // initialize server JettyServerInitializer initializer = injector.getInstance(JettyServerInitializer.class); try { initializer.initialize(server, injector); @@ -286,6 +309,27 @@ public class JettyServerModule extends JerseyServletModule public void start() throws Exception { server.start(); + if (node.isEnableTlsPort()) { + // Perform validation + Preconditions.checkNotNull(sslContextFactory); + final SSLEngine sslEngine = sslContextFactory.newSSLEngine(); + if (sslEngine.getEnabledCipherSuites() == null || sslEngine.getEnabledCipherSuites().length == 0) { + throw new ISE( + "No supported cipher suites found, supported suites [%s], configured suites include list: [%s] exclude list: [%s]", + Arrays.toString(sslEngine.getSupportedCipherSuites()), + tlsServerConfig.getIncludeCipherSuites(), + tlsServerConfig.getExcludeCipherSuites() + ); + } + if (sslEngine.getEnabledProtocols() == null || sslEngine.getEnabledProtocols().length == 0) { + throw new ISE( + "No supported protocols found, supported protocols [%s], configured protocols include list: [%s] exclude list: [%s]", + Arrays.toString(sslEngine.getSupportedProtocols()), + tlsServerConfig.getIncludeProtocols(), + tlsServerConfig.getExcludeProtocols() + ); + } + } } @Override @@ -300,6 +344,8 @@ public class JettyServerModule extends JerseyServletModule } } ); + + return server; } private static int getMaxJettyAcceptorsSelectorsNum(DruidNode druidNode)