diff --git a/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java b/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java index 597fa1e..5f9d985 100644 --- a/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java +++ b/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java @@ -18,6 +18,13 @@ package org.apache.hive.jdbc; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; import java.sql.Array; import java.sql.Blob; import java.sql.CallableStatement; @@ -44,6 +51,7 @@ import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; +import javax.net.ssl.SSLContext; import javax.security.sasl.Sasl; import javax.security.sasl.SaslException; @@ -60,7 +68,11 @@ import org.apache.hive.service.cli.thrift.TOpenSessionResp; import org.apache.hive.service.cli.thrift.TProtocolVersion; import org.apache.hive.service.cli.thrift.TSessionHandle; -import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.HttpRequestInterceptor; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.conn.ssl.SSLContexts; import org.apache.thrift.TException; import org.apache.thrift.protocol.TBinaryProtocol; import org.apache.thrift.protocol.TProtocol; @@ -84,6 +96,8 @@ private static final String HIVE_USE_SSL = "ssl"; private static final String HIVE_SSL_TRUST_STORE = "sslTrustStore"; private static final String HIVE_SSL_TRUST_STORE_PASSWORD = "trustStorePassword"; + // TODO HIVE-6286: Add support for PKCS12 keystore format + private static final String HIVE_SSL_TRUST_STORE_TYPE = "JKS"; private final String jdbcURI; private final String host; @@ -162,22 +176,25 @@ private void openTransport() throws SQLException { } private TTransport createHttpTransport() throws SQLException { + CloseableHttpClient httpClient; // http path should begin with "/" String httpPath; - httpPath = hiveConfMap.get(HiveConf.ConfVars.HIVE_SERVER2_THRIFT_HTTP_PATH.varname); + httpPath = hiveConfMap.get( + HiveConf.ConfVars.HIVE_SERVER2_THRIFT_HTTP_PATH.varname); if(httpPath == null) { httpPath = "/"; } if(!httpPath.startsWith("/")) { httpPath = "/" + httpPath; } - - DefaultHttpClient httpClient = new DefaultHttpClient(); - String httpUrl = hiveConfMap.get(HiveConf.ConfVars.HIVE_SERVER2_TRANSPORT_MODE.varname) + - "://" + host + ":" + port + httpPath; - httpClient.addRequestInterceptor( - new HttpBasicAuthInterceptor(getUserName(), getPasswd()) - ); + Boolean useSsl = "true".equalsIgnoreCase(sessConfMap.get(HIVE_USE_SSL)); + // Create an http client from the configs + httpClient = getHttpClient(useSsl); + + // Create the http/https url + // JDBC driver will set up an https url if ssl is enabled, otherwise http + String schemeName = useSsl ? "https" : "http"; + String httpUrl = schemeName + "://" + host + ":" + port + httpPath; try { transport = new THttpClient(httpUrl, httpClient); } @@ -189,6 +206,45 @@ private TTransport createHttpTransport() throws SQLException { return transport; } + private CloseableHttpClient getHttpClient(Boolean useSsl) throws SQLException { + // Add an interceptor to pass username/password in the header + // for basic preemtive http authentication at the server + // In https mode, the entire information is encrypted + HttpRequestInterceptor authInterceptor = new HttpBasicAuthInterceptor( + getUserName(), getPasswd()); + if (useSsl) { + String sslTrustStorePath = sessConfMap.get(HIVE_SSL_TRUST_STORE); + String sslTrustStorePassword = sessConfMap.get( + HIVE_SSL_TRUST_STORE_PASSWORD); + KeyStore sslTrustStore; + SSLContext sslContext; + if (sslTrustStorePath == null || sslTrustStorePath.isEmpty()) { + // Create a default client context based on standard JSSE trust material + sslContext = SSLContexts.createDefault(); + } else { + // Pick trust store config from the given path + try { + sslTrustStore = KeyStore.getInstance(HIVE_SSL_TRUST_STORE_TYPE); + sslTrustStore.load(new FileInputStream(sslTrustStorePath), + sslTrustStorePassword.toCharArray()); + sslContext = SSLContexts.custom().loadTrustMaterial( + sslTrustStore).build(); + } + catch (Exception e) { + String msg = "Could not create an https connection to " + + jdbcURI + ". " + e.getMessage(); + throw new SQLException(msg, " 08S01", e); + } + } + return HttpClients.custom().setSslcontext( + sslContext).addInterceptorFirst(authInterceptor).build(); + } + else { + // Create a plain http client + return HttpClients.custom().addInterceptorFirst(authInterceptor).build(); + } + } + private TTransport createBinaryTransport() throws SQLException { try { // handle secure connection if specified @@ -201,8 +257,8 @@ private TTransport createBinaryTransport() throws SQLException { try { saslQOP = SaslQOP.fromString(sessConfMap.get(HIVE_AUTH_QOP)); } catch (IllegalArgumentException e) { - throw new SQLException("Invalid " + HIVE_AUTH_QOP + " parameter. " + e.getMessage(), - "42000", e); + throw new SQLException("Invalid " + HIVE_AUTH_QOP + + " parameter. " + e.getMessage(), "42000", e); } } saslProps.put(Sasl.QOP, saslQOP.toString()); diff --git a/pom.xml b/pom.xml index 41f5337..598f7b0 100644 --- a/pom.xml +++ b/pom.xml @@ -102,7 +102,7 @@ 0.96.0-hadoop1 0.96.0-hadoop2 - 4.2.5 + 4.3.2 4.2.4 1.9.2 0.3.2 @@ -111,6 +111,7 @@ 6.1.26 7.6.0.v20120127 + 7.6.0.v20120127 1.14 0.9.94 1.1 diff --git a/service/pom.xml b/service/pom.xml index dff3174..b1002e2 100644 --- a/service/pom.xml +++ b/service/pom.xml @@ -67,14 +67,9 @@ ${commons-logging.version} - org.mortbay.jetty - jetty - ${jetty.version} - - - org.mortbay.jetty - jetty-util - ${jetty.version} + org.eclipse.jetty.aggregate + jetty-all + ${jetty.hive-service.version} org.apache.thrift diff --git a/service/src/java/org/apache/hive/service/cli/thrift/ThriftCLIService.java b/service/src/java/org/apache/hive/service/cli/thrift/ThriftCLIService.java index b5a6138..26bda5a 100644 --- a/service/src/java/org/apache/hive/service/cli/thrift/ThriftCLIService.java +++ b/service/src/java/org/apache/hive/service/cli/thrift/ThriftCLIService.java @@ -59,7 +59,7 @@ protected int portNum; protected InetSocketAddress serverAddress; protected TServer server; - protected org.mortbay.jetty.Server httpServer; + protected org.eclipse.jetty.server.Server httpServer; private boolean isStarted = false; protected boolean isEmbedded = false; diff --git a/service/src/java/org/apache/hive/service/cli/thrift/ThriftHttpCLIService.java b/service/src/java/org/apache/hive/service/cli/thrift/ThriftHttpCLIService.java index e487a7f..33d8c0f 100644 --- a/service/src/java/org/apache/hive/service/cli/thrift/ThriftHttpCLIService.java +++ b/service/src/java/org/apache/hive/service/cli/thrift/ThriftHttpCLIService.java @@ -26,10 +26,12 @@ import org.apache.thrift.protocol.TBinaryProtocol; import org.apache.thrift.protocol.TProtocolFactory; import org.apache.thrift.server.TServlet; -import org.mortbay.jetty.nio.SelectChannelConnector; -import org.mortbay.jetty.servlet.Context; -import org.mortbay.jetty.servlet.ServletHolder; -import org.mortbay.thread.QueuedThreadPool; +import org.eclipse.jetty.server.nio.SelectChannelConnector; +import org.eclipse.jetty.server.ssl.SslSelectChannelConnector; +import org.eclipse.jetty.util.ssl.SslContextFactory; +import org.eclipse.jetty.util.thread.QueuedThreadPool; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHolder; public class ThriftHttpCLIService extends ThriftCLIService { @@ -74,16 +76,32 @@ public void run() { httpPath = httpPath + "/*"; } } - - httpServer = new org.mortbay.jetty.Server(); - + + httpServer = new org.eclipse.jetty.server.Server(); QueuedThreadPool threadPool = new QueuedThreadPool(); threadPool.setMinThreads(minWorkerThreads); threadPool.setMaxThreads(maxWorkerThreads); httpServer.setThreadPool(threadPool); - SelectChannelConnector connector = new SelectChannelConnector(); - connector.setPort(portNum); + SelectChannelConnector connector; + Boolean useSsl = hiveConf.getBoolVar(ConfVars.HIVE_SERVER2_USE_SSL); + String schemeName = useSsl ? "https" : "http"; + + if (useSsl) { + String keyStorePath = hiveConf.getVar(ConfVars.HIVE_SERVER2_SSL_KEYSTORE_PATH).trim(); + String keyStorePassword = hiveConf.getVar(ConfVars.HIVE_SERVER2_SSL_KEYSTORE_PASSWORD); + if (keyStorePath.isEmpty()) { + throw new IllegalArgumentException(ConfVars.HIVE_SERVER2_SSL_KEYSTORE_PATH.varname + + " Not configured for SSL connection"); + } + SslContextFactory sslContextFactory = new SslContextFactory(); + sslContextFactory.setKeyStorePath(keyStorePath); + sslContextFactory.setKeyStorePassword(keyStorePassword); + connector = new SslSelectChannelConnector(sslContextFactory); + } else { + connector = new SelectChannelConnector(); + } + connector.setPort(portNum); // Linux:yes, Windows:no connector.setReuseAddress(!Shell.WINDOWS); httpServer.addConnector(connector); @@ -93,12 +111,15 @@ public void run() { TProtocolFactory protocolFactory = new TBinaryProtocol.Factory(); TServlet thriftHttpServlet = new ThriftHttpServlet(processor, protocolFactory); - final Context context = new Context(httpServer, "/", Context.SESSIONS); + + final ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); + context.setContextPath("/"); + httpServer.setHandler(context); context.addServlet(new ServletHolder(thriftHttpServlet), httpPath); // TODO: check defaults: maxTimeout, keepalive, maxBodySize, bodyRecieveDuration, etc. httpServer.start(); - String msg = "Starting CLIService in Http mode on port " + portNum + + String msg = "Starting CLIService in " + schemeName + " mode on port " + portNum + " path=" + httpPath + " with " + minWorkerThreads + ".." + maxWorkerThreads + " worker threads"; LOG.info(msg);