You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@drill.apache.org by vo...@apache.org on 2020/03/10 18:41:35 UTC
[drill] 06/10: DRILL-7625: Add options for SslContextFactory
This is an automated email from the ASF dual-hosted git repository.
volodymyr pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/drill.git
commit f44f335473b587ceab9aa5043ac5bfbe172c7426
Author: Igor Guzenko <ih...@gmail.com>
AuthorDate: Mon Mar 2 17:09:54 2020 +0200
DRILL-7625: Add options for SslContextFactory
closes #2012
---
.../src/main/resources/drill-override-example.conf | 66 +++++++
.../java/org/apache/drill/exec/ExecConstants.java | 32 +++
.../apache/drill/exec/server/rest/WebServer.java | 121 ++----------
.../rest/ssl/SslContextFactoryConfigurator.java | 214 +++++++++++++++++++++
.../org/apache/drill/exec/ssl/SSLConfigServer.java | 16 +-
.../ssl/SslContextFactoryConfiguratorTest.java | 71 +++++++
6 files changed, 404 insertions(+), 116 deletions(-)
diff --git a/distribution/src/main/resources/drill-override-example.conf b/distribution/src/main/resources/drill-override-example.conf
index ad942bb..2dec959 100644
--- a/distribution/src/main/resources/drill-override-example.conf
+++ b/distribution/src/main/resources/drill-override-example.conf
@@ -113,6 +113,72 @@ drill.exec: {
# Location to keytab file for above spnego principal
spnego.keytab: "<keytab_file_location>";
},
+ jetty: {
+ server: {
+ # Optional params to set on Jetty's org.eclipse.jetty.util.ssl.SslContextFactory when drill.exec.http.ssl_enabled
+ sslContextFactory: {
+ # allows to specify cert to use when multiple non-SNI certificates are available.
+ certAlias: "certAlias",
+ # path to file that contains Certificate Revocation List
+ crlPath: "/etc/file.crl",
+ # enable Certificate Revocation List Distribution Points Support
+ enableCRLDP: false,
+ # enable On-Line Certificate Status Protocol support
+ enableOCSP: false,
+ # when set to "HTTPS" hostname verification will be enabled
+ endpointIdentificationAlgorithm: "HTTPS",
+ # accepts exact cipher suite names and/or regular expressions.
+ excludeCipherSuites: ["SSL_DHE_DSS_WITH_DES_CBC_SHA"],
+ # list of TLS/SSL protocols to exclude
+ excludeProtocols: ["TLSv1.1"],
+ # accepts exact cipher suite names and/or regular expressions.
+ includeCipherSuites: ["SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA", "SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA"],
+ # list of TLS/SSL protocols to include
+ includeProtocols: ["TLSv1.2", "TLSv1.3"],
+ # the algorithm name (default "SunX509") used by the javax.net.ssl.KeyManagerFactory
+ keyManagerFactoryAlgorithm: "SunX509",
+ # classname of custom java.security.Provider implementation
+ keyStoreProvider: null,
+ # type of key store (default "JKS")
+ keyStoreType: "JKS",
+ # max number of intermediate certificates in sertificate chain
+ maxCertPathLength: -1,
+ # set true if ssl needs client authentication
+ needClientAuth: false,
+ # location of the OCSP Responder
+ ocspResponderURL: "",
+ # javax.net.ssl.SSLContext provider
+ provider: null,
+ # whether TLS renegotiation is allowed
+ renegotiationAllowed: false,
+ # number of renegotions allowed for this connection (-1 for unlimited, default 5) .
+ renegotiationLimit: 5,
+ # algorithm name for java.security.SecurityRandom instances.
+ # https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#SecureRandom
+ secureRandomAlgorithm: "NativePRNG",
+ # set the flag to enable SSL Session caching
+ sessionCachingEnabled: false,
+ # set if you want to bound session cache size
+ sslSessionCacheSize: -1,
+ # session timeout in seconds.
+ sslSessionTimeout: -1,
+ # the algorithm name (default "SunX509") used by the javax.net.ssl.TrustManagerFactory
+ trustManagerFactoryAlgorithm: "SunX509",
+ # provider of the trust store
+ trustStoreProvider: null,
+ # type of the trust store (default "JKS")
+ trustStoreType: "JKS",
+ # sets whether the local cipher suites preference should be honored.
+ useCipherSuiteOrder: false,
+ # true if SSL certificates have to be validated
+ validateCerts: false,
+ # true if SSL certificates of the peer have to be validated
+ validatePeerCerts: false,
+ # true if SSL wants client authentication.
+ wantClientAuth: false
+ }
+ }
+ }
},
# Below SSL parameters need to be set for custom transport layer settings.
ssl: {
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/ExecConstants.java b/exec/java-exec/src/main/java/org/apache/drill/exec/ExecConstants.java
index d0a4718..2e3a9cf 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/ExecConstants.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/ExecConstants.java
@@ -212,6 +212,38 @@ public final class ExecConstants {
public static final String HTTP_JETTY_SERVER_ACCEPTORS = "drill.exec.http.jetty.server.acceptors";
public static final String HTTP_JETTY_SERVER_SELECTORS = "drill.exec.http.jetty.server.selectors";
public static final String HTTP_JETTY_SERVER_HANDLERS = "drill.exec.http.jetty.server.handlers";
+
+ public static final String HTTP_JETTY_SSL_CONTEXT_FACTORY_OPTIONS_PREFIX = "drill.exec.http.jetty.server.sslContextFactory";
+ public static final String HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_CERT_ALIAS = "drill.exec.http.jetty.server.sslContextFactory.certAlias";
+ public static final String HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_CRL_PATH = "drill.exec.http.jetty.server.sslContextFactory.crlPath";
+ public static final String HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_ENABLE_CRLDP = "drill.exec.http.jetty.server.sslContextFactory.enableCRLDP";
+ public static final String HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_ENABLE_OCSP = "drill.exec.http.jetty.server.sslContextFactory.enableOCSP";
+ public static final String HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_ENDPOINT_IDENTIFICATION_ALGORITHM = "drill.exec.http.jetty.server.sslContextFactory.endpointIdentificationAlgorithm";
+ public static final String HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_EXCLUDE_CIPHER_SUITES = "drill.exec.http.jetty.server.sslContextFactory.excludeCipherSuites";
+ public static final String HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_EXCLUDE_PROTOCOLS = "drill.exec.http.jetty.server.sslContextFactory.excludeProtocols";
+ public static final String HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_INCLUDE_CIPHER_SUITES = "drill.exec.http.jetty.server.sslContextFactory.includeCipherSuites";
+ public static final String HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_INCLUDE_PROTOCOLS = "drill.exec.http.jetty.server.sslContextFactory.includeProtocols";
+ public static final String HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_KEY_MANAGER_FACTORY_ALGORITHM = "drill.exec.http.jetty.server.sslContextFactory.keyManagerFactoryAlgorithm";
+ public static final String HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_KEYSTORE_PROVIDER = "drill.exec.http.jetty.server.sslContextFactory.keyStoreProvider";
+ public static final String HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_KEYSTORE_TYPE = "drill.exec.http.jetty.server.sslContextFactory.keyStoreType";
+ public static final String HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_MAX_CERT_PATH_LENGTH = "drill.exec.http.jetty.server.sslContextFactory.maxCertPathLength";
+ public static final String HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_NEED_CLIENT_AUTH = "drill.exec.http.jetty.server.sslContextFactory.needClientAuth";
+ public static final String HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_OCSP_RESPONDER_URL = "drill.exec.http.jetty.server.sslContextFactory.ocspResponderURL";
+ public static final String HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_PROVIDER = "drill.exec.http.jetty.server.sslContextFactory.provider";
+ public static final String HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_RENEGOTIATION_ALLOWED = "drill.exec.http.jetty.server.sslContextFactory.renegotiationAllowed";
+ public static final String HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_RENEGOTIATION_LIMIT = "drill.exec.http.jetty.server.sslContextFactory.renegotiationLimit";
+ public static final String HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_SECURE_RANDOM_ALGORITHM = "drill.exec.http.jetty.server.sslContextFactory.secureRandomAlgorithm";
+ public static final String HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_SESSION_CACHING_ENABLED = "drill.exec.http.jetty.server.sslContextFactory.sessionCachingEnabled";
+ public static final String HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_SSL_SESSION_CACHE_SIZE = "drill.exec.http.jetty.server.sslContextFactory.sslSessionCacheSize";
+ public static final String HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_SSL_SESSION_TIMEOUT = "drill.exec.http.jetty.server.sslContextFactory.sslSessionTimeout";
+ public static final String HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_TRUSTMANAGERFACTORY_ALGORITHM = "drill.exec.http.jetty.server.sslContextFactory.trustManagerFactoryAlgorithm";
+ public static final String HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_TRUSTSTORE_PROVIDER = "drill.exec.http.jetty.server.sslContextFactory.trustStoreProvider";
+ public static final String HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_TRUSTSTORE_TYPE = "drill.exec.http.jetty.server.sslContextFactory.trustStoreType";
+ public static final String HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_USE_CIPHER_SUITE_ORDER = "drill.exec.http.jetty.server.sslContextFactory.useCipherSuiteOrder";
+ public static final String HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_VALIDATE_CERTS = "drill.exec.http.jetty.server.sslContextFactory.validateCerts";
+ public static final String HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_VALIDATE_PEER_CERTS = "drill.exec.http.jetty.server.sslContextFactory.validatePeerCerts";
+ public static final String HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_WANT_CLIENT_AUTH = "drill.exec.http.jetty.server.sslContextFactory.wantClientAuth";
+
public static final String HTTP_ENABLE_SSL = "drill.exec.http.ssl_enabled";
public static final String HTTP_CLIENT_TIMEOUT = "drill.exec.http.client.timeout";
public static final String HTTP_CORS_ENABLED = "drill.exec.http.cors.enabled";
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/WebServer.java b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/WebServer.java
index b648de5..0e937d0 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/WebServer.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/WebServer.java
@@ -22,13 +22,12 @@ import com.codahale.metrics.servlets.MetricsServlet;
import com.codahale.metrics.servlets.ThreadDumpServlet;
import org.apache.commons.io.FileUtils;
-import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.drill.common.config.DrillConfig;
import org.apache.drill.common.exceptions.DrillException;
import org.apache.drill.exec.ExecConstants;
-import org.apache.drill.exec.ssl.SSLConfig;
+import org.apache.drill.exec.server.rest.ssl.SslContextFactoryConfigurator;
import org.apache.drill.exec.exception.DrillbitStartupException;
import org.apache.drill.exec.expr.fn.registry.FunctionHolder;
import org.apache.drill.exec.expr.fn.registry.LocalFunctionRegistry;
@@ -40,15 +39,7 @@ import org.apache.drill.exec.server.options.OptionValidator.OptionDescription;
import org.apache.drill.exec.server.options.OptionValue;
import org.apache.drill.exec.server.rest.auth.DrillErrorHandler;
import org.apache.drill.exec.server.rest.auth.DrillHttpSecurityHandlerProvider;
-import org.apache.drill.exec.ssl.SSLConfigBuilder;
import org.apache.drill.exec.work.WorkManager;
-import org.bouncycastle.asn1.x500.X500NameBuilder;
-import org.bouncycastle.asn1.x500.style.BCStyle;
-import org.bouncycastle.cert.X509v3CertificateBuilder;
-import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
-import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
-import org.bouncycastle.operator.ContentSigner;
-import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.security.SecurityHandler;
import org.eclipse.jetty.security.authentication.SessionAuthentication;
@@ -71,7 +62,6 @@ import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.glassfish.jersey.servlet.ServletContainer;
-import org.joda.time.DateTime;
import javax.servlet.DispatcherType;
import javax.servlet.http.HttpSession;
@@ -82,18 +72,11 @@ import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
-import java.math.BigInteger;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
-import java.security.KeyStore;
-import java.security.SecureRandom;
-import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.Date;
import java.util.EnumSet;
import java.util.List;
import java.util.TreeSet;
@@ -161,26 +144,24 @@ public class WebServer implements AutoCloseable {
return;
}
- final boolean authEnabled = config.getBoolean(ExecConstants.USER_AUTHENTICATION_ENABLED);
- int port = config.getInt(ExecConstants.HTTP_PORT);
- final boolean portHunt = config.getBoolean(ExecConstants.HTTP_PORT_HUNT);
- final int acceptors = config.getInt(ExecConstants.HTTP_JETTY_SERVER_ACCEPTORS);
- final int selectors = config.getInt(ExecConstants.HTTP_JETTY_SERVER_SELECTORS);
- final int handlers = config.getInt(ExecConstants.HTTP_JETTY_SERVER_HANDLERS);
final QueuedThreadPool threadPool = new QueuedThreadPool(2, 2);
embeddedJetty = new Server(threadPool);
+
+ final boolean authEnabled = config.getBoolean(ExecConstants.USER_AUTHENTICATION_ENABLED);
ServletContextHandler webServerContext = createServletContextHandler(authEnabled);
- //Allow for Other Drillbits to make REST calls
- FilterHolder filterHolder = new FilterHolder(CrossOriginFilter.class);
- filterHolder.setInitParameter("allowedOrigins", "*");
- //Allowing CORS for metrics only
- webServerContext.addFilter(filterHolder, STATUS_METRICS_PATH, null);
embeddedJetty.setHandler(webServerContext);
+ final int acceptors = config.getInt(ExecConstants.HTTP_JETTY_SERVER_ACCEPTORS);
+ final int selectors = config.getInt(ExecConstants.HTTP_JETTY_SERVER_SELECTORS);
+ int port = config.getInt(ExecConstants.HTTP_PORT);
ServerConnector connector = createConnector(port, acceptors, selectors);
+
+ final int handlers = config.getInt(ExecConstants.HTTP_JETTY_SERVER_HANDLERS);
threadPool.setMaxThreads(handlers + connector.getAcceptors() + connector.getSelectorManager().getSelectorCount());
embeddedJetty.addConnector(connector);
+
+ final boolean portHunt = config.getBoolean(ExecConstants.HTTP_PORT_HUNT);
for (int retry = 0; retry < PORT_HUNT_TRIES; retry++) {
connector.setPort(port);
try {
@@ -264,6 +245,12 @@ public class WebServer implements AutoCloseable {
}
}
+ //Allow for Other Drillbits to make REST calls
+ FilterHolder filterHolder = new FilterHolder(CrossOriginFilter.class);
+ filterHolder.setInitParameter("allowedOrigins", "*");
+ //Allowing CORS for metrics only
+ servletContextHandler.addFilter(filterHolder, STATUS_METRICS_PATH, null);
+
return servletContextHandler;
}
@@ -346,79 +333,9 @@ public class WebServer implements AutoCloseable {
*/
private ServerConnector createHttpsConnector(int port, int acceptors, int selectors) throws Exception {
logger.info("Setting up HTTPS connector for web server");
-
- final SslContextFactory sslContextFactory = new SslContextFactory();
- SSLConfig ssl = new SSLConfigBuilder()
- .config(config)
- .mode(SSLConfig.Mode.SERVER)
- .initializeSSLContext(false)
- .validateKeyStore(true)
- .build();
- if (ssl.isSslValid()) {
- logger.info("Using configured SSL settings for web server");
-
- sslContextFactory.setKeyStorePath(ssl.getKeyStorePath());
- sslContextFactory.setKeyStorePassword(ssl.getKeyStorePassword());
- sslContextFactory.setKeyManagerPassword(ssl.getKeyPassword());
- if(ssl.hasTrustStorePath()){
- sslContextFactory.setTrustStorePath(ssl.getTrustStorePath());
- if(ssl.hasTrustStorePassword()){
- sslContextFactory.setTrustStorePassword(ssl.getTrustStorePassword());
- }
- }
- } else {
- logger.info("Using generated self-signed SSL settings for web server");
- final SecureRandom random = new SecureRandom();
-
- // Generate a private-public key pair
- final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
- keyPairGenerator.initialize(1024, random);
- final KeyPair keyPair = keyPairGenerator.generateKeyPair();
-
- final DateTime now = DateTime.now();
-
- // Create builder for certificate attributes
- final X500NameBuilder nameBuilder = new X500NameBuilder(BCStyle.INSTANCE)
- .addRDN(BCStyle.OU, "Apache Drill (auth-generated)")
- .addRDN(BCStyle.O, "Apache Software Foundation (auto-generated)")
- .addRDN(BCStyle.CN, workManager.getContext().getEndpoint().getAddress());
-
- final Date notBefore = now.minusMinutes(1).toDate();
- final Date notAfter = now.plusYears(5).toDate();
- final BigInteger serialNumber = new BigInteger(128, random);
-
- // Create a certificate valid for 5years from now.
- final X509v3CertificateBuilder certificateBuilder = new JcaX509v3CertificateBuilder(
- nameBuilder.build(), // attributes
- serialNumber,
- notBefore,
- notAfter,
- nameBuilder.build(),
- keyPair.getPublic());
-
- // Sign the certificate using the private key
- final ContentSigner contentSigner =
- new JcaContentSignerBuilder("SHA256WithRSAEncryption").build(keyPair.getPrivate());
- final X509Certificate certificate =
- new JcaX509CertificateConverter().getCertificate(certificateBuilder.build(contentSigner));
-
- // Check the validity
- certificate.checkValidity(now.toDate());
-
- // Make sure the certificate is self-signed.
- certificate.verify(certificate.getPublicKey());
-
- // Generate a random password for keystore protection
- final String keyStorePasswd = RandomStringUtils.random(20);
- final KeyStore keyStore = KeyStore.getInstance("JKS");
- keyStore.load(null, null);
- keyStore.setKeyEntry("DrillAutoGeneratedCert", keyPair.getPrivate(),
- keyStorePasswd.toCharArray(), new java.security.cert.Certificate[]{certificate});
-
- sslContextFactory.setKeyStore(keyStore);
- sslContextFactory.setKeyStorePassword(keyStorePasswd);
- }
-
+ SslContextFactory sslContextFactory = new SslContextFactoryConfigurator(config,
+ workManager.getContext().getEndpoint().getAddress())
+ .configureNewSslContextFactory();
final HttpConfiguration httpsConfig = baseHttpConfig();
httpsConfig.addCustomizer(new SecureRequestCustomizer());
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/ssl/SslContextFactoryConfigurator.java b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/ssl/SslContextFactoryConfigurator.java
new file mode 100644
index 0000000..a5aa541
--- /dev/null
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/ssl/SslContextFactoryConfigurator.java
@@ -0,0 +1,214 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.drill.exec.server.rest.ssl;
+
+import org.apache.commons.lang3.RandomStringUtils;
+import org.apache.drill.common.config.DrillConfig;
+import org.apache.drill.exec.ExecConstants;
+import org.apache.drill.exec.ssl.SSLConfig;
+import org.apache.drill.exec.ssl.SSLConfigBuilder;
+import org.bouncycastle.asn1.x500.X500NameBuilder;
+import org.bouncycastle.asn1.x500.style.BCStyle;
+import org.bouncycastle.cert.X509v3CertificateBuilder;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
+import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
+import org.bouncycastle.operator.ContentSigner;
+import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.joda.time.DateTime;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.math.BigInteger;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.KeyStore;
+import java.security.SecureRandom;
+import java.security.cert.X509Certificate;
+import java.util.Date;
+import java.util.List;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+/**
+ * Configures {@link SslContextFactory} when https is enabled for Web UI
+ */
+public class SslContextFactoryConfigurator {
+ private static final Logger logger = LoggerFactory.getLogger(SslContextFactoryConfigurator.class);
+
+ private final DrillConfig config;
+ private final String drillbitEndpointAddress;
+
+ public SslContextFactoryConfigurator(DrillConfig config, String drillbitEndpointAddress) {
+ this.config = config;
+ this.drillbitEndpointAddress = drillbitEndpointAddress;
+ }
+
+ /**
+ * Tries to apply ssl options configured by user. If provided configuration isn't valid,
+ * new self-signed certificate will be generated and used in sslContextFactory.
+ *
+ * @return new configured sslContextFactory
+ * @throws Exception when generation of self-signed certificate failed
+ */
+ public SslContextFactory configureNewSslContextFactory() throws Exception {
+ SSLConfig sslConf = new SSLConfigBuilder()
+ .config(config)
+ .mode(SSLConfig.Mode.SERVER)
+ .initializeSSLContext(false)
+ .validateKeyStore(true)
+ .build();
+ final SslContextFactory sslContextFactory = new SslContextFactory();
+ if (sslConf.isSslValid()) {
+ useOptionsConfiguredByUser(sslContextFactory, sslConf);
+ } else {
+ useAutoGeneratedSelfSignedCertificate(sslContextFactory);
+ }
+ return sslContextFactory;
+ }
+
+ private void useOptionsConfiguredByUser(SslContextFactory sslFactory, SSLConfig sslConf) {
+ logger.info("Using configured SSL settings for web server");
+ sslFactory.setKeyStorePath(sslConf.getKeyStorePath());
+ sslFactory.setKeyStorePassword(sslConf.getKeyStorePassword());
+ sslFactory.setKeyManagerPassword(sslConf.getKeyPassword());
+ if (sslConf.hasTrustStorePath()) {
+ sslFactory.setTrustStorePath(sslConf.getTrustStorePath());
+ if (sslConf.hasTrustStorePassword()) {
+ sslFactory.setTrustStorePassword(sslConf.getTrustStorePassword());
+ }
+ }
+ sslFactory.setProtocol(sslConf.getProtocol());
+ sslFactory.setIncludeProtocols(sslConf.getProtocol());
+ if (config.hasPath(ExecConstants.HTTP_JETTY_SSL_CONTEXT_FACTORY_OPTIONS_PREFIX)) {
+ setStringIfPresent(ExecConstants.HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_CERT_ALIAS, sslFactory::setCertAlias);
+ setStringIfPresent(ExecConstants.HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_CRL_PATH, sslFactory::setCrlPath);
+ setBooleanIfPresent(ExecConstants.HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_ENABLE_CRLDP, sslFactory::setEnableCRLDP);
+ setBooleanIfPresent(ExecConstants.HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_ENABLE_OCSP, sslFactory::setEnableOCSP);
+ setStringIfPresent(ExecConstants.HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_ENDPOINT_IDENTIFICATION_ALGORITHM, sslFactory::setEndpointIdentificationAlgorithm);
+ setStringArrayIfPresent(ExecConstants.HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_EXCLUDE_CIPHER_SUITES, sslFactory::setExcludeCipherSuites);
+ setStringArrayIfPresent(ExecConstants.HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_EXCLUDE_PROTOCOLS, sslFactory::setExcludeProtocols);
+ setStringArrayIfPresent(ExecConstants.HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_INCLUDE_CIPHER_SUITES, sslFactory::setIncludeCipherSuites);
+ setStringArrayIfPresent(ExecConstants.HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_INCLUDE_PROTOCOLS, sslFactory::setIncludeProtocols);
+ setStringIfPresent(ExecConstants.HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_KEY_MANAGER_FACTORY_ALGORITHM, sslFactory::setKeyManagerFactoryAlgorithm);
+ setStringIfPresent(ExecConstants.HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_KEYSTORE_PROVIDER, sslFactory::setKeyStoreProvider);
+ setStringIfPresent(ExecConstants.HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_KEYSTORE_TYPE, sslFactory::setKeyStoreType);
+ setIntIfPresent(ExecConstants.HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_MAX_CERT_PATH_LENGTH, sslFactory::setMaxCertPathLength);
+ setBooleanIfPresent(ExecConstants.HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_NEED_CLIENT_AUTH, sslFactory::setNeedClientAuth);
+ setStringIfPresent(ExecConstants.HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_OCSP_RESPONDER_URL, sslFactory::setOcspResponderURL);
+ setStringIfPresent(ExecConstants.HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_PROVIDER, sslFactory::setProvider);
+ setBooleanIfPresent(ExecConstants.HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_RENEGOTIATION_ALLOWED, sslFactory::setRenegotiationAllowed);
+ setIntIfPresent(ExecConstants.HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_RENEGOTIATION_LIMIT, sslFactory::setRenegotiationLimit);
+ setStringIfPresent(ExecConstants.HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_SECURE_RANDOM_ALGORITHM, sslFactory::setSecureRandomAlgorithm);
+ setBooleanIfPresent(ExecConstants.HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_SESSION_CACHING_ENABLED, sslFactory::setSessionCachingEnabled);
+ setIntIfPresent(ExecConstants.HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_SSL_SESSION_CACHE_SIZE, sslFactory::setSslSessionCacheSize);
+ setIntIfPresent(ExecConstants.HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_SSL_SESSION_TIMEOUT, sslFactory::setSslSessionTimeout);
+ setStringIfPresent(ExecConstants.HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_TRUSTMANAGERFACTORY_ALGORITHM, sslFactory::setTrustManagerFactoryAlgorithm);
+ setStringIfPresent(ExecConstants.HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_TRUSTSTORE_PROVIDER, sslFactory::setTrustStoreProvider);
+ setStringIfPresent(ExecConstants.HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_TRUSTSTORE_TYPE, sslFactory::setTrustStoreType);
+ setBooleanIfPresent(ExecConstants.HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_USE_CIPHER_SUITE_ORDER, sslFactory::setUseCipherSuitesOrder);
+ setBooleanIfPresent(ExecConstants.HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_VALIDATE_CERTS, sslFactory::setValidateCerts);
+ setBooleanIfPresent(ExecConstants.HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_VALIDATE_PEER_CERTS, sslFactory::setValidatePeerCerts);
+ setBooleanIfPresent(ExecConstants.HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_WANT_CLIENT_AUTH, sslFactory::setWantClientAuth);
+ }
+ }
+
+ private void setStringArrayIfPresent(String optKey, Consumer<String[]> optSet) {
+ setIfPresent(optKey,
+ key -> {
+ List<String> list = config.getStringList(key);
+ return list == null ? null : list.toArray(new String[0]);
+ },
+ optSet);
+ }
+
+ private void setBooleanIfPresent(String optKey, Consumer<Boolean> optSet) {
+ setIfPresent(optKey, config::getBoolean, optSet);
+ }
+
+ private void setStringIfPresent(String optKey, Consumer<String> optSet) {
+ setIfPresent(optKey, config::getString, optSet);
+ }
+
+ private void setIntIfPresent(String optKey, Consumer<Integer> optSet) {
+ setIfPresent(optKey, config::getInt, optSet);
+ }
+
+ private <T> void setIfPresent(String optKey, Function<String, T> optGet, Consumer<T> optSet) {
+ if (config.hasPath(optKey)) {
+ T optVal = optGet.apply(optKey);
+ if (optVal != null) {
+ optSet.accept(optVal);
+ }
+ }
+ }
+
+
+ private void useAutoGeneratedSelfSignedCertificate(SslContextFactory sslContextFactory) throws Exception {
+ logger.info("Using generated self-signed SSL settings for web server");
+ final SecureRandom random = new SecureRandom();
+
+ // Generate a private-public key pair
+ final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
+ keyPairGenerator.initialize(1024, random);
+ final KeyPair keyPair = keyPairGenerator.generateKeyPair();
+
+
+ // Create builder for certificate attributes
+ final X500NameBuilder nameBuilder = new X500NameBuilder(BCStyle.INSTANCE)
+ .addRDN(BCStyle.OU, "Apache Drill (auth-generated)")
+ .addRDN(BCStyle.O, "Apache Software Foundation (auto-generated)")
+ .addRDN(BCStyle.CN, drillbitEndpointAddress);
+
+ final DateTime now = DateTime.now();
+ final Date notBefore = now.minusMinutes(1).toDate();
+ final Date notAfter = now.plusYears(5).toDate();
+ final BigInteger serialNumber = new BigInteger(128, random);
+
+ // Create a certificate valid for 5years from now.
+ final X509v3CertificateBuilder certificateBuilder = new JcaX509v3CertificateBuilder(
+ nameBuilder.build(), // attributes
+ serialNumber,
+ notBefore,
+ notAfter,
+ nameBuilder.build(),
+ keyPair.getPublic());
+
+ // Sign the certificate using the private key
+ final ContentSigner contentSigner =
+ new JcaContentSignerBuilder("SHA256WithRSAEncryption").build(keyPair.getPrivate());
+ final X509Certificate certificate =
+ new JcaX509CertificateConverter().getCertificate(certificateBuilder.build(contentSigner));
+
+ // Check the validity
+ certificate.checkValidity(now.toDate());
+
+ // Make sure the certificate is self-signed.
+ certificate.verify(certificate.getPublicKey());
+
+ // Generate a random password for keystore protection
+ final String keyStorePasswd = RandomStringUtils.random(20);
+ final KeyStore keyStore = KeyStore.getInstance("JKS");
+ keyStore.load(null, null);
+ keyStore.setKeyEntry("DrillAutoGeneratedCert", keyPair.getPrivate(),
+ keyStorePasswd.toCharArray(), new java.security.cert.Certificate[]{certificate});
+
+ sslContextFactory.setKeyStore(keyStore);
+ sslContextFactory.setKeyStorePassword(keyStorePasswd);
+ }
+}
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/ssl/SSLConfigServer.java b/exec/java-exec/src/main/java/org/apache/drill/exec/ssl/SSLConfigServer.java
index e0b8f54..53d2616 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/ssl/SSLConfigServer.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/ssl/SSLConfigServer.java
@@ -90,7 +90,7 @@ public class SSLConfigServer extends SSLConfig {
String keyPass = getConfigParam(ExecConstants.SSL_KEY_PASSWORD,
resolveHadoopPropertyName(HADOOP_SSL_KEYSTORE_KEYPASSWORD_TPL_KEY, mode));
keyPassword = keyPass.isEmpty() ? keyStorePassword : keyPass;
- protocol = getConfigParamWithDefault(ExecConstants.SSL_PROTOCOL, DEFAULT_SSL_PROTOCOL);
+ protocol = config.getString(ExecConstants.SSL_PROTOCOL);
// If provider is OPENSSL then to debug or run this code in an IDE, you will need to enable
// the dependency on netty-tcnative with the correct classifier for the platform you use.
// This can be done by enabling the openssl profile.
@@ -99,7 +99,7 @@ public class SSLConfigServer extends SSLConfig {
// or from your local maven repository:
// ~/.m2/repository/kr/motd/maven/os-maven-plugin/1.6.1/os-maven-plugin-1.6.1.jar
// Note that installing this plugin may require you to start with a new workspace
- provider = getConfigParamWithDefault(ExecConstants.SSL_PROVIDER, DEFAULT_SSL_PROVIDER);
+ provider = config.getString(ExecConstants.SSL_PROVIDER);
}
public void validateKeyStore() throws DrillException {
@@ -222,18 +222,6 @@ public class SSLConfigServer extends SSLConfig {
return value;
}
- private String getConfigParamWithDefault(String name, String defaultValue) {
- String value = "";
- if (config.hasPath(name)) {
- value = config.getString(name);
- }
- if (value.isEmpty()) {
- value = defaultValue;
- }
- value = value.trim();
- return value;
- }
-
private String resolveHadoopPropertyName(String nameTemplate, Mode mode) {
return MessageFormat.format(nameTemplate, mode.toString().toLowerCase());
}
diff --git a/exec/java-exec/src/test/java/org/apache/drill/exec/server/rest/ssl/SslContextFactoryConfiguratorTest.java b/exec/java-exec/src/test/java/org/apache/drill/exec/server/rest/ssl/SslContextFactoryConfiguratorTest.java
new file mode 100644
index 0000000..6a184fb
--- /dev/null
+++ b/exec/java-exec/src/test/java/org/apache/drill/exec/server/rest/ssl/SslContextFactoryConfiguratorTest.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.drill.exec.server.rest.ssl;
+
+import java.util.Arrays;
+
+import org.apache.drill.categories.OptionsTest;
+import org.apache.drill.exec.ExecConstants;
+import org.apache.drill.test.ClusterFixture;
+import org.apache.drill.test.ClusterFixtureBuilder;
+import org.apache.drill.test.ClusterTest;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+@Category(OptionsTest.class)
+public class SslContextFactoryConfiguratorTest extends ClusterTest {
+
+ private static SslContextFactoryConfigurator sslContextFactoryConfigurator;
+
+ @BeforeClass
+ public static void setUpClass() throws Exception {
+ ClusterFixtureBuilder fixtureBuilder = ClusterFixture.builder(dirTestWatcher)
+ // imitate proper ssl config for embedded web
+ .configProperty(ExecConstants.SSL_PROTOCOL, "TLSv1.2")
+ .configProperty(ExecConstants.HTTP_ENABLE_SSL, true)
+ .configProperty(ExecConstants.HTTP_TRUSTSTORE_PATH, "/tmp/ssl/cacerts.jks")
+ .configProperty(ExecConstants.HTTP_TRUSTSTORE_PASSWORD, "passphrase")
+ .configProperty(ExecConstants.HTTP_KEYSTORE_PATH, "/tmp/ssl/keystore.jks")
+ .configProperty(ExecConstants.HTTP_KEYSTORE_PASSWORD, "passphrase")
+ .configProperty(ExecConstants.SSL_KEY_PASSWORD, "passphrase")
+ .configProperty(ExecConstants.SSL_USE_HADOOP_CONF, false)
+
+ // few specific opts for Jetty sslContextFactory
+ .configProperty(ExecConstants.HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_SSL_SESSION_TIMEOUT, 30)
+ .configProperty(ExecConstants.HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_WANT_CLIENT_AUTH, true);
+ fixtureBuilder.configBuilder().put(ExecConstants.HTTP_JETTY_SERVER_SSL_CONTEXT_FACTORY_EXCLUDE_PROTOCOLS,
+ Arrays.asList("TLSv1.0", "TLSv1.1"));
+ startCluster(fixtureBuilder);
+ sslContextFactoryConfigurator = new SslContextFactoryConfigurator(cluster.config(), cluster.drillbit().getContext().getEndpoint().getAddress());
+ }
+
+ @Test
+ public void configureNewSslContextFactory() throws Exception {
+ SslContextFactory sslContextFactory = sslContextFactoryConfigurator.configureNewSslContextFactory();
+
+ assertEquals(30, sslContextFactory.getSslSessionTimeout());
+ assertTrue(sslContextFactory.getWantClientAuth());
+ assertArrayEquals(new String[]{"TLSv1.0", "TLSv1.1"}, sslContextFactory.getExcludeProtocols());
+ }
+}