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 551639f..99b8815 100644
--- a/common/src/java/org/apache/hadoop/hive/conf/HiveConf.java
+++ b/common/src/java/org/apache/hadoop/hive/conf/HiveConf.java
@@ -887,6 +887,8 @@
HIVE_SERVER2_ALLOW_USER_SUBSTITUTION("hive.server2.allow.user.substitution", true),
HIVE_SERVER2_KERBEROS_KEYTAB("hive.server2.authentication.kerberos.keytab", ""),
HIVE_SERVER2_KERBEROS_PRINCIPAL("hive.server2.authentication.kerberos.principal", ""),
+ HIVE_SERVER2_SPNEGO_KEYTAB("hive.server2.authentication.spnego.keytab", ""),
+ HIVE_SERVER2_SPNEGO_PRINCIPAL("hive.server2.authentication.spnego.principal", ""),
HIVE_SERVER2_PLAIN_LDAP_URL("hive.server2.authentication.ldap.url", null),
HIVE_SERVER2_PLAIN_LDAP_BASEDN("hive.server2.authentication.ldap.baseDN", null),
HIVE_SERVER2_PLAIN_LDAP_DOMAIN("hive.server2.authentication.ldap.Domain", null),
diff --git a/conf/hive-default.xml.template b/conf/hive-default.xml.template
index 3c3df43..af0ff8d 100644
--- a/conf/hive-default.xml.template
+++ b/conf/hive-default.xml.template
@@ -2218,7 +2218,36 @@
Kerberos keytab file for server principal
-
+
+
+ hive.server2.authentication.spnego.principal
+
+
+ SPNego service principal, optional,
+ typical value would look like HTTP/_HOST@EXAMPLE.COM
+ SPNego service principal would be used by hiveserver2 when kerberos security is enabled
+ and HTTP transport mode is used.
+ This needs to be set only if SPNEGO is to be used in authentication.
+
+
+
+
+ hive.server2.authentication.spnego.keytab
+
+
+ keytab file for SPNego principal, optional,
+ typical value would look like /etc/security/keytabs/spnego.service.keytab,
+ This keytab would be used by hiveserver2 when kerberos security is enabled
+ and HTTP transport mode is used.
+ This needs to be set only if SPNEGO is to be used in authentication.
+ SPNego authentication would be honored only if valid
+ hive.server2.authentication.spnego.principal
+ and
+ hive.server2.authentication.spnego.keytab
+ are specified
+
+
+
hive.server2.authentication.ldap.url
diff --git a/service/src/java/org/apache/hive/service/auth/HiveAuthFactory.java b/service/src/java/org/apache/hive/service/auth/HiveAuthFactory.java
index 86d2009..d8f4822 100644
--- a/service/src/java/org/apache/hive/service/auth/HiveAuthFactory.java
+++ b/service/src/java/org/apache/hive/service/auth/HiveAuthFactory.java
@@ -186,7 +186,22 @@ public static void loginFromKeytab(HiveConf hiveConf) throws IOException {
if (!principal.isEmpty() && !keyTabFile.isEmpty()) {
ShimLoader.getHadoopShims().loginUserFromKeytab(principal, keyTabFile);
} else {
- throw new IOException ("HiveServer2 kerberos principal or keytab is not correctly configured");
+ throw new IOException ("HiveServer2 kerberos principal or keytab " +
+ "is not correctly configured");
+ }
+ }
+
+ // Perform spnego login using the hadoop shim API if the configuration is available
+ public static UserGroupInformation loginFromSpnegoKeytabAndReturnUGI(
+ HiveConf hiveConf) throws IOException {
+ String principal = hiveConf.getVar(ConfVars.HIVE_SERVER2_SPNEGO_PRINCIPAL);
+ String keyTabFile = hiveConf.getVar(ConfVars.HIVE_SERVER2_SPNEGO_KEYTAB);
+ if (!principal.isEmpty() && !keyTabFile.isEmpty()) {
+ return ShimLoader.getHadoopShims().loginUserFromKeytabAndReturnUGI(
+ principal, keyTabFile);
+ } else {
+ throw new IOException ("HiveServer2 SPNego principal or keytab " +
+ "is not correctly configured");
}
}
diff --git a/service/src/java/org/apache/hive/service/cli/CLIService.java b/service/src/java/org/apache/hive/service/cli/CLIService.java
index e31a74e..d01bce9 100644
--- a/service/src/java/org/apache/hive/service/cli/CLIService.java
+++ b/service/src/java/org/apache/hive/service/cli/CLIService.java
@@ -67,6 +67,7 @@
private SessionManager sessionManager;
private IMetaStoreClient metastoreClient;
private UserGroupInformation serviceUGI;
+ private UserGroupInformation httpUGI;
public CLIService() {
super("CLIService");
@@ -90,6 +91,21 @@ public synchronized void init(HiveConf hiveConf) {
} catch (LoginException e) {
throw new ServiceException("Unable to login to kerberos with given principal/keytab", e);
}
+
+ // Also try creating a UGI object for the SPNego principal
+ String principal = hiveConf.getVar(ConfVars.HIVE_SERVER2_SPNEGO_PRINCIPAL);
+ String keyTabFile = hiveConf.getVar(ConfVars.HIVE_SERVER2_SPNEGO_KEYTAB);
+ if (principal.isEmpty() || keyTabFile.isEmpty()) {
+ LOG.info("SPNego httpUGI not created, spNegoPrincipal: " + principal +
+ ", ketabFile: " + keyTabFile);
+ } else {
+ try {
+ this.httpUGI = HiveAuthFactory.loginFromSpnegoKeytabAndReturnUGI(hiveConf);
+ LOG.info("SPNego httpUGI successfully created.");
+ } catch (IOException e) {
+ LOG.warn("SPNego httpUGI creation failed: ", e);
+ }
+ }
}
super.init(hiveConf);
}
@@ -98,6 +114,10 @@ public UserGroupInformation getServiceUGI() {
return this.serviceUGI;
}
+ public UserGroupInformation getHttpUGI() {
+ return this.httpUGI;
+ }
+
@Override
public synchronized void start() {
super.start();
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 f4cbe91..98d75b5 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
@@ -76,7 +76,10 @@ public void run() {
String schemeName = useSsl ? "https" : "http";
String authType = hiveConf.getVar(ConfVars.HIVE_SERVER2_AUTHENTICATION);
// Set during the init phase of HiveServer2 if auth mode is kerberos
+ // UGI for the hive/_HOST (kerberos) principal
UserGroupInformation serviceUGI = cliService.getServiceUGI();
+ // UGI for the http/_HOST (SPNego) principal
+ UserGroupInformation httpUGI = cliService.getHttpUGI();
if (useSsl) {
String keyStorePath = hiveConf.getVar(ConfVars.HIVE_SERVER2_SSL_KEYSTORE_PATH).trim();
@@ -101,8 +104,9 @@ public void run() {
TProcessor processor = processorFactory.getProcessor(null);
TProtocolFactory protocolFactory = new TBinaryProtocol.Factory();
+
TServlet thriftHttpServlet = new ThriftHttpServlet(processor, protocolFactory,
- authType, serviceUGI);
+ authType, serviceUGI, httpUGI);
final ServletContextHandler context = new ServletContextHandler(
ServletContextHandler.SESSIONS);
diff --git a/service/src/java/org/apache/hive/service/cli/thrift/ThriftHttpServlet.java b/service/src/java/org/apache/hive/service/cli/thrift/ThriftHttpServlet.java
index 255a165..c579db5 100644
--- a/service/src/java/org/apache/hive/service/cli/thrift/ThriftHttpServlet.java
+++ b/service/src/java/org/apache/hive/service/cli/thrift/ThriftHttpServlet.java
@@ -58,12 +58,14 @@
public static final Log LOG = LogFactory.getLog(ThriftHttpServlet.class.getName());
private final String authType;
private final UserGroupInformation serviceUGI;
+ private final UserGroupInformation httpUGI;
public ThriftHttpServlet(TProcessor processor, TProtocolFactory protocolFactory,
- String authType, UserGroupInformation serviceUGI) {
+ String authType, UserGroupInformation serviceUGI, UserGroupInformation httpUGI) {
super(processor, protocolFactory);
this.authType = authType;
this.serviceUGI = serviceUGI;
+ this.httpUGI = httpUGI;
}
@Override
@@ -73,24 +75,25 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response)
try {
// For a kerberos setup
if(isKerberosAuthMode(authType)) {
- clientUserName = doKerberosAuth(request, serviceUGI);
+ clientUserName = doKerberosAuth(request);
}
else {
clientUserName = doPasswdAuth(request, authType);
}
LOG.info("Client username: " + clientUserName);
-
+
// Set the thread local username to be used for doAs if true
SessionManager.setUserName(clientUserName);
super.doPost(request, response);
}
catch (HttpAuthenticationException e) {
- // Send a 403 to the client
LOG.error("Error: ", e);
- response.setContentType("application/x-thrift");
- response.setStatus(HttpServletResponse.SC_FORBIDDEN);
- // Send the response back to the client
+ // Send a 401 to the client
+ response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+ if(isKerberosAuthMode(authType)) {
+ response.addHeader(HttpAuthUtils.WWW_AUTHENTICATE, HttpAuthUtils.NEGOTIATE);
+ }
response.getWriter().println("Authentication Error: " + e.getMessage());
}
finally {
@@ -127,24 +130,38 @@ private String doPasswdAuth(HttpServletRequest request, String authType)
* Do the GSS-API kerberos authentication.
* We already have a logged in subject in the form of serviceUGI,
* which GSS-API will extract information from.
+ * In case of a SPNego request we use the httpUGI,
+ * for the authenticating service tickets.
* @param request
* @return
* @throws HttpAuthenticationException
*/
- private String doKerberosAuth(HttpServletRequest request,
- UserGroupInformation serviceUGI) throws HttpAuthenticationException {
+ private String doKerberosAuth(HttpServletRequest request)
+ throws HttpAuthenticationException {
+ // Try authenticating with the http/_HOST principal
+ if (httpUGI != null) {
+ try {
+ return httpUGI.doAs(new HttpKerberosServerAction(request, httpUGI));
+ } catch (Exception e) {
+ LOG.info("Failed to authenticate with http/_HOST kerberos principal, " +
+ "trying with hive/_HOST kerberos principal");
+ }
+ }
+ // Now try with hive/_HOST principal
try {
return serviceUGI.doAs(new HttpKerberosServerAction(request, serviceUGI));
} catch (Exception e) {
+ LOG.error("Failed to authenticate with hive/_HOST kerberos principal");
throw new HttpAuthenticationException(e);
}
+
}
class HttpKerberosServerAction implements PrivilegedExceptionAction {
HttpServletRequest request;
UserGroupInformation serviceUGI;
-
- HttpKerberosServerAction(HttpServletRequest request,
+
+ HttpKerberosServerAction(HttpServletRequest request,
UserGroupInformation serviceUGI) {
this.request = request;
this.serviceUGI = serviceUGI;
@@ -152,14 +169,16 @@ private String doKerberosAuth(HttpServletRequest request,
@Override
public String run() throws HttpAuthenticationException {
- // Get own Kerberos credentials for accepting connection
+ // Get own Kerberos credentials for accepting connection
GSSManager manager = GSSManager.getInstance();
GSSContext gssContext = null;
String serverPrincipal = getPrincipalWithoutRealm(
serviceUGI.getUserName());
try {
// This Oid for Kerberos GSS-API mechanism.
- Oid mechOid = new Oid("1.2.840.113554.1.2.2");
+ Oid kerberosMechOid = new Oid("1.2.840.113554.1.2.2");
+ // Oid for SPNego GSS-API mechanism.
+ Oid spnegoMechOid = new Oid("1.3.6.1.5.5.2");
// Oid for kerberos principal name
Oid krb5PrincipalOid = new Oid("1.2.840.113554.1.2.2.1");
@@ -168,7 +187,9 @@ public String run() throws HttpAuthenticationException {
// GSS credentials for server
GSSCredential serverCreds = manager.createCredential(serverName,
- GSSCredential.DEFAULT_LIFETIME, mechOid, GSSCredential.ACCEPT_ONLY);
+ GSSCredential.DEFAULT_LIFETIME,
+ new Oid[]{kerberosMechOid, spnegoMechOid},
+ GSSCredential.ACCEPT_ONLY);
// Create a GSS context
gssContext = manager.createContext(serverCreds);
@@ -275,6 +296,7 @@ private String getAuthHeader(HttpServletRequest request, String authType)
private boolean isKerberosAuthMode(String authType) {
return authType.equalsIgnoreCase(HiveAuthFactory.AuthTypes.KERBEROS.toString());
}
+
}
diff --git a/shims/0.20/src/main/java/org/apache/hadoop/hive/shims/Hadoop20Shims.java b/shims/0.20/src/main/java/org/apache/hadoop/hive/shims/Hadoop20Shims.java
index 80247ec..7aae689 100644
--- a/shims/0.20/src/main/java/org/apache/hadoop/hive/shims/Hadoop20Shims.java
+++ b/shims/0.20/src/main/java/org/apache/hadoop/hive/shims/Hadoop20Shims.java
@@ -599,6 +599,13 @@ public void loginUserFromKeytab(String principal, String keytabFile) throws IOEx
}
@Override
+ public UserGroupInformation loginUserFromKeytabAndReturnUGI(
+ String principal, String keytabFile) throws IOException {
+ throwKerberosUnsupportedError();
+ return null;
+ }
+
+ @Override
public void reLoginUserFromKeytab() throws IOException{
throwKerberosUnsupportedError();
}
diff --git a/shims/common-secure/src/main/java/org/apache/hadoop/hive/shims/HadoopShimsSecure.java b/shims/common-secure/src/main/java/org/apache/hadoop/hive/shims/HadoopShimsSecure.java
index d4cddda..b7016a6 100644
--- a/shims/common-secure/src/main/java/org/apache/hadoop/hive/shims/HadoopShimsSecure.java
+++ b/shims/common-secure/src/main/java/org/apache/hadoop/hive/shims/HadoopShimsSecure.java
@@ -558,6 +558,13 @@ public void loginUserFromKeytab(String principal, String keytabFile) throws IOEx
}
@Override
+ public UserGroupInformation loginUserFromKeytabAndReturnUGI(
+ String principal, String keytabFile) throws IOException {
+ String hostPrincipal = SecurityUtil.getServerPrincipal(principal, "0.0.0.0");
+ return UserGroupInformation.loginUserFromKeytabAndReturnUGI(hostPrincipal, keytabFile);
+ }
+
+ @Override
public String getTokenFileLocEnvName() {
return UserGroupInformation.HADOOP_TOKEN_FILE_LOCATION;
}
diff --git a/shims/common/src/main/java/org/apache/hadoop/hive/shims/HadoopShims.java b/shims/common/src/main/java/org/apache/hadoop/hive/shims/HadoopShims.java
index 90c5602..c5fa1b7 100644
--- a/shims/common/src/main/java/org/apache/hadoop/hive/shims/HadoopShims.java
+++ b/shims/common/src/main/java/org/apache/hadoop/hive/shims/HadoopShims.java
@@ -311,6 +311,14 @@ public String addServiceToToken(String tokenStr, String tokenService)
public void loginUserFromKeytab(String principal, String keytabFile) throws IOException;
/**
+ * Perform kerberos login using the given principal and keytab,
+ * and return the UGI object
+ * @throws IOException
+ */
+ public UserGroupInformation loginUserFromKeytabAndReturnUGI(String principal,
+ String keytabFile) throws IOException;
+
+ /**
* Perform kerberos re-login using the given principal and keytab, to renew
* the credentials
* @throws IOException