Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id E5C97200D42 for ; Thu, 12 Oct 2017 20:28:16 +0200 (CEST) Received: by cust-asf.ponee.io (Postfix) id E47391609E4; Thu, 12 Oct 2017 18:28:16 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id B5965160BE4 for ; Thu, 12 Oct 2017 20:28:14 +0200 (CEST) Received: (qmail 27276 invoked by uid 500); 12 Oct 2017 18:28:13 -0000 Mailing-List: contact commits-help@drill.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: commits@drill.apache.org Delivered-To: mailing list commits@drill.apache.org Received: (qmail 27168 invoked by uid 99); 12 Oct 2017 18:28:13 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 12 Oct 2017 18:28:13 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id AB75FDFB3D; Thu, 12 Oct 2017 18:28:13 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: parthc@apache.org To: commits@drill.apache.org Date: Thu, 12 Oct 2017 18:28:15 -0000 Message-Id: In-Reply-To: <2ebe1d9b87e04553bd7f87719e8a3982@git.apache.org> References: <2ebe1d9b87e04553bd7f87719e8a3982@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [03/13] drill git commit: DRILL-5431: SSL Support (Java) - Java client server SSL implementation archived-at: Thu, 12 Oct 2017 18:28:17 -0000 DRILL-5431: SSL Support (Java) - Java client server SSL implementation Also enable OpenSSL support Also fix exclusions and java-exec pom file to eliminate netty-tcnative as a transitive dependency on all projects Project: http://git-wip-us.apache.org/repos/asf/drill/repo Commit: http://git-wip-us.apache.org/repos/asf/drill/commit/552d7d82 Tree: http://git-wip-us.apache.org/repos/asf/drill/tree/552d7d82 Diff: http://git-wip-us.apache.org/repos/asf/drill/diff/552d7d82 Branch: refs/heads/master Commit: 552d7d825e37d42835bd6bfccfc07fc7d3b5fd94 Parents: b803405 Author: Parth Chandra Authored: Fri Jun 9 22:03:59 2017 -0700 Committer: Parth Chandra Committed: Wed Oct 11 19:26:13 2017 -0700 ---------------------------------------------------------------------- .../drill/common/config/DrillProperties.java | 16 +- contrib/format-maprdb/pom.xml | 45 +-- contrib/storage-hbase/pom.xml | 78 ++-- distribution/pom.xml | 29 +- distribution/src/assemble/bin.xml | 38 ++ distribution/src/resources/drill-config.sh | 22 ++ .../src/resources/drill-override-example.conf | 18 +- exec/java-exec/pom.xml | 66 +++- .../org/apache/drill/exec/ExecConstants.java | 28 +- .../java/org/apache/drill/exec/SSLConfig.java | 69 ---- .../apache/drill/exec/client/DrillClient.java | 6 +- .../apache/drill/exec/rpc/user/UserClient.java | 356 +++++++++++-------- .../exec/rpc/user/UserConnectionConfig.java | 13 +- .../apache/drill/exec/rpc/user/UserServer.java | 49 +++ .../drill/exec/server/rest/WebServer.java | 12 +- .../org/apache/drill/exec/ssl/SSLConfig.java | 265 ++++++++++++++ .../apache/drill/exec/ssl/SSLConfigBuilder.java | 94 +++++ .../apache/drill/exec/ssl/SSLConfigClient.java | 273 ++++++++++++++ .../apache/drill/exec/ssl/SSLConfigServer.java | 330 +++++++++++++++++ .../src/main/resources/drill-module.conf | 33 +- .../java/org/apache/drill/BaseTestQuery.java | 10 +- .../org/apache/drill/exec/TestSSLConfig.java | 75 +++- .../exec/rpc/user/security/TestUserBitSSL.java | 344 ++++++++++++++++++ .../rpc/user/security/TestUserBitSSLServer.java | 143 ++++++++ .../src/test/resources/ssl-server-invalid.xml | 72 ++++ .../drill/jdbc/impl/DrillConnectionImpl.java | 6 +- .../exec/rpc/AbstractRemoteConnection.java | 27 +- .../org/apache/drill/exec/rpc/BasicClient.java | 149 +++----- .../org/apache/drill/exec/rpc/BasicServer.java | 28 ++ .../drill/exec/rpc/ConnectionMultiListener.java | 235 ++++++++++++ .../org/apache/drill/exec/rpc/RpcConstants.java | 1 + 31 files changed, 2447 insertions(+), 483 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/drill/blob/552d7d82/common/src/main/java/org/apache/drill/common/config/DrillProperties.java ---------------------------------------------------------------------- diff --git a/common/src/main/java/org/apache/drill/common/config/DrillProperties.java b/common/src/main/java/org/apache/drill/common/config/DrillProperties.java index 75064e0..53a45b3 100644 --- a/common/src/main/java/org/apache/drill/common/config/DrillProperties.java +++ b/common/src/main/java/org/apache/drill/common/config/DrillProperties.java @@ -72,6 +72,17 @@ public final class DrillProperties extends Properties { public static final String QUOTING_IDENTIFIERS = "quoting_identifiers"; + public static final String ENABLE_TLS = "enableTLS"; + public static final String TLS_PROTOCOL = "TLSProtocol"; + public static final String TRUSTSTORE_TYPE = "trustStoreType"; + public static final String TRUSTSTORE_PATH = "trustStorePath"; + public static final String TRUSTSTORE_PASSWORD = "trustStorePassword"; + public static final String DISABLE_HOST_VERIFICATION = "disableHostVerification"; + public static final String DISABLE_CERT_VERIFICATION = "disableCertificateVerification"; + public static final String TLS_HANDSHAKE_TIMEOUT = "TLSHandshakeTimeout"; + public static final String TLS_PROVIDER = "TLSProvider"; + public static final String USE_SYSTEM_TRUSTSTORE = "useSystemTrustStore"; + // Although all properties from the application are sent to the server (from the client), the following // sets of properties are used by the client and server respectively. These are reserved words. @@ -80,7 +91,10 @@ public final class DrillProperties extends Properties { ZOOKEEPER_CONNECTION, DRILLBIT_CONNECTION, TRIES, SCHEMA, USER, PASSWORD, IMPERSONATION_TARGET, AUTH_MECHANISM, - SERVICE_PRINCIPAL, SERVICE_NAME, SERVICE_HOST, REALM, KEYTAB, KERBEROS_FROM_SUBJECT + SERVICE_PRINCIPAL, SERVICE_NAME, SERVICE_HOST, REALM, KEYTAB, KERBEROS_FROM_SUBJECT, + ENABLE_TLS, TLS_PROTOCOL, TRUSTSTORE_TYPE, TRUSTSTORE_PATH, TRUSTSTORE_PASSWORD, + DISABLE_HOST_VERIFICATION, DISABLE_CERT_VERIFICATION, TLS_HANDSHAKE_TIMEOUT, TLS_PROVIDER, + USE_SYSTEM_TRUSTSTORE ); public static final ImmutableSet ACCEPTED_BY_SERVER = ImmutableSet.of( http://git-wip-us.apache.org/repos/asf/drill/blob/552d7d82/contrib/format-maprdb/pom.xml ---------------------------------------------------------------------- diff --git a/contrib/format-maprdb/pom.xml b/contrib/format-maprdb/pom.xml index ed59c3c..c4204f8 100644 --- a/contrib/format-maprdb/pom.xml +++ b/contrib/format-maprdb/pom.xml @@ -50,6 +50,15 @@ io.netty netty-all + + log4j + log4j + + + commons-logging + commons-logging + + @@ -122,14 +131,6 @@ log4j-over-slf4j org.slf4j - - io.netty - netty - - - io.netty - netty-all - @@ -152,16 +153,6 @@ ${project.version} tests test - - - io.netty - netty - - - io.netty - netty-all - - org.apache.drill @@ -174,14 +165,6 @@ log4j-over-slf4j org.slf4j - - io.netty - netty - - - io.netty - netty-all - @@ -190,16 +173,6 @@ ${project.version} tests test - - - io.netty - netty - - - io.netty - netty-all - - http://git-wip-us.apache.org/repos/asf/drill/blob/552d7d82/contrib/storage-hbase/pom.xml ---------------------------------------------------------------------- diff --git a/contrib/storage-hbase/pom.xml b/contrib/storage-hbase/pom.xml index 1188f97..b0dcac5 100644 --- a/contrib/storage-hbase/pom.xml +++ b/contrib/storage-hbase/pom.xml @@ -53,14 +53,6 @@ hadoop-client org.apache.hadoop - - io.netty - netty - - - io.netty - netty-all - @@ -71,16 +63,6 @@ tests ${project.version} test - - - io.netty - netty - - - io.netty - netty-all - - org.apache.drill @@ -88,16 +70,6 @@ tests ${project.version} test - - - io.netty - netty - - - io.netty - netty-all - - com.yammer.metrics @@ -205,6 +177,10 @@ org.apache.hbase hbase-client + + org.apache.hadoop + hadoop-mapreduce-client-core + io.netty netty @@ -213,6 +189,14 @@ io.netty netty-all + + log4j + log4j + + + commons-logging + commons-logging + @@ -230,14 +214,6 @@ commons-codec commons-codec - - io.netty - netty-all - - - io.netty - netty-parent - test @@ -254,14 +230,6 @@ commons-codec commons-codec - - io.netty - netty-all - - - io.netty - netty-parent - @@ -286,7 +254,15 @@ io.netty netty-all - + + log4j + log4j + + + commons-logging + commons-logging + + org.apache.hbase @@ -297,18 +273,6 @@ log4j log4j - - io.netty - netty - - - io.netty - netty-all - - - io.netty - netty-parent - http://git-wip-us.apache.org/repos/asf/drill/blob/552d7d82/distribution/pom.xml ---------------------------------------------------------------------- diff --git a/distribution/pom.xml b/distribution/pom.xml index fa08dd0..86c3d11 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -104,7 +104,34 @@ - + + io.netty + netty-tcnative + 2.0.1.Final + linux-x86_64 + test + + + io.netty + netty-tcnative + 2.0.1.Final + linux-x86_64-fedora + test + + + io.netty + netty-tcnative + 2.0.1.Final + osx-x86_64 + test + + + io.netty + netty-tcnative + 2.0.1.Final + windows-x86_64 + test + de.huxhorn.lilith de.huxhorn.lilith.logback.appender.multiplex-classic http://git-wip-us.apache.org/repos/asf/drill/blob/552d7d82/distribution/src/assemble/bin.xml ---------------------------------------------------------------------- diff --git a/distribution/src/assemble/bin.xml b/distribution/src/assemble/bin.xml index e428e6c..b0119d2 100644 --- a/distribution/src/assemble/bin.xml +++ b/distribution/src/assemble/bin.xml @@ -168,6 +168,8 @@ org.eclipse.jdt com.github.stephenc.high-scale-lib + io.netty:netty-tcnative + org.jamon @@ -193,6 +195,42 @@ test + + jars/3rdparty/linux + false + false + + io.netty:netty-tcnative:jar:linux-x86_64:2.0.1.Final + + test + + + jars/3rdparty/fedora + false + false + + io.netty:netty-tcnative:jar:linux-x86_64-fedora:2.0.1.Final + + test + + + jars/3rdparty/windows + false + false + + io.netty:netty-tcnative:jar:windows-x86_64:2.0.1.Final + + test + + + jars/3rdparty/osx + false + false + + io.netty:netty-tcnative:jar:osx-x86_64:2.0.1.Final + + test + winutils/bin http://git-wip-us.apache.org/repos/asf/drill/blob/552d7d82/distribution/src/resources/drill-config.sh ---------------------------------------------------------------------- diff --git a/distribution/src/resources/drill-config.sh b/distribution/src/resources/drill-config.sh index 92119e5..2341540 100644 --- a/distribution/src/resources/drill-config.sh +++ b/distribution/src/resources/drill-config.sh @@ -1,3 +1,4 @@ +#!/usr/bin/env bash # 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. @@ -309,6 +310,27 @@ fi CP="$CP:$DRILL_HOME/jars/3rdparty/*" CP="$CP:$DRILL_HOME/jars/classb/*" +#Followed by OS Specific jars +if [[ "$OSTYPE" == "linux-gnu" ]]; then + # Linux + # check for Fedora. netty-tcnative has a Fedora variant + linuxvariant=$(lsb_release -i | cut -d: -f2 | sed s/'^\t'//) + if [[ "$linuxvariant" == "Fedora" ]]; then + CP="$CP:$DRILL_HOME/jars/3rdparty/fedora/*" + else + CP="$CP:$DRILL_HOME/jars/3rdparty/linux/*" + fi +elif [[ "$OSTYPE" == "darwin"* ]]; then + # Mac OSX + CP="$CP:$DRILL_HOME/jars/3rdparty/osx/*" +elif [[ "$OSTYPE" == "cygwin" ]]; then + # Cygwin + CP="$CP:$DRILL_HOME/jars/3rdparty/windows/*" +elif [[ "$OSTYPE" == "msys" ]]; then + # Msys env on MinGW + CP="$CP:$DRILL_HOME/jars/3rdparty/windows/*" +fi + # Finally any user specified # Allow user jars to appear in $DRILL_CONF_DIR/jars to avoid mixing # user and Drill distribution jars. http://git-wip-us.apache.org/repos/asf/drill/blob/552d7d82/distribution/src/resources/drill-override-example.conf ---------------------------------------------------------------------- diff --git a/distribution/src/resources/drill-override-example.conf b/distribution/src/resources/drill-override-example.conf index 986e4b6..dbc9c8e 100644 --- a/distribution/src/resources/drill-override-example.conf +++ b/distribution/src/resources/drill-override-example.conf @@ -226,7 +226,23 @@ drill.exec: { # Full workspace name should be indicated (including schema and workspace separated by dot). # Workspace MUST be file-based and writable. Workspace name is case-sensitive. default_temporary_workspace: "dfs.tmp" -} + # Enable and provide additional parameters for Client-Server communication over SSL + # see also the javax.net.ssl parameters below + security.user.encryption.ssl: { + #Set this to true to enable all client server communication to occur over SSL. + enabled: false, + #key password is optional if it is the same as the keystore password + keyPassword: "key_passwd", + #Optional handshakeTimeout in milliseconds. Default is 10000 ms (10 seconds) + handshakeTimeout: 10000, + #protocol is optional. Drill will default to TLSv1.2. Valid values depend on protocol versions + # enabled for tje underlying securrity provider. For JSSE these are : SSL, SSLV2, SSLV3, + # TLS, TLSV1, TLSv1.1, TLSv1.2 + protocol: "TLSv1.2", + #ssl provider. May be "JDK" or "OPENSSL". Default is "JDK" + provider: "JDK" + } +} http://git-wip-us.apache.org/repos/asf/drill/blob/552d7d82/exec/java-exec/pom.xml ---------------------------------------------------------------------- diff --git a/exec/java-exec/pom.xml b/exec/java-exec/pom.xml index ab1e02e..88039a3 100644 --- a/exec/java-exec/pom.xml +++ b/exec/java-exec/pom.xml @@ -20,6 +20,9 @@ exec/Java Execution Engine 1.8-rev1 + + + fedora @@ -456,6 +459,14 @@ commons-codec commons-codec + + io.netty + netty + + + io.netty + netty-all + @@ -469,6 +480,14 @@ commons-codec + commons-logging + commons-logging + + + log4j + log4j + + io.netty netty @@ -486,14 +505,6 @@ commons-codec commons-codec - - io.netty - netty - - - io.netty - netty-all - @@ -576,6 +587,14 @@ libpam4j ${libpam4j.version} + + io.netty + netty-tcnative + 2.0.1.Final + provided + ${os.detected.classifier} + + @@ -595,6 +614,18 @@ org.apache.hadoop + commons-codec + commons-codec + + + commons-logging + commons-logging + + + log4j + log4j + + io.netty netty @@ -614,6 +645,14 @@ org.apache.hadoop + commons-logging + commons-logging + + + log4j + log4j + + io.netty netty @@ -658,6 +697,17 @@ + + + + kr.motd.maven + os-maven-plugin + 1.5.0.Final + + + maven-resources-plugin http://git-wip-us.apache.org/repos/asf/drill/blob/552d7d82/exec/java-exec/src/main/java/org/apache/drill/exec/ExecConstants.java ---------------------------------------------------------------------- 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 9b125cb..5c19e13 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 @@ -117,6 +117,18 @@ public final class ExecConstants { public static final String HASHAGG_FALLBACK_ENABLED_KEY = "drill.exec.hashagg.fallback.enabled"; public static final BooleanValidator HASHAGG_FALLBACK_ENABLED_VALIDATOR = new BooleanValidator(HASHAGG_FALLBACK_ENABLED_KEY); + public static final String SSL_PROVIDER = "drill.exec.ssl.provider"; // valid values are "JDK", "OPENSSL" // default JDK + public static final String SSL_PROTOCOL = "drill.exec.ssl.protocol"; // valid values are SSL, SSLV2, SSLV3, TLS, TLSV1, TLSv1.1, TLSv1.2(default) + public static final String SSL_KEYSTORE_TYPE = "drill.exec.ssl.keyStoreType"; + public static final String SSL_KEYSTORE_PATH = "drill.exec.ssl.keyStorePath"; // path to keystore. default : $JRE_HOME/lib/security/keystore.jks + public static final String SSL_KEYSTORE_PASSWORD = "drill.exec.ssl.keyStorePassword"; // default: changeit + public static final String SSL_KEY_PASSWORD = "drill.exec.ssl.keyPassword"; // + public static final String SSL_TRUSTSTORE_TYPE = "drill.exec.ssl.trustStoreType"; // valid values are jks(default), jceks, pkcs12 + public static final String SSL_TRUSTSTORE_PATH = "drill.exec.ssl.trustStorePath"; // path to keystore. default : $JRE_HOME/lib/security/cacerts.jks + public static final String SSL_TRUSTSTORE_PASSWORD = "drill.exec.ssl.trustStorePassword"; // default: changeit + public static final String SSL_USE_HADOOP_CONF = "drill.exec.ssl.useHadoopConfig"; // Initialize ssl params from hadoop if not provided by drill. default: true + public static final String SSL_HANDSHAKE_TIMEOUT = "drill.exec.security.user.encryption.ssl.handshakeTimeout"; // Default 10 seconds + public static final String TEXT_LINE_READER_BATCH_SIZE = "drill.exec.storage.file.text.batch.size"; public static final String TEXT_LINE_READER_BUFFER_SIZE = "drill.exec.storage.file.text.buffer.size"; public static final String HAZELCAST_SUBNETS = "drill.exec.cache.hazel.subnets"; @@ -133,10 +145,10 @@ public final class ExecConstants { public static final String HTTP_SESSION_MEMORY_RESERVATION = "drill.exec.http.session.memory.reservation"; public static final String HTTP_SESSION_MEMORY_MAXIMUM = "drill.exec.http.session.memory.maximum"; public static final String HTTP_SESSION_MAX_IDLE_SECS = "drill.exec.http.session_max_idle_secs"; - public static final String HTTP_KEYSTORE_PATH = "drill.exec.ssl.keyStorePath"; - public static final String HTTP_KEYSTORE_PASSWORD = "drill.exec.ssl.keyStorePassword"; - public static final String HTTP_TRUSTSTORE_PATH = "drill.exec.ssl.trustStorePath"; - public static final String HTTP_TRUSTSTORE_PASSWORD = "drill.exec.ssl.trustStorePassword"; + public static final String HTTP_KEYSTORE_PATH = SSL_KEYSTORE_PATH; + public static final String HTTP_KEYSTORE_PASSWORD = SSL_KEYSTORE_PASSWORD; + public static final String HTTP_TRUSTSTORE_PATH = SSL_TRUSTSTORE_PATH; + public static final String HTTP_TRUSTSTORE_PASSWORD = SSL_TRUSTSTORE_PASSWORD; public static final String SYS_STORE_PROVIDER_CLASS = "drill.exec.sys.store.provider.class"; public static final String SYS_STORE_PROVIDER_LOCAL_PATH = "drill.exec.sys.store.provider.local.path"; public static final String SYS_STORE_PROVIDER_LOCAL_ENABLE_WRITE = "drill.exec.sys.store.provider.local.write"; @@ -153,6 +165,8 @@ public final class ExecConstants { public static final String USE_LOGIN_PRINCIPAL = "drill.exec.security.bit.auth.use_login_principal"; public static final String USER_ENCRYPTION_SASL_ENABLED = "drill.exec.security.user.encryption.sasl.enabled"; public static final String USER_ENCRYPTION_SASL_MAX_WRAPPED_SIZE = "drill.exec.security.user.encryption.sasl.max_wrapped_size"; + + public static final String USER_SSL_ENABLED = "drill.exec.security.user.encryption.ssl.enabled"; public static final String BIT_ENCRYPTION_SASL_ENABLED = "drill.exec.security.bit.encryption.sasl.enabled"; public static final String BIT_ENCRYPTION_SASL_MAX_WRAPPED_SIZE = "drill.exec.security.bit.encryption.sasl.max_wrapped_size"; @@ -208,7 +222,7 @@ public final class ExecConstants { public static final OptionValidator PARQUET_DICT_PAGE_SIZE_VALIDATOR = new PositiveLongValidator(PARQUET_DICT_PAGE_SIZE, Integer.MAX_VALUE); public static final String PARQUET_WRITER_COMPRESSION_TYPE = "store.parquet.compression"; public static final OptionValidator PARQUET_WRITER_COMPRESSION_TYPE_VALIDATOR = new EnumeratedStringValidator( - PARQUET_WRITER_COMPRESSION_TYPE, "snappy", "gzip", "none"); + PARQUET_WRITER_COMPRESSION_TYPE, "snappy", "gzip", "none"); public static final String PARQUET_WRITER_ENABLE_DICTIONARY_ENCODING = "store.parquet.enable_dictionary_encoding"; public static final OptionValidator PARQUET_WRITER_ENABLE_DICTIONARY_ENCODING_VALIDATOR = new BooleanValidator( PARQUET_WRITER_ENABLE_DICTIONARY_ENCODING); @@ -464,7 +478,7 @@ public final class ExecConstants { */ public static final String IMPERSONATION_POLICIES_KEY = "exec.impersonation.inbound_policies"; public static final StringValidator IMPERSONATION_POLICY_VALIDATOR = - new InboundImpersonationManager.InboundImpersonationPolicyValidator(IMPERSONATION_POLICIES_KEY); + new InboundImpersonationManager.InboundImpersonationPolicyValidator(IMPERSONATION_POLICIES_KEY); /** @@ -546,5 +560,5 @@ public final class ExecConstants { public static String bootDefaultFor(String name) { return OPTION_DEFAULTS_ROOT + name; - } +} } http://git-wip-us.apache.org/repos/asf/drill/blob/552d7d82/exec/java-exec/src/main/java/org/apache/drill/exec/SSLConfig.java ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/SSLConfig.java b/exec/java-exec/src/main/java/org/apache/drill/exec/SSLConfig.java deleted file mode 100644 index c6d6374..0000000 --- a/exec/java-exec/src/main/java/org/apache/drill/exec/SSLConfig.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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; - -import com.typesafe.config.Config; -import org.apache.drill.common.exceptions.DrillException; - -public class SSLConfig { - - private final String keystorePath; - - private final String keystorePassword; - - private final String truststorePath; - - private final String truststorePassword; - - - public SSLConfig(Config config) throws DrillException { - - keystorePath = config.getString(ExecConstants.HTTP_KEYSTORE_PATH).trim(); - - keystorePassword = config.getString(ExecConstants.HTTP_KEYSTORE_PASSWORD).trim(); - - truststorePath = config.getString(ExecConstants.HTTP_TRUSTSTORE_PATH).trim(); - - truststorePassword = config.getString(ExecConstants.HTTP_TRUSTSTORE_PASSWORD).trim(); - - /*If keystorePath or keystorePassword is provided in the configuration file use that*/ - if (!keystorePath.isEmpty() || !keystorePassword.isEmpty()) { - if (keystorePath.isEmpty()) { - throw new DrillException(" *.ssl.keyStorePath in the configuration file is empty, but *.ssl.keyStorePassword is set"); - } - else if (keystorePassword.isEmpty()){ - throw new DrillException(" *.ssl.keyStorePassword in the configuration file is empty, but *.ssl.keyStorePath is set "); - } - - } - } - - public boolean isSslValid() { return !keystorePath.isEmpty() && !keystorePassword.isEmpty(); } - - public String getKeyStorePath() { return keystorePath; } - - public String getKeyStorePassword() { return keystorePassword; } - - public boolean hasTrustStorePath() { return !truststorePath.isEmpty(); } - - public boolean hasTrustStorePassword() { return !truststorePassword.isEmpty(); } - - public String getTrustStorePath() { return truststorePath; } - - public String getTrustStorePassword() { return truststorePassword; } -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/drill/blob/552d7d82/exec/java-exec/src/main/java/org/apache/drill/exec/client/DrillClient.java ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/client/DrillClient.java b/exec/java-exec/src/main/java/org/apache/drill/exec/client/DrillClient.java index 9fbbfdd..84b34a7 100644 --- a/exec/java-exec/src/main/java/org/apache/drill/exec/client/DrillClient.java +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/client/DrillClient.java @@ -367,8 +367,12 @@ public class DrillClient implements Closeable, ConnectionThrottle { DrillbitEndpoint endpoint; while (triedEndpointIndex < connectTriesVal) { - client = new UserClient(clientName, config, supportComplexTypes, allocator, eventLoopGroup, executor); endpoint = endpoints.get(triedEndpointIndex); + // Note: the properties member is a DrillProperties instance which lower cases names of + // properties. That does not work too well with properties that are mixed case. + // For user client severla properties are mixed case so we do not use the properties member + // but instead pass the props parameter. + client = new UserClient(clientName, config, props, supportComplexTypes, allocator, eventLoopGroup, executor, endpoint); logger.debug("Connecting to server {}:{}", endpoint.getAddress(), endpoint.getUserPort()); if (!properties.containsKey(DrillProperties.SERVICE_HOST)) { http://git-wip-us.apache.org/repos/asf/drill/blob/552d7d82/exec/java-exec/src/main/java/org/apache/drill/exec/rpc/user/UserClient.java ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/rpc/user/UserClient.java b/exec/java-exec/src/main/java/org/apache/drill/exec/rpc/user/UserClient.java index 2f47538..99614bd 100644 --- a/exec/java-exec/src/main/java/org/apache/drill/exec/rpc/user/UserClient.java +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/rpc/user/UserClient.java @@ -20,16 +20,25 @@ package org.apache.drill.exec.rpc.user; import java.io.IOException; import java.util.List; import java.util.Map; +import java.util.Properties; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import javax.net.ssl.SSLEngine; import javax.security.sasl.SaslClient; import javax.security.sasl.SaslException; +import io.netty.channel.ChannelPipeline; +import io.netty.handler.ssl.SslHandler; import org.apache.drill.common.KerberosUtil; import org.apache.drill.common.config.DrillConfig; import org.apache.drill.common.config.DrillProperties; +import org.apache.drill.common.exceptions.DrillException; +import org.apache.drill.exec.client.InvalidConnectionInfoException; +import org.apache.drill.exec.ssl.SSLConfig; import org.apache.drill.exec.memory.BufferAllocator; import org.apache.drill.exec.proto.CoordinationProtos.DrillbitEndpoint; import org.apache.drill.exec.proto.GeneralRPCProtos.Ack; @@ -55,6 +64,7 @@ import org.apache.drill.exec.proto.UserProtos.UserToBitHandshake; import org.apache.drill.exec.rpc.AbstractClientConnection; import org.apache.drill.exec.rpc.Acks; import org.apache.drill.exec.rpc.BasicClient; +import org.apache.drill.exec.rpc.ConnectionMultiListener; import org.apache.drill.exec.rpc.DrillRpcFuture; import org.apache.drill.exec.rpc.NonTransientRpcException; import org.apache.drill.exec.rpc.OutOfMemoryHandler; @@ -62,6 +72,7 @@ import org.apache.drill.exec.rpc.ProtobufLengthDecoder; import org.apache.drill.exec.rpc.Response; import org.apache.drill.exec.rpc.ResponseSender; import org.apache.drill.exec.rpc.RpcConnectionHandler; +import org.apache.drill.exec.rpc.RpcConstants; import org.apache.drill.exec.rpc.RpcException; import org.apache.drill.exec.rpc.RpcOutcomeListener; import org.apache.drill.exec.rpc.security.AuthStringUtil; @@ -70,7 +81,9 @@ import org.apache.drill.exec.rpc.security.AuthenticatorFactory; import org.apache.drill.exec.rpc.security.ClientAuthenticatorProvider; import org.apache.drill.exec.rpc.security.plain.PlainFactory; import org.apache.drill.exec.rpc.security.SaslProperties; +import org.apache.drill.exec.ssl.SSLConfigBuilder; import org.apache.hadoop.security.UserGroupInformation; +import org.apache.hadoop.security.ssl.SSLFactory; import org.slf4j.Logger; import com.google.common.base.Strings; @@ -87,8 +100,8 @@ import io.netty.buffer.ByteBuf; import io.netty.channel.EventLoopGroup; import io.netty.channel.socket.SocketChannel; -public class UserClient extends BasicClient { +public class UserClient + extends BasicClient { private static final Logger logger = org.slf4j.LoggerFactory.getLogger(UserClient.class); private final BufferAllocator allocator; @@ -102,19 +115,47 @@ public class UserClient extends BasicClient serverAuthMechanisms = null; private volatile boolean authComplete = true; - - public UserClient(String clientName, DrillConfig config, boolean supportComplexTypes, - BufferAllocator allocator, EventLoopGroup eventLoopGroup, Executor eventExecutor) { - super( - UserRpcConfig.getMapping(config, eventExecutor), - allocator.getAsByteBufAllocator(), - eventLoopGroup, - RpcType.HANDSHAKE, - BitToUserHandshake.class, - BitToUserHandshake.PARSER); + private SSLConfig sslConfig; + private DrillbitEndpoint endpoint; + + public UserClient(String clientName, DrillConfig config, Properties properties, boolean supportComplexTypes, + BufferAllocator allocator, EventLoopGroup eventLoopGroup, Executor eventExecutor, + DrillbitEndpoint endpoint) throws NonTransientRpcException { + super(UserRpcConfig.getMapping(config, eventExecutor), allocator.getAsByteBufAllocator(), + eventLoopGroup, RpcType.HANDSHAKE, BitToUserHandshake.class, BitToUserHandshake.PARSER); + this.endpoint = endpoint; // save the endpoint; it might be needed by SSL init. this.clientName = clientName; this.allocator = allocator; this.supportComplexTypes = supportComplexTypes; + try { + this.sslConfig = new SSLConfigBuilder().properties(properties).mode(SSLFactory.Mode.CLIENT) + .initializeSSLContext(true).validateKeyStore(false).build(); + } catch (DrillException e) { + throw new InvalidConnectionInfoException(e.getMessage()); + } + + } + + @Override protected void setupSSL(ChannelPipeline pipe, + ConnectionMultiListener.SSLHandshakeListener sslHandshakeListener) { + + String peerHost = endpoint.getAddress(); + int peerPort = endpoint.getUserPort(); + SSLEngine sslEngine = sslConfig.createSSLEngine(allocator, peerHost, peerPort); + + // Add SSL handler into pipeline + SslHandler sslHandler = new SslHandler(sslEngine); + sslHandler.setHandshakeTimeoutMillis(sslConfig.getHandshakeTimeout()); + + // Add a listener for SSL Handshake complete. The Drill client handshake will be enabled only + // after this is done. + sslHandler.handshakeFuture().addListener(sslHandshakeListener); + pipe.addFirst(RpcConstants.SSL_HANDLER, sslHandler); + logger.debug(sslConfig.toString()); + } + + @Override protected boolean isSslEnabled() { + return sslConfig.isUserSslEnabled(); } public RpcEndpointInfos getServerInfos() { @@ -132,38 +173,51 @@ public class UserClient extends BasicClient connect(final UserToBitHandshake handshake, - final DrillbitEndpoint endpoint) { + final DrillbitEndpoint endpoint) { final SettableFuture connectionSettable = SettableFuture.create(); final CheckedFuture connectionFuture = new AbstractCheckedFuture(connectionSettable) { - @Override - protected RpcException mapException(Exception e) { + @Override protected RpcException mapException(Exception e) { return RpcException.mapException(e); } }; final RpcConnectionHandler connectionHandler = new RpcConnectionHandler() { - @Override - public void connectionSucceeded(UserToBitConnection connection) { + @Override public void connectionSucceeded(UserToBitConnection connection) { connectionSettable.set(null); } - @Override - public void connectionFailed(FailureType type, Throwable t) { - connectionSettable.setException(new RpcException(String.format("%s : %s", - type.name(), t.getMessage()), t)); + @Override public void connectionFailed(FailureType type, Throwable t) { + connectionSettable + .setException(new RpcException(String.format("%s : %s", type.name(), t.getMessage()), t)); } }; - connectAsClient(queryResultHandler.getWrappedConnectionHandler(connectionHandler), - handshake, endpoint.getAddress(), endpoint.getUserPort()); + connectAsClient(queryResultHandler.getWrappedConnectionHandler(connectionHandler), handshake, + endpoint.getAddress(), endpoint.getUserPort()); return connectionFuture; } @@ -211,15 +262,15 @@ public class UserClient extends BasicClient authSettable = SettableFuture.create(); // use handleAuthFailure to setException + final SettableFuture authSettable = + SettableFuture.create(); // use handleAuthFailure to setException final CheckedFuture authFuture = new AbstractCheckedFuture(authSettable) { - @Override - protected SaslException mapException(Exception e) { + @Override protected SaslException mapException(Exception e) { if (e instanceof ExecutionException) { final Throwable cause = Throwables.getRootCause(e); if (cause instanceof SaslException) { @@ -227,8 +278,9 @@ public class UserClient extends BasicClient(this, connection, RpcType.SASL_MESSAGE, ugi, new RpcOutcomeListener() { - @Override - public void failed(RpcException ex) { + @Override public void failed(RpcException ex) { authSettable.setException(ex); } - @Override - public void success(Void value, ByteBuf buffer) { + @Override public void success(Void value, ByteBuf buffer) { authComplete = true; authSettable.set(null); } - @Override - public void interrupted(InterruptedException e) { + @Override public void interrupted(InterruptedException e) { authSettable.setException(e); } }).initiate(mechanismName); return authFuture; } - private AuthenticatorFactory getAuthenticatorFactory(final DrillProperties properties) throws SaslException { + private AuthenticatorFactory getAuthenticatorFactory(final DrillProperties properties) + throws SaslException { final Set mechanismSet = AuthStringUtil.asSet(serverAuthMechanisms); // first, check if a certain mechanism must be used @@ -285,129 +337,126 @@ public class UserClient extends BasicClient - void send(RpcOutcomeListener listener, RpcType rpcType, SEND protobufBody, Class clazz, - boolean allowInEventLoop, ByteBuf... dataBodies) { + protected void send( + RpcOutcomeListener listener, RpcType rpcType, SEND protobufBody, Class clazz, + boolean allowInEventLoop, ByteBuf... dataBodies) { super.send(listener, connection, rpcType, protobufBody, clazz, allowInEventLoop, dataBodies); } - @Override - protected MessageLite getResponseDefaultInstance(int rpcType) throws RpcException { + @Override protected MessageLite getResponseDefaultInstance(int rpcType) throws RpcException { switch (rpcType) { - case RpcType.ACK_VALUE: - return Ack.getDefaultInstance(); - case RpcType.HANDSHAKE_VALUE: - return BitToUserHandshake.getDefaultInstance(); - case RpcType.QUERY_HANDLE_VALUE: - return QueryId.getDefaultInstance(); - case RpcType.QUERY_RESULT_VALUE: - return QueryResult.getDefaultInstance(); - case RpcType.QUERY_DATA_VALUE: - return QueryData.getDefaultInstance(); - case RpcType.QUERY_PLAN_FRAGMENTS_VALUE: - return QueryPlanFragments.getDefaultInstance(); - case RpcType.CATALOGS_VALUE: - return GetCatalogsResp.getDefaultInstance(); - case RpcType.SCHEMAS_VALUE: - return GetSchemasResp.getDefaultInstance(); - case RpcType.TABLES_VALUE: - return GetTablesResp.getDefaultInstance(); - case RpcType.COLUMNS_VALUE: - return GetColumnsResp.getDefaultInstance(); - case RpcType.PREPARED_STATEMENT_VALUE: - return CreatePreparedStatementResp.getDefaultInstance(); - case RpcType.SASL_MESSAGE_VALUE: - return SaslMessage.getDefaultInstance(); - case RpcType.SERVER_META_VALUE: - return GetServerMetaResp.getDefaultInstance(); + case RpcType.ACK_VALUE: + return Ack.getDefaultInstance(); + case RpcType.HANDSHAKE_VALUE: + return BitToUserHandshake.getDefaultInstance(); + case RpcType.QUERY_HANDLE_VALUE: + return QueryId.getDefaultInstance(); + case RpcType.QUERY_RESULT_VALUE: + return QueryResult.getDefaultInstance(); + case RpcType.QUERY_DATA_VALUE: + return QueryData.getDefaultInstance(); + case RpcType.QUERY_PLAN_FRAGMENTS_VALUE: + return QueryPlanFragments.getDefaultInstance(); + case RpcType.CATALOGS_VALUE: + return GetCatalogsResp.getDefaultInstance(); + case RpcType.SCHEMAS_VALUE: + return GetSchemasResp.getDefaultInstance(); + case RpcType.TABLES_VALUE: + return GetTablesResp.getDefaultInstance(); + case RpcType.COLUMNS_VALUE: + return GetColumnsResp.getDefaultInstance(); + case RpcType.PREPARED_STATEMENT_VALUE: + return CreatePreparedStatementResp.getDefaultInstance(); + case RpcType.SASL_MESSAGE_VALUE: + return SaslMessage.getDefaultInstance(); + case RpcType.SERVER_META_VALUE: + return GetServerMetaResp.getDefaultInstance(); } throw new RpcException(String.format("Unable to deal with RpcType of %d", rpcType)); } - @Override - protected void handle(UserToBitConnection connection, int rpcType, ByteBuf pBody, ByteBuf dBody, - ResponseSender sender) throws RpcException { + @Override protected void handle(UserToBitConnection connection, int rpcType, ByteBuf pBody, ByteBuf dBody, + ResponseSender sender) throws RpcException { if (!authComplete) { // Remote should not be making any requests before authenticating, drop connection - throw new RpcException(String.format("Request of type %d is not allowed without authentication. " + - "Remote on %s must authenticate before making requests. Connection dropped.", - rpcType, connection.getRemoteAddress())); + throw new RpcException(String.format("Request of type %d is not allowed without authentication. " + + "Remote on %s must authenticate before making requests. Connection dropped.", rpcType, + connection.getRemoteAddress())); } switch (rpcType) { - case RpcType.QUERY_DATA_VALUE: - queryResultHandler.batchArrived(connection, pBody, dBody); - sender.send(new Response(RpcType.ACK, Acks.OK)); - break; - case RpcType.QUERY_RESULT_VALUE: - queryResultHandler.resultArrived(pBody); - sender.send(new Response(RpcType.ACK, Acks.OK)); - break; - default: - throw new RpcException(String.format("Unknown Rpc Type %d. ", rpcType)); + case RpcType.QUERY_DATA_VALUE: + queryResultHandler.batchArrived(connection, pBody, dBody); + sender.send(new Response(RpcType.ACK, Acks.OK)); + break; + case RpcType.QUERY_RESULT_VALUE: + queryResultHandler.resultArrived(pBody); + sender.send(new Response(RpcType.ACK, Acks.OK)); + break; + default: + throw new RpcException(String.format("Unknown Rpc Type %d. ", rpcType)); } } - @Override - protected void validateHandshake(BitToUserHandshake inbound) throws RpcException { -// logger.debug("Handling handshake from bit to user. {}", inbound); + @Override protected void validateHandshake(BitToUserHandshake inbound) throws RpcException { + // logger.debug("Handling handshake from bit to user. {}", inbound); if (inbound.hasServerInfos()) { serverInfos = inbound.getServerInfos(); } supportedMethods = Sets.immutableEnumSet(inbound.getSupportedMethodsList()); switch (inbound.getStatus()) { - case SUCCESS: - break; - case AUTH_REQUIRED: { - authComplete = false; - serverAuthMechanisms = ImmutableList.copyOf(inbound.getAuthenticationMechanismsList()); - connection.setEncryption(inbound.hasEncrypted() && inbound.getEncrypted()); - - if (inbound.hasMaxWrappedSize()) { - connection.setMaxWrappedSize(inbound.getMaxWrappedSize()); + case SUCCESS: + break; + case AUTH_REQUIRED: { + authComplete = false; + serverAuthMechanisms = ImmutableList.copyOf(inbound.getAuthenticationMechanismsList()); + connection.setEncryption(inbound.hasEncrypted() && inbound.getEncrypted()); + + if (inbound.hasMaxWrappedSize()) { + connection.setMaxWrappedSize(inbound.getMaxWrappedSize()); + } + logger.trace(String + .format("Server requires authentication with encryption context %s before proceeding.", + connection.getEncryptionCtxtString())); + break; } - logger.trace(String.format("Server requires authentication with encryption context %s before proceeding.", - connection.getEncryptionCtxtString())); - break; - } - case AUTH_FAILED: - case RPC_VERSION_MISMATCH: - case UNKNOWN_FAILURE: - final String errMsg = String.format("Status: %s, Error Id: %s, Error message: %s", - inbound.getStatus(), inbound.getErrorId(), inbound.getErrorMessage()); - logger.error(errMsg); - throw new NonTransientRpcException(errMsg); + case AUTH_FAILED: + case RPC_VERSION_MISMATCH: + case UNKNOWN_FAILURE: + final String errMsg = String + .format("Status: %s, Error Id: %s, Error message: %s", inbound.getStatus(), + inbound.getErrorId(), inbound.getErrorMessage()); + logger.error(errMsg); + throw new NonTransientRpcException(errMsg); } } - @Override - protected UserToBitConnection initRemoteConnection(SocketChannel channel) { + @Override protected UserToBitConnection initRemoteConnection(SocketChannel channel) { super.initRemoteConnection(channel); return new UserToBitConnection(channel); } @@ -421,39 +470,34 @@ public class UserClient extends BasicClient planQuery( - GetQueryPlanFragments req) { + public DrillRpcFuture planQuery(GetQueryPlanFragments req) { return send(RpcType.GET_QUERY_PLAN_FRAGMENTS, req, QueryPlanFragments.class); } } http://git-wip-us.apache.org/repos/asf/drill/blob/552d7d82/exec/java-exec/src/main/java/org/apache/drill/exec/rpc/user/UserConnectionConfig.java ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/rpc/user/UserConnectionConfig.java b/exec/java-exec/src/main/java/org/apache/drill/exec/rpc/user/UserConnectionConfig.java index 57ad4d5..4e4c80e 100644 --- a/exec/java-exec/src/main/java/org/apache/drill/exec/rpc/user/UserConnectionConfig.java +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/rpc/user/UserConnectionConfig.java @@ -33,6 +33,7 @@ class UserConnectionConfig extends AbstractConnectionConfig { private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(UserConnectionConfig.class); private final boolean authEnabled; + private final boolean sslEnabled; private final InboundImpersonationManager impersonationManager; private final UserServerRequestHandler handler; @@ -75,10 +76,16 @@ class UserConnectionConfig extends AbstractConnectionConfig { } else { authEnabled = false; } - impersonationManager = !config.getBoolean(ExecConstants.IMPERSONATION_ENABLED) ? null : new InboundImpersonationManager(); + + sslEnabled = config.getBoolean(ExecConstants.USER_SSL_ENABLED); + + if(isSSLEnabled() && isAuthEnabled() && isEncryptionEnabled()){ + logger.warn("The server is configured to use both SSL and SASL encryption (only one should be configured)."); + } + } @Override @@ -90,6 +97,10 @@ class UserConnectionConfig extends AbstractConnectionConfig { return authEnabled; } + boolean isSSLEnabled() { + return sslEnabled; + } + InboundImpersonationManager getImpersonationManager() { return impersonationManager; } http://git-wip-us.apache.org/repos/asf/drill/blob/552d7d82/exec/java-exec/src/main/java/org/apache/drill/exec/rpc/user/UserServer.java ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/rpc/user/UserServer.java b/exec/java-exec/src/main/java/org/apache/drill/exec/rpc/user/UserServer.java index 254fdca..c0573db 100644 --- a/exec/java-exec/src/main/java/org/apache/drill/exec/rpc/user/UserServer.java +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/rpc/user/UserServer.java @@ -21,9 +21,15 @@ import java.io.IOException; import java.net.SocketAddress; import java.util.UUID; +import javax.net.ssl.SSLEngine; import javax.security.sasl.SaslException; +import io.netty.channel.Channel; +import io.netty.channel.ChannelPipeline; +import io.netty.handler.ssl.SslHandler; import org.apache.drill.common.config.DrillProperties; +import org.apache.drill.common.exceptions.DrillException; +import org.apache.drill.exec.ssl.SSLConfig; import org.apache.drill.exec.exception.DrillbitStartupException; import org.apache.drill.exec.memory.BufferAllocator; import org.apache.drill.exec.physical.impl.materialize.QueryWritableBatch; @@ -53,8 +59,10 @@ import org.apache.drill.exec.rpc.security.plain.PlainFactory; import org.apache.drill.exec.rpc.user.UserServer.BitToUserConnection; import org.apache.drill.exec.rpc.user.security.UserAuthenticationException; import org.apache.drill.exec.server.BootStrapContext; +import org.apache.drill.exec.ssl.SSLConfigBuilder; import org.apache.drill.exec.work.user.UserWorker; import org.apache.hadoop.security.HadoopKerberosName; +import org.apache.hadoop.security.ssl.SSLFactory; import org.slf4j.Logger; import com.google.protobuf.MessageLite; @@ -71,6 +79,8 @@ public class UserServer extends BasicServer { private static final String SERVER_NAME = "Apache Drill Server"; private final UserConnectionConfig config; + private final SSLConfig sslConfig; + private Channel sslChannel; private final UserWorker userWorker; public UserServer(BootStrapContext context, BufferAllocator allocator, EventLoopGroup eventLoopGroup, @@ -79,6 +89,17 @@ public class UserServer extends BasicServer { allocator.getAsByteBufAllocator(), eventLoopGroup); this.config = new UserConnectionConfig(allocator, context, new UserServerRequestHandler(worker)); + this.sslChannel = null; + try { + this.sslConfig = new SSLConfigBuilder() + .config(context.getConfig()) + .mode(SSLFactory.Mode.SERVER) + .initializeSSLContext(true) + .validateKeyStore(true) + .build(); + } catch (DrillException e) { + throw new DrillbitStartupException(e.getMessage(), e.getCause()); + } this.userWorker = worker; // Initialize Singleton instance of UserRpcMetrics. @@ -86,6 +107,34 @@ public class UserServer extends BasicServer { } @Override + protected void setupSSL(ChannelPipeline pipe) { + + SSLEngine sslEngine = sslConfig.createSSLEngine(config.getAllocator(), null, 0); + // Add SSL handler into pipeline + pipe.addFirst(RpcConstants.SSL_HANDLER, new SslHandler(sslEngine)); + logger.debug("SSL communication between client and server is enabled."); + logger.debug(sslConfig.toString()); + + } + + @Override + protected boolean isSslEnabled() { + return sslConfig.isUserSslEnabled(); + } + + @Override + public void setSslChannel(Channel c) { + sslChannel = c; + } + + @Override + protected void closeSSL(){ + if(isSslEnabled() && sslChannel != null){ + sslChannel.close(); + } + } + + @Override protected MessageLite getResponseDefaultInstance(int rpcType) throws RpcException { // a user server only expects acknowledgments on messages it creates. switch (rpcType) { http://git-wip-us.apache.org/repos/asf/drill/blob/552d7d82/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/WebServer.java ---------------------------------------------------------------------- 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 69f2cab..e35f542 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 @@ -26,12 +26,14 @@ import org.apache.commons.lang3.StringUtils; import org.apache.drill.common.exceptions.DrillException; import org.apache.drill.common.config.DrillConfig; import org.apache.drill.exec.ExecConstants; -import org.apache.drill.exec.SSLConfig; +import org.apache.drill.exec.ssl.SSLConfig; import org.apache.drill.exec.exception.DrillbitStartupException; import org.apache.drill.exec.rpc.security.plain.PlainFactory; import org.apache.drill.exec.server.BootStrapContext; import org.apache.drill.exec.server.rest.auth.DrillRestLoginService; +import org.apache.drill.exec.ssl.SSLConfigBuilder; import org.apache.drill.exec.work.WorkManager; +import org.apache.hadoop.security.ssl.SSLFactory; import org.bouncycastle.asn1.x500.X500NameBuilder; import org.bouncycastle.asn1.x500.style.BCStyle; import org.bouncycastle.cert.X509v3CertificateBuilder; @@ -331,12 +333,18 @@ public class WebServer implements AutoCloseable { logger.info("Setting up HTTPS connector for web server"); final SslContextFactory sslContextFactory = new SslContextFactory(); - SSLConfig ssl = new SSLConfig(config); + SSLConfig ssl = new SSLConfigBuilder() + .config(config) + .mode(SSLFactory.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()){ http://git-wip-us.apache.org/repos/asf/drill/blob/552d7d82/exec/java-exec/src/main/java/org/apache/drill/exec/ssl/SSLConfig.java ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/ssl/SSLConfig.java b/exec/java-exec/src/main/java/org/apache/drill/exec/ssl/SSLConfig.java new file mode 100644 index 0000000..98a8c8d --- /dev/null +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/ssl/SSLConfig.java @@ -0,0 +1,265 @@ +/* + * 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.ssl; + +import io.netty.handler.ssl.SslContext; +import io.netty.handler.ssl.SslProvider; +import io.netty.handler.ssl.util.InsecureTrustManagerFactory; +import org.apache.drill.common.exceptions.DrillException; +import org.apache.drill.exec.memory.BufferAllocator; +import org.apache.hadoop.security.ssl.SSLFactory; + +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.TrustManagerFactory; +import java.io.FileInputStream; +import java.io.InputStream; +import java.security.KeyStore; + +public abstract class SSLConfig { + + private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(SSLConfig.class); + + public static final String DEFAULT_SSL_PROVIDER = "JDK"; // JDK or OPENSSL + public static final String DEFAULT_SSL_PROTOCOL = "TLSv1.2"; + public static final int DEFAULT_SSL_HANDSHAKE_TIMEOUT_MS = 10 * 1000; // 10 seconds + + // Either the Netty SSL context or the JDK SSL context will be initialized + // The JDK SSL context is use iff the useSystemTrustStore setting is enabled. + protected SslContext nettySslContext; + protected SSLContext jdkSSlContext; + + private static final boolean isWindows = System.getProperty("os.name").toLowerCase().indexOf("win") >= 0; + private static final boolean isMacOs = System.getProperty("os.name").toLowerCase().indexOf("mac") >= 0; + + public static final String HADOOP_SSL_CONF_TPL_KEY = "hadoop.ssl.{0}.conf"; + public static final String HADOOP_SSL_KEYSTORE_LOCATION_TPL_KEY = "ssl.{0}.keystore.location"; + public static final String HADOOP_SSL_KEYSTORE_PASSWORD_TPL_KEY = "ssl.{0}.keystore.password"; + public static final String HADOOP_SSL_KEYSTORE_TYPE_TPL_KEY = "ssl.{0}.keystore.type"; + public static final String HADOOP_SSL_KEYSTORE_KEYPASSWORD_TPL_KEY = + "ssl.{0}.keystore.keypassword"; + public static final String HADOOP_SSL_TRUSTSTORE_LOCATION_TPL_KEY = "ssl.{0}.truststore.location"; + public static final String HADOOP_SSL_TRUSTSTORE_PASSWORD_TPL_KEY = "ssl.{0}.truststore.password"; + public static final String HADOOP_SSL_TRUSTSTORE_TYPE_TPL_KEY = "ssl.{0}.truststore.type"; + + public SSLConfig() { + } + + public abstract void validateKeyStore() throws DrillException; + + // We need to use different SSLContext objects depending on what the user has chosen + // For most uses we will use the Netty SslContext class. This allows us to use either + // the JDK implementation or the OpenSSL implementation. However if the user wants to + // use the system trust store, then the only way to access it is via the JDK's + // SSLContext class. (See the createSSLEngine method below). + + public abstract SslContext initNettySslContext() throws DrillException; + + public abstract SSLContext initJDKSSLContext() throws DrillException; + + public abstract boolean isUserSslEnabled(); + + public abstract boolean isHttpsEnabled(); + + public abstract String getKeyStoreType(); + + public abstract String getKeyStorePath(); + + public abstract String getKeyStorePassword(); + + public abstract String getKeyPassword(); + + public abstract String getTrustStoreType(); + + public abstract boolean hasTrustStorePath(); + + public abstract String getTrustStorePath(); + + public abstract boolean hasTrustStorePassword(); + + public abstract String getTrustStorePassword(); + + public abstract String getProtocol(); + + public abstract SslProvider getProvider(); + + public abstract int getHandshakeTimeout(); + + public abstract SSLFactory.Mode getMode(); + + public abstract boolean disableHostVerification(); + + public abstract boolean disableCertificateVerification(); + + public abstract boolean useSystemTrustStore(); + + public abstract boolean isSslValid(); + + public SslContext getNettySslContext() { + return nettySslContext; + } + + public TrustManagerFactory initializeTrustManagerFactory() throws DrillException { + TrustManagerFactory tmf; + KeyStore ts = null; + //Support Windows/MacOs system trust store + try { + String trustStoreType = getTrustStoreType(); + if ((isWindows || isMacOs) && useSystemTrustStore()) { + // This is valid for MS-Windows and MacOs + logger.debug("Initializing System truststore."); + ts = KeyStore.getInstance(!trustStoreType.isEmpty() ? trustStoreType : KeyStore.getDefaultType()); + ts.load(null, null); + } else if (!getTrustStorePath().isEmpty()) { + // if truststore is not provided then we will use the default. Note that the default depends on + // the TrustManagerFactory that in turn depends on the Security Provider. + // Use null as the truststore which will result in the default truststore being picked up + logger.debug("Initializing truststore {}.", getTrustStorePath()); + ts = KeyStore.getInstance(!trustStoreType.isEmpty() ? trustStoreType : KeyStore.getDefaultType()); + InputStream tsStream = new FileInputStream(getTrustStorePath()); + ts.load(tsStream, getTrustStorePassword().toCharArray()); + } else { + logger.debug("Initializing default truststore."); + } + if (disableCertificateVerification()) { + tmf = InsecureTrustManagerFactory.INSTANCE; + } else { + tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + } + tmf.init(ts); + } catch (Exception e) { + // Catch any SSL initialization Exceptions here and abort. + throw new DrillException( + new StringBuilder() + .append("Exception while initializing the truststore: [") + .append(e.getMessage()) + .append("]. ") + .toString(), e); + } + return tmf; + } + + public KeyManagerFactory initializeKeyManagerFactory() throws DrillException { + KeyManagerFactory kmf; + String keyStorePath = getKeyStorePath(); + String keyStorePassword = getKeyStorePassword(); + String keyStoreType = getKeyStoreType(); + try { + if (keyStorePath.isEmpty()) { + throw new DrillException("No Keystore provided."); + } + KeyStore ks = + KeyStore.getInstance(!keyStoreType.isEmpty() ? keyStoreType : KeyStore.getDefaultType()); + //initialize the key manager factory + // Will throw an exception if the file is not found/accessible. + InputStream ksStream = new FileInputStream(keyStorePath); + // A key password CANNOT be null or an empty string. + if (keyStorePassword.isEmpty()) { + throw new DrillException("The Keystore password cannot be empty."); + } + ks.load(ksStream, keyStorePassword.toCharArray()); + // Empty Keystore. (Remarkably, it is possible to do this). + if (ks.size() == 0) { + throw new DrillException("The Keystore has no entries."); + } + kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + kmf.init(ks, getKeyPassword().toCharArray()); + + } catch (Exception e) { + throw new DrillException( + new StringBuilder() + .append("Exception while initializing the keystore: [") + .append(e.getMessage()) + .append("]. ") + .toString()); + } + return kmf; + } + + public void initContext() throws DrillException { + if ((isWindows || isMacOs) && useSystemTrustStore()) { + initJDKSSLContext(); + logger.debug("Initialized Windows/MacOs SSL context using JDK."); + } else { + initNettySslContext(); + logger.debug("Initialized SSL context."); + } + return; + } + + public SSLEngine createSSLEngine(BufferAllocator allocator, String peerHost, int peerPort) { + SSLEngine engine; + if ((isWindows || isMacOs) && useSystemTrustStore()) { + if (peerHost != null) { + engine = jdkSSlContext.createSSLEngine(peerHost, peerPort); + logger.debug("Initializing Windows/MacOs SSLEngine with hostname."); + } else { + engine = jdkSSlContext.createSSLEngine(); + logger.debug("Initializing Windows/MacOs SSLEngine with no hostname."); + } + } else { + if (peerHost != null) { + engine = nettySslContext.newEngine(allocator.getAsByteBufAllocator(), peerHost, peerPort); + logger.debug("Initializing SSLEngine with hostname."); + } else { + engine = nettySslContext.newEngine(allocator.getAsByteBufAllocator()); + logger.debug("Initializing SSLEngine with no hostname."); + } + } + return engine; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("SSL is ") + .append(isUserSslEnabled()?"":" not ") + .append("enabled.\n"); + sb.append("HTTPS is ") + .append(isHttpsEnabled()?"":" not ") + .append("enabled.\n"); + if(isUserSslEnabled() || isHttpsEnabled()) { + sb.append("SSL Configuration :") + .append("OS:").append(System.getProperty("os.name")) + .append("\n\tUsing system trust store: ").append(useSystemTrustStore()) + .append("\n\tprotocol: ").append(getProtocol()) + .append("\n\tkeyStoreType: ").append(getKeyStoreType()) + .append("\n\tkeyStorePath: ").append(getKeyStorePath()) + .append("\n\tkeyStorePassword: ").append(getPrintablePassword(getKeyStorePassword())) + .append("\n\tkeyPassword: ").append(getPrintablePassword(getKeyPassword())) + .append("\n\ttrustStoreType: ").append(getTrustStoreType()) + .append("\n\ttrustStorePath: ").append(getTrustStorePath()) + .append("\n\ttrustStorePassword: ").append(getPrintablePassword(getTrustStorePassword())) + .append("\n\thandshakeTimeout: ").append(getHandshakeTimeout()) + .append("\n\tdisableHostVerification: ").append(disableHostVerification()) + .append("\n\tdisableCertificateVerification: ").append(disableCertificateVerification()) + ; + } + return sb.toString(); + } + + private String getPrintablePassword(String password) { + StringBuilder sb = new StringBuilder(); + if(password == null || password.length()<2 ){ + return password; + } + sb.append(password.charAt(0)).append("****").append(password.charAt(password.length()-1)); + return sb.toString(); + } +} http://git-wip-us.apache.org/repos/asf/drill/blob/552d7d82/exec/java-exec/src/main/java/org/apache/drill/exec/ssl/SSLConfigBuilder.java ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/ssl/SSLConfigBuilder.java b/exec/java-exec/src/main/java/org/apache/drill/exec/ssl/SSLConfigBuilder.java new file mode 100644 index 0000000..0941960 --- /dev/null +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/ssl/SSLConfigBuilder.java @@ -0,0 +1,94 @@ +/* + * 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.ssl; + +import org.apache.drill.common.config.DrillConfig; +import org.apache.drill.common.exceptions.DrillConfigurationException; +import org.apache.drill.common.exceptions.DrillException; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.security.ssl.SSLFactory; + +import java.util.Properties; + + +public class SSLConfigBuilder { + + private static final org.slf4j.Logger logger = + org.slf4j.LoggerFactory.getLogger(org.apache.drill.exec.ssl.SSLConfigBuilder.class); + + private DrillConfig config = null; + private Configuration hadoopConfig = null; + private Properties properties; + private SSLFactory.Mode mode = SSLFactory.Mode.SERVER; + private boolean initializeSSLContext = false; + private boolean validateKeyStore = false; + + public SSLConfigBuilder() { + + } + + public SSLConfig build() throws DrillException { + if (mode == SSLFactory.Mode.SERVER && config == null) { + throw new DrillConfigurationException( + "Cannot create SSL configuration from null Drill configuration."); + } + SSLConfig sslConfig; + if (mode == SSLFactory.Mode.SERVER) { + sslConfig = new SSLConfigServer(config, hadoopConfig); + } else { + sslConfig = new SSLConfigClient(properties); + } + if(validateKeyStore){ + sslConfig.validateKeyStore(); + } + if(initializeSSLContext){ + sslConfig.initContext(); + } + return sslConfig; + } + + public SSLConfigBuilder config(DrillConfig config) { + this.config = config; + return this; + } + + public SSLConfigBuilder hadoopConfig(Configuration hadoopConfig) { + this.hadoopConfig = hadoopConfig; + return this; + } + + public SSLConfigBuilder properties(Properties props) { + this.properties = props; + return this; + } + + public SSLConfigBuilder mode(SSLFactory.Mode mode) { + this.mode = mode; + return this; + } + + public SSLConfigBuilder initializeSSLContext(boolean initializeSSLContext) { + this.initializeSSLContext = initializeSSLContext; + return this; + } + + public SSLConfigBuilder validateKeyStore(boolean validateKeyStore) { + this.validateKeyStore = validateKeyStore; + return this; + } +}