You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hive.apache.org by jx...@apache.org on 2015/12/03 22:39:03 UTC
hive git commit: HIVE-12471: Secure HS2 web UI with SSL (Jimmy,
reviewed by Mohit, Szehon)
Repository: hive
Updated Branches:
refs/heads/master bdec08f9d -> 72c5d61f5
HIVE-12471: Secure HS2 web UI with SSL (Jimmy, reviewed by Mohit, Szehon)
Project: http://git-wip-us.apache.org/repos/asf/hive/repo
Commit: http://git-wip-us.apache.org/repos/asf/hive/commit/72c5d61f
Tree: http://git-wip-us.apache.org/repos/asf/hive/tree/72c5d61f
Diff: http://git-wip-us.apache.org/repos/asf/hive/diff/72c5d61f
Branch: refs/heads/master
Commit: 72c5d61f536e547c0c301855c221cc1ede6605f4
Parents: bdec08f
Author: Jimmy Xiang <jx...@apache.org>
Authored: Fri Nov 20 13:39:30 2015 -0800
Committer: Jimmy Xiang <jx...@apache.org>
Committed: Thu Dec 3 13:37:25 2015 -0800
----------------------------------------------------------------------
.../org/apache/hadoop/hive/conf/HiveConf.java | 6 +
.../java/org/apache/hive/http/HttpServer.java | 168 ++++++++++++++-----
.../apache/hive/service/server/HiveServer2.java | 41 +++--
3 files changed, 159 insertions(+), 56 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/hive/blob/72c5d61f/common/src/java/org/apache/hadoop/hive/conf/HiveConf.java
----------------------------------------------------------------------
diff --git a/common/src/java/org/apache/hadoop/hive/conf/HiveConf.java b/common/src/java/org/apache/hadoop/hive/conf/HiveConf.java
index 7f96071..c36d566 100644
--- a/common/src/java/org/apache/hadoop/hive/conf/HiveConf.java
+++ b/common/src/java/org/apache/hadoop/hive/conf/HiveConf.java
@@ -1848,6 +1848,12 @@ public class HiveConf extends Configuration {
HIVE_SERVER2_WEBUI_BIND_HOST("hive.server2.webui.host", "0.0.0.0", "The host address the HiveServer2 WebUI will listen on"),
HIVE_SERVER2_WEBUI_PORT("hive.server2.webui.port", 10002, "The port the HiveServer2 WebUI will listen on"),
HIVE_SERVER2_WEBUI_MAX_THREADS("hive.server2.webui.max.threads", 50, "The max HiveServer2 WebUI threads"),
+ HIVE_SERVER2_WEBUI_USE_SSL("hive.server2.webui.use.SSL", false,
+ "Set this to true for using SSL encryption for HiveServer2 WebUI."),
+ HIVE_SERVER2_WEBUI_SSL_KEYSTORE_PATH("hive.server2.webui.keystore.path", "",
+ "SSL certificate keystore location for HiveServer2 WebUI."),
+ HIVE_SERVER2_WEBUI_SSL_KEYSTORE_PASSWORD("hive.server2.webui.keystore.password", "",
+ "SSL certificate keystore password for HiveServer2 WebUI."),
// Tez session settings
HIVE_SERVER2_TEZ_DEFAULT_QUEUES("hive.server2.tez.default.queues", "",
http://git-wip-us.apache.org/repos/asf/hive/blob/72c5d61f/common/src/java/org/apache/hive/http/HttpServer.java
----------------------------------------------------------------------
diff --git a/common/src/java/org/apache/hive/http/HttpServer.java b/common/src/java/org/apache/hive/http/HttpServer.java
index 1ff8d7c..4b0ed68 100644
--- a/common/src/java/org/apache/hive/http/HttpServer.java
+++ b/common/src/java/org/apache/hive/http/HttpServer.java
@@ -21,6 +21,9 @@ package org.apache.hive.http;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServlet;
@@ -29,6 +32,8 @@ import javax.servlet.http.HttpServletResponse;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.CommonConfigurationKeys;
+import org.apache.hadoop.hive.conf.HiveConf;
+import org.apache.hadoop.hive.conf.HiveConf.ConfVars;
import org.apache.hadoop.security.UserGroupInformation;
import org.apache.hadoop.security.authorize.AccessControlList;
import org.apache.hadoop.util.Shell;
@@ -44,12 +49,18 @@ import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ContextHandler.Context;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.eclipse.jetty.server.nio.SelectChannelConnector;
+import org.eclipse.jetty.server.ssl.SslSelectChannelConnector;
import org.eclipse.jetty.servlet.DefaultServlet;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.eclipse.jetty.webapp.WebAppContext;
+import com.google.common.base.Splitter;
+import com.google.common.base.Strings;
+import com.google.common.collect.Sets;
+
/**
* A simple embedded Jetty server to serve as HS2/HMS web UI.
*/
@@ -57,32 +68,90 @@ public class HttpServer {
public static final String CONF_CONTEXT_ATTRIBUTE = "hive.conf";
public static final String ADMINS_ACL = "admins.acl";
- private final AccessControlList adminsAcl;
private final String appDir;
- private final String name;
- private final String host;
private final int port;
- private final int maxThreads;
- private final Configuration conf;
private final WebAppContext webAppContext;
private final Server webServer;
/**
* Create a status server on the given port.
*/
- public HttpServer(String name, String host, int port, int maxThreads,
- Configuration conf, AccessControlList adminsAcl) throws IOException {
- this.name = name;
- this.host = host;
- this.port = port;
- this.maxThreads = maxThreads;
- this.conf = conf;
- this.adminsAcl = adminsAcl;
+ private HttpServer(final Builder b) throws IOException {
+ this.port = b.port;
webServer = new Server();
- appDir = getWebAppsPath(name);
- webAppContext = createWebAppContext();
- initializeWebServer();
+ appDir = getWebAppsPath(b.name);
+ webAppContext = createWebAppContext(b);
+ initializeWebServer(b);
+ }
+
+ public static class Builder {
+ private String name;
+ private String host;
+ private int port;
+ private int maxThreads;
+ private HiveConf conf;
+ private Map<String, Object> contextAttrs = new HashMap<String, Object>();
+ private String keyStorePassword;
+ private String keyStorePath;
+ private boolean useSSL;
+
+ public HttpServer build() throws IOException {
+ return new HttpServer(this);
+ }
+
+ public Builder setConf(HiveConf conf) {
+ setContextAttribute(CONF_CONTEXT_ATTRIBUTE, conf);
+ this.conf = conf;
+ return this;
+ }
+
+ public Builder setName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ public Builder setHost(String host) {
+ this.host = host;
+ return this;
+ }
+
+ public Builder setPort(int port) {
+ this.port = port;
+ return this;
+ }
+
+ public Builder setMaxThreads(int maxThreads) {
+ this.maxThreads = maxThreads;
+ return this;
+ }
+
+ public Builder setAdmins(String admins) {
+ if (admins != null) {
+ setContextAttribute(ADMINS_ACL, new AccessControlList(admins));
+ }
+ return this;
+ }
+
+ public Builder setKeyStorePassword(String keyStorePassword) {
+ this.keyStorePassword = keyStorePassword;
+ return this;
+ }
+
+ public Builder setKeyStorePath(String keyStorePath) {
+ this.keyStorePath = keyStorePath;
+ return this;
+ }
+
+ public Builder setUseSSL(boolean useSSL) {
+ this.useSSL = useSSL;
+ return this;
+ }
+
+ public Builder setContextAttribute(String name, Object value) {
+ contextAttrs.put(name, value);
+ return this;
+ }
}
public void start() throws Exception {
@@ -98,13 +167,6 @@ public class HttpServer {
}
/**
- * Set servlet context attribute that can be used in jsp.
- */
- public void setContextAttribute(String name, Object value) {
- webAppContext.getServletContext().setAttribute(name, value);
- }
-
- /**
* Checks the user has privileges to access to instrumentation servlets.
* <p/>
* If <code>hadoop.security.instrumentation.requires.admin</code> is set to FALSE
@@ -195,44 +257,64 @@ public class HttpServer {
/**
* Create the web context for the application of specified name
*/
- WebAppContext createWebAppContext() {
+ WebAppContext createWebAppContext(Builder b) {
WebAppContext ctx = new WebAppContext();
- setContextAttributes(ctx.getServletContext());
- ctx.setDisplayName(name);
+ setContextAttributes(ctx.getServletContext(), b.contextAttrs);
+ ctx.setDisplayName(b.name);
ctx.setContextPath("/");
- ctx.setWar(appDir + "/" + name);
+ ctx.setWar(appDir + "/" + b.name);
return ctx;
}
/**
- * Create a default regular channel connector for "http" requests
+ * Create a channel connector for "http/https" requests
*/
- Connector createDefaultChannelConnector() {
- SelectChannelConnector connector = new SelectChannelConnector();
+ Connector createChannelConnector(int queueSize, Builder b) {
+ SelectChannelConnector connector;
+ if (!b.useSSL) {
+ connector = new SelectChannelConnector();
+ } else {
+ SslContextFactory sslContextFactory = new SslContextFactory();
+ sslContextFactory.setKeyStorePath(b.keyStorePath);
+ Set<String> excludedSSLProtocols = Sets.newHashSet(
+ Splitter.on(",").trimResults().omitEmptyStrings().split(
+ Strings.nullToEmpty(b.conf.getVar(ConfVars.HIVE_SSL_PROTOCOL_BLACKLIST))));
+ sslContextFactory.addExcludeProtocols(excludedSSLProtocols.toArray(
+ new String[excludedSSLProtocols.size()]));
+ sslContextFactory.setKeyStorePassword(b.keyStorePassword);
+ connector = new SslSelectChannelConnector(sslContextFactory);
+ }
+
connector.setLowResourcesMaxIdleTime(10000);
- connector.setAcceptQueueSize(maxThreads);
+ connector.setAcceptQueueSize(queueSize);
connector.setResolveNames(false);
connector.setUseDirectBuffers(false);
connector.setReuseAddress(!Shell.WINDOWS);
return connector;
}
- void setContextAttributes(Context ctx) {
- ctx.setAttribute(CONF_CONTEXT_ATTRIBUTE, conf);
- ctx.setAttribute(ADMINS_ACL, adminsAcl);
+ /**
+ * Set servlet context attributes that can be used in jsp.
+ */
+ void setContextAttributes(Context ctx, Map<String, Object> contextAttrs) {
+ for (Map.Entry<String, Object> e: contextAttrs.entrySet()) {
+ ctx.setAttribute(e.getKey(), e.getValue());
+ }
}
- void initializeWebServer() {
+ void initializeWebServer(Builder b) {
// Create the thread pool for the web server to handle HTTP requests
- QueuedThreadPool threadPool = maxThreads <= 0 ? new QueuedThreadPool()
- : new QueuedThreadPool(maxThreads);
+ QueuedThreadPool threadPool = new QueuedThreadPool();
+ if (b.maxThreads > 0) {
+ threadPool.setMaxThreads(b.maxThreads);
+ }
threadPool.setDaemon(true);
- threadPool.setName(name + "-web");
+ threadPool.setName(b.name + "-web");
webServer.setThreadPool(threadPool);
// Create the channel connector for the web server
- Connector connector = createDefaultChannelConnector();
- connector.setHost(host);
+ Connector connector = createChannelConnector(threadPool.getMaxThreads(), b);
+ connector.setHost(b.host);
connector.setPort(port);
webServer.addConnector(connector);
@@ -250,18 +332,18 @@ public class HttpServer {
staticCtx.addServlet(DefaultServlet.class, "/*");
staticCtx.setDisplayName("static");
- String logDir = getLogDir();
+ String logDir = getLogDir(b.conf);
if (logDir != null) {
ServletContextHandler logCtx =
new ServletContextHandler(contexts, "/logs");
- setContextAttributes(logCtx.getServletContext());
+ setContextAttributes(logCtx.getServletContext(), b.contextAttrs);
logCtx.addServlet(AdminAuthorizedServlet.class, "/*");
logCtx.setResourceBase(logDir);
logCtx.setDisplayName("logs");
}
}
- String getLogDir() {
+ String getLogDir(Configuration conf) {
String logDir = conf.get("hive.log.dir");
if (logDir == null) {
logDir = System.getProperty("hive.log.dir");
http://git-wip-us.apache.org/repos/asf/hive/blob/72c5d61f/service/src/java/org/apache/hive/service/server/HiveServer2.java
----------------------------------------------------------------------
diff --git a/service/src/java/org/apache/hive/service/server/HiveServer2.java b/service/src/java/org/apache/hive/service/server/HiveServer2.java
index 204eb5a..cad541a 100644
--- a/service/src/java/org/apache/hive/service/server/HiveServer2.java
+++ b/service/src/java/org/apache/hive/service/server/HiveServer2.java
@@ -43,20 +43,18 @@ import org.apache.curator.framework.api.CuratorEventType;
import org.apache.curator.framework.recipes.nodes.PersistentEphemeralNode;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.hadoop.hive.common.JvmPauseMonitor;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import org.apache.hadoop.hive.common.LogUtils;
import org.apache.hadoop.hive.common.LogUtils.LogInitializationException;
-import org.apache.hadoop.hive.common.metrics.common.MetricsFactory;
import org.apache.hadoop.hive.common.ServerUtils;
+import org.apache.hadoop.hive.common.metrics.common.MetricsFactory;
import org.apache.hadoop.hive.conf.HiveConf;
import org.apache.hadoop.hive.conf.HiveConf.ConfVars;
import org.apache.hadoop.hive.ql.exec.spark.session.SparkSessionManagerImpl;
import org.apache.hadoop.hive.ql.exec.tez.TezSessionPoolManager;
import org.apache.hadoop.hive.ql.util.ZooKeeperHiveHelper;
+import org.apache.hadoop.hive.shims.ShimLoader;
import org.apache.hadoop.hive.shims.Utils;
import org.apache.hadoop.security.UserGroupInformation;
-import org.apache.hadoop.security.authorize.AccessControlList;
import org.apache.hive.common.util.HiveStringUtils;
import org.apache.hive.common.util.HiveVersionInfo;
import org.apache.hive.http.HttpServer;
@@ -66,6 +64,7 @@ import org.apache.hive.service.cli.CLIService;
import org.apache.hive.service.cli.thrift.ThriftBinaryCLIService;
import org.apache.hive.service.cli.thrift.ThriftCLIService;
import org.apache.hive.service.cli.thrift.ThriftHttpCLIService;
+import org.apache.logging.log4j.util.Strings;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
@@ -73,6 +72,8 @@ import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooDefs.Perms;
import org.apache.zookeeper.data.ACL;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import com.google.common.base.Joiner;
@@ -130,17 +131,31 @@ public class HiveServer2 extends CompositeService {
if (webUIPort <= 0) {
LOG.info("Web UI is disabled since port is set to " + webUIPort);
} else {
- AccessControlList adminsAcl =
- new AccessControlList(hiveConf.getVar(ConfVars.USERS_IN_ADMIN_ROLE));
- hiveConf.set("startcode", String.valueOf(System.currentTimeMillis()));
- webServer = new HttpServer("hiveserver2",
- hiveConf.getVar(ConfVars.HIVE_SERVER2_WEBUI_BIND_HOST),
- webUIPort,
- hiveConf.getIntVar(ConfVars.HIVE_SERVER2_WEBUI_MAX_THREADS),
- hiveConf, adminsAcl);
+ HttpServer.Builder builder = new HttpServer.Builder();
+ builder.setName("hiveserver2").setPort(webUIPort).setConf(hiveConf);
+ builder.setHost(hiveConf.getVar(ConfVars.HIVE_SERVER2_WEBUI_BIND_HOST));
+ builder.setMaxThreads(
+ hiveConf.getIntVar(ConfVars.HIVE_SERVER2_WEBUI_MAX_THREADS));
+ builder.setAdmins(hiveConf.getVar(ConfVars.USERS_IN_ADMIN_ROLE));
// SessionManager is initialized
- webServer.setContextAttribute("hive.sm",
+ builder.setContextAttribute("hive.sm",
cliService.getSessionManager());
+ hiveConf.set("startcode",
+ String.valueOf(System.currentTimeMillis()));
+ if (hiveConf.getBoolVar(ConfVars.HIVE_SERVER2_WEBUI_USE_SSL)) {
+ String keyStorePath = hiveConf.getVar(
+ ConfVars.HIVE_SERVER2_WEBUI_SSL_KEYSTORE_PATH);
+ if (Strings.isBlank(keyStorePath)) {
+ throw new IllegalArgumentException(
+ ConfVars.HIVE_SERVER2_WEBUI_SSL_KEYSTORE_PATH.varname
+ + " Not configured for SSL connection");
+ }
+ builder.setKeyStorePassword(ShimLoader.getHadoopShims().getPassword(
+ hiveConf, ConfVars.HIVE_SERVER2_WEBUI_SSL_KEYSTORE_PASSWORD.varname));
+ builder.setKeyStorePath(keyStorePath);
+ builder.setUseSSL(true);
+ }
+ webServer = builder.build();
}
}
} catch (IOException ie) {