This is an automated email from the ASF dual-hosted git repository. elserj pushed a commit to branch branch-2.1 in repository https://gitbox.apache.org/repos/asf/hbase.git
The following commit(s) were added to refs/heads/branch-2.1 by this push: new 95ebb59 HBASE-23312 HBase Thrift SPNEGO configs (HBASE-19852) should be backwards compatible 95ebb59 is described below commit 95ebb5981e5100a94828d266f86db9e5a4e449b8 Author: Kevin Risden <kris...@apache.org> AuthorDate: Mon Nov 25 17:44:33 2019 -0500 HBASE-23312 HBase Thrift SPNEGO configs (HBASE-19852) should be backwards compatible HBase Thrift SPNEGO configs should not be required. The `hbase.thrift.spnego.keytab.file` and `hbase.thrift.spnego.principal` configs should fall back to the `hbase.thrift.keytab.file` and `hbase.thrift.kerberos.principal` configs. This will avoid any issues during upgrades. Signed-off-by: Josh Elser <els...@apache.org> Closes #878 --- .../hadoop/hbase/thrift/ThriftHttpServlet.java | 21 +--- .../hadoop/hbase/thrift/ThriftServerRunner.java | 46 +++++++- ...ava => TestThriftSpnegoHttpFallbackServer.java} | 120 ++++++++------------- .../hbase/thrift/TestThriftSpnegoHttpServer.java | 100 +++++++---------- 4 files changed, 133 insertions(+), 154 deletions(-) diff --git a/hbase-thrift/src/main/java/org/apache/hadoop/hbase/thrift/ThriftHttpServlet.java b/hbase-thrift/src/main/java/org/apache/hadoop/hbase/thrift/ThriftHttpServlet.java index 4c9a35b..7458162 100644 --- a/hbase-thrift/src/main/java/org/apache/hadoop/hbase/thrift/ThriftHttpServlet.java +++ b/hbase-thrift/src/main/java/org/apache/hadoop/hbase/thrift/ThriftHttpServlet.java @@ -18,9 +18,6 @@ package org.apache.hadoop.hbase.thrift; -import static org.apache.hadoop.hbase.thrift.ThriftServerRunner.THRIFT_SPNEGO_KEYTAB_FILE_KEY; -import static org.apache.hadoop.hbase.thrift.ThriftServerRunner.THRIFT_SPNEGO_PRINCIPAL_KEY; - import java.io.IOException; import java.security.PrivilegedExceptionAction; import java.util.Base64; @@ -29,7 +26,6 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.security.SecurityUtil; import org.apache.hadoop.security.UserGroupInformation; import org.apache.hadoop.security.authorize.AuthorizationException; @@ -66,25 +62,14 @@ public class ThriftHttpServlet extends TServlet { public static final String NEGOTIATE = "Negotiate"; public ThriftHttpServlet(TProcessor processor, TProtocolFactory protocolFactory, - UserGroupInformation serviceUGI, Configuration conf, - ThriftServerRunner.HBaseHandler hbaseHandler, boolean securityEnabled, boolean doAsEnabled) - throws IOException { + UserGroupInformation serviceUGI, UserGroupInformation httpUGI, + ThriftServerRunner.HBaseHandler hbaseHandler, boolean securityEnabled, boolean doAsEnabled) { super(processor, protocolFactory); this.serviceUGI = serviceUGI; + this.httpUGI = httpUGI; this.hbaseHandler = hbaseHandler; this.securityEnabled = securityEnabled; this.doAsEnabled = doAsEnabled; - - if (securityEnabled) { - // login the spnego principal - UserGroupInformation.setConfiguration(conf); - this.httpUGI = UserGroupInformation.loginUserFromKeytabAndReturnUGI( - conf.get(THRIFT_SPNEGO_PRINCIPAL_KEY), - conf.get(THRIFT_SPNEGO_KEYTAB_FILE_KEY) - ); - } else { - this.httpUGI = null; - } } @Override diff --git a/hbase-thrift/src/main/java/org/apache/hadoop/hbase/thrift/ThriftServerRunner.java b/hbase-thrift/src/main/java/org/apache/hadoop/hbase/thrift/ThriftServerRunner.java index b510ff5..0c34213 100644 --- a/hbase-thrift/src/main/java/org/apache/hadoop/hbase/thrift/ThriftServerRunner.java +++ b/hbase-thrift/src/main/java/org/apache/hadoop/hbase/thrift/ThriftServerRunner.java @@ -224,6 +224,7 @@ public class ThriftServerRunner implements Runnable { private final ThriftMetrics metrics; private final HBaseHandler hbaseHandler; private final UserGroupInformation serviceUGI; + private UserGroupInformation httpUGI; private SaslUtil.QualityOfProtection qop; private String host; @@ -349,8 +350,19 @@ public class ThriftServerRunner implements Runnable { conf.get(THRIFT_DNS_INTERFACE_KEY, "default"), conf.get(THRIFT_DNS_NAMESERVER_KEY, "default"))); userProvider.login(THRIFT_KEYTAB_FILE_KEY, THRIFT_KERBEROS_PRINCIPAL_KEY, host); + + // Setup the SPNEGO user for HTTP if configured + String spnegoPrincipal = getSpengoPrincipal(conf, host); + String spnegoKeytab = getSpnegoKeytab(conf); + UserGroupInformation.setConfiguration(conf); + // login the SPNEGO principal using UGI to avoid polluting the login user + this.httpUGI = UserGroupInformation.loginUserFromKeytabAndReturnUGI(spnegoPrincipal, + spnegoKeytab); } this.serviceUGI = userProvider.getCurrent().getUGI(); + if (httpUGI == null) { + this.httpUGI = serviceUGI; + } this.conf = HBaseConfiguration.create(conf); this.listenPort = conf.getInt(PORT_CONF_KEY, DEFAULT_LISTEN_PORT); @@ -387,6 +399,38 @@ public class ThriftServerRunner implements Runnable { } } + + private String getSpengoPrincipal(Configuration conf, String host) throws IOException { + String principal = conf.get(THRIFT_SPNEGO_PRINCIPAL_KEY); + if (principal == null) { + // We cannot use the Hadoop configuration deprecation handling here since + // the THRIFT_KERBEROS_PRINCIPAL_KEY config is still valid for regular Kerberos + // communication. The preference should be to use the THRIFT_SPNEGO_PRINCIPAL_KEY + // config so that THRIFT_KERBEROS_PRINCIPAL_KEY doesn't control both backend + // Kerberos principal and SPNEGO principal. + LOG.info("Using deprecated {} config for SPNEGO principal. Use {} instead.", + THRIFT_KERBEROS_PRINCIPAL_KEY, THRIFT_SPNEGO_PRINCIPAL_KEY); + principal = conf.get(THRIFT_KERBEROS_PRINCIPAL_KEY); + } + // Handle _HOST in principal value + return org.apache.hadoop.security.SecurityUtil.getServerPrincipal(principal, host); + } + + private String getSpnegoKeytab(Configuration conf) { + String keytab = conf.get(THRIFT_SPNEGO_KEYTAB_FILE_KEY); + if (keytab == null) { + // We cannot use the Hadoop configuration deprecation handling here since + // the THRIFT_KEYTAB_FILE_KEY config is still valid for regular Kerberos + // communication. The preference should be to use the THRIFT_SPNEGO_KEYTAB_FILE_KEY + // config so that THRIFT_KEYTAB_FILE_KEY doesn't control both backend + // Kerberos keytab and SPNEGO keytab. + LOG.info("Using deprecated {} config for SPNEGO keytab. Use {} instead.", + THRIFT_KEYTAB_FILE_KEY, THRIFT_SPNEGO_KEYTAB_FILE_KEY); + keytab = conf.get(THRIFT_KEYTAB_FILE_KEY); + } + return keytab; + } + private void checkHttpSecurity(QualityOfProtection qop, Configuration conf) { if (qop == QualityOfProtection.PRIVACY && conf.getBoolean(USE_HTTP_CONF_KEY, false) && @@ -448,7 +492,7 @@ public class ThriftServerRunner implements Runnable { TProtocolFactory protocolFactory = new TBinaryProtocol.Factory(); TProcessor processor = new Hbase.Processor<>(handler); TServlet thriftHttpServlet = new ThriftHttpServlet(processor, protocolFactory, serviceUGI, - conf, hbaseHandler, securityEnabled, doAsEnabled); + httpUGI, hbaseHandler, securityEnabled, doAsEnabled); // Set the default max thread number to 100 to limit // the number of concurrent requests so that Thrfit HTTP server doesn't OOM easily. diff --git a/hbase-thrift/src/test/java/org/apache/hadoop/hbase/thrift/TestThriftSpnegoHttpServer.java b/hbase-thrift/src/test/java/org/apache/hadoop/hbase/thrift/TestThriftSpnegoHttpFallbackServer.java similarity index 67% copy from hbase-thrift/src/test/java/org/apache/hadoop/hbase/thrift/TestThriftSpnegoHttpServer.java copy to hbase-thrift/src/test/java/org/apache/hadoop/hbase/thrift/TestThriftSpnegoHttpFallbackServer.java index 1218d18..c8667e4 100644 --- a/hbase-thrift/src/test/java/org/apache/hadoop/hbase/thrift/TestThriftSpnegoHttpServer.java +++ b/hbase-thrift/src/test/java/org/apache/hadoop/hbase/thrift/TestThriftSpnegoHttpFallbackServer.java @@ -1,34 +1,27 @@ /* - * Copyright The Apache Software Foundation + * 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 * - * 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 + * 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. + * 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.hadoop.hbase.thrift; -import static org.apache.hadoop.hbase.thrift.ThriftServerRunner.THRIFT_KERBEROS_PRINCIPAL_KEY; -import static org.apache.hadoop.hbase.thrift.ThriftServerRunner.THRIFT_KEYTAB_FILE_KEY; -import static org.apache.hadoop.hbase.thrift.ThriftServerRunner.THRIFT_SPNEGO_KEYTAB_FILE_KEY; -import static org.apache.hadoop.hbase.thrift.ThriftServerRunner.THRIFT_SPNEGO_PRINCIPAL_KEY; -import static org.apache.hadoop.hbase.thrift.ThriftServerRunner.THRIFT_SUPPORT_PROXYUSER_KEY; -import static org.apache.hadoop.hbase.thrift.ThriftServerRunner.USE_HTTP_CONF_KEY; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; import java.io.File; +import java.nio.file.Paths; import java.security.Principal; import java.security.PrivilegedExceptionAction; import java.util.Set; @@ -36,7 +29,6 @@ import java.util.Set; import javax.security.auth.Subject; import javax.security.auth.kerberos.KerberosTicket; -import org.apache.commons.io.FileUtils; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.HBaseClassTestRule; import org.apache.hadoop.hbase.HBaseTestingUtility; @@ -58,7 +50,6 @@ import org.apache.http.impl.auth.SPNegoSchemeFactory; import org.apache.http.impl.client.BasicCredentialsProvider; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; -import org.apache.kerby.kerberos.kerb.KrbException; import org.apache.kerby.kerberos.kerb.client.JaasKrbUtil; import org.apache.kerby.kerberos.kerb.server.SimpleKdcServer; import org.apache.thrift.protocol.TBinaryProtocol; @@ -78,39 +69,32 @@ import org.slf4j.LoggerFactory; /** * Start the HBase Thrift HTTP server on a random port through the command-line * interface and talk to it from client side with SPNEGO security enabled. + * + * Supplemental test to TestThriftSpnegoHttpServer which falls back to the original + * Kerberos principal and keytab configuration properties, not the separate + * SPNEGO-specific properties. */ @Category({ClientTests.class, LargeTests.class}) -public class TestThriftSpnegoHttpServer extends TestThriftHttpServer { +public class TestThriftSpnegoHttpFallbackServer extends TestThriftHttpServer { @ClassRule public static final HBaseClassTestRule CLASS_RULE = - HBaseClassTestRule.forClass(TestThriftSpnegoHttpServer.class); + HBaseClassTestRule.forClass(TestThriftSpnegoHttpFallbackServer.class); private static final Logger LOG = - LoggerFactory.getLogger(TestThriftSpnegoHttpServer.class); + LoggerFactory.getLogger(TestThriftSpnegoHttpFallbackServer.class); private static SimpleKdcServer kdc; private static File serverKeytab; - private static File spnegoServerKeytab; private static File clientKeytab; private static String clientPrincipal; private static String serverPrincipal; private static String spnegoServerPrincipal; - private static void setupUser(SimpleKdcServer kdc, File keytab, String principal) - throws KrbException { - kdc.createPrincipal(principal); - kdc.exportPrincipal(principal, keytab); - } - private static SimpleKdcServer buildMiniKdc() throws Exception { SimpleKdcServer kdc = new SimpleKdcServer(); - final File target = new File(System.getProperty("user.dir"), "target"); - File kdcDir = new File(target, TestThriftSpnegoHttpServer.class.getSimpleName()); - if (kdcDir.exists()) { - FileUtils.deleteDirectory(kdcDir); - } + File kdcDir = Paths.get(TEST_UTIL.getRandomDir().toString()).toAbsolutePath().toFile(); kdcDir.mkdirs(); kdc.setWorkDir(kdcDir); @@ -131,66 +115,55 @@ public class TestThriftSpnegoHttpServer extends TestThriftHttpServer { KerberosName.setRules("DEFAULT"); HBaseKerberosUtils.setKeytabFileForTesting(serverKeytab.getAbsolutePath()); - HBaseKerberosUtils.setPrincipalForTesting(serverPrincipal); + HBaseKerberosUtils.setPrincipalForTesting(spnegoServerPrincipal); HBaseKerberosUtils.setSecuredConfiguration(conf); // if we drop support for hadoop-2.4.0 and hadoop-2.4.1, // the following key should be changed. // 1) DFS_NAMENODE_USER_NAME_KEY -> DFS_NAMENODE_KERBEROS_PRINCIPAL_KEY // 2) DFS_DATANODE_USER_NAME_KEY -> DFS_DATANODE_KERBEROS_PRINCIPAL_KEY - conf.set(DFSConfigKeys.DFS_NAMENODE_USER_NAME_KEY, serverPrincipal); + conf.set(DFSConfigKeys.DFS_NAMENODE_USER_NAME_KEY, spnegoServerPrincipal); conf.set(DFSConfigKeys.DFS_NAMENODE_KEYTAB_FILE_KEY, serverKeytab.getAbsolutePath()); - conf.set(DFSConfigKeys.DFS_DATANODE_USER_NAME_KEY, serverPrincipal); + conf.set(DFSConfigKeys.DFS_DATANODE_USER_NAME_KEY, spnegoServerPrincipal); conf.set(DFSConfigKeys.DFS_DATANODE_KEYTAB_FILE_KEY, serverKeytab.getAbsolutePath()); conf.setBoolean(DFSConfigKeys.DFS_BLOCK_ACCESS_TOKEN_ENABLE_KEY, true); conf.set(DFSConfigKeys.DFS_WEB_AUTHENTICATION_KERBEROS_PRINCIPAL_KEY, spnegoServerPrincipal); conf.set(DFSConfigKeys.DFS_WEB_AUTHENTICATION_KERBEROS_KEYTAB_KEY, - spnegoServerKeytab.getAbsolutePath()); + serverKeytab.getAbsolutePath()); conf.setBoolean("ignore.secure.ports.for.testing", true); - conf.setBoolean(THRIFT_SUPPORT_PROXYUSER_KEY, true); - conf.setBoolean(USE_HTTP_CONF_KEY, true); - conf.set("hadoop.proxyuser.hbase.hosts", "*"); - conf.set("hadoop.proxyuser.hbase.groups", "*"); + conf.setBoolean(ThriftServerRunner.THRIFT_SUPPORT_PROXYUSER_KEY, true); + conf.setBoolean(ThriftServerRunner.USE_HTTP_CONF_KEY, true); + conf.set("hadoop.proxyuser.HTTP.hosts", "*"); + conf.set("hadoop.proxyuser.HTTP.groups", "*"); - conf.set(THRIFT_KERBEROS_PRINCIPAL_KEY, serverPrincipal); - conf.set(THRIFT_KEYTAB_FILE_KEY, serverKeytab.getAbsolutePath()); - conf.set(THRIFT_SPNEGO_PRINCIPAL_KEY, spnegoServerPrincipal); - conf.set(THRIFT_SPNEGO_KEYTAB_FILE_KEY, spnegoServerKeytab.getAbsolutePath()); + conf.set(ThriftServerRunner.THRIFT_KERBEROS_PRINCIPAL_KEY, spnegoServerPrincipal); + conf.set(ThriftServerRunner.THRIFT_KEYTAB_FILE_KEY, serverKeytab.getAbsolutePath()); } @BeforeClass public static void setUpBeforeClass() throws Exception { - final File target = new File(System.getProperty("user.dir"), "target"); - assertTrue(target.exists()); - - File keytabDir = new File(target, TestThriftSpnegoHttpServer.class.getSimpleName() + - "_keytabs"); - if (keytabDir.exists()) { - FileUtils.deleteDirectory(keytabDir); - } - keytabDir.mkdirs(); - kdc = buildMiniKdc(); kdc.start(); + File keytabDir = Paths.get(TEST_UTIL.getRandomDir().toString()).toAbsolutePath().toFile(); + keytabDir.mkdirs(); + clientPrincipal = "client@" + kdc.getKdcConfig().getKdcRealm(); clientKeytab = new File(keytabDir, clientPrincipal + ".keytab"); - setupUser(kdc, clientKeytab, clientPrincipal); + kdc.createAndExportPrincipals(clientKeytab, clientPrincipal); serverPrincipal = "hbase/" + HConstants.LOCALHOST + "@" + kdc.getKdcConfig().getKdcRealm(); serverKeytab = new File(keytabDir, serverPrincipal.replace('/', '_') + ".keytab"); - setupUser(kdc, serverKeytab, serverPrincipal); spnegoServerPrincipal = "HTTP/" + HConstants.LOCALHOST + "@" + kdc.getKdcConfig().getKdcRealm(); - spnegoServerKeytab = new File(keytabDir, spnegoServerPrincipal.replace('/', '_') + ".keytab"); - setupUser(kdc, spnegoServerKeytab, spnegoServerPrincipal); + // Add SPNEGO principal to server keytab + kdc.createAndExportPrincipals(serverKeytab, serverPrincipal, spnegoServerPrincipal); - TEST_UTIL.getConfiguration().setBoolean(USE_HTTP_CONF_KEY, true); - TEST_UTIL.getConfiguration().setBoolean("hbase.table.sanity.checks", false); + TEST_UTIL.getConfiguration().setBoolean(ThriftServerRunner.USE_HTTP_CONF_KEY, true); addSecurityConfigurations(TEST_UTIL.getConfiguration()); TestThriftHttpServer.setUpBeforeClass(); @@ -203,6 +176,7 @@ public class TestThriftSpnegoHttpServer extends TestThriftHttpServer { try { if (null != kdc) { kdc.stop(); + kdc = null; } } catch (Exception e) { LOG.info("Failed to stop mini KDC", e); @@ -210,7 +184,7 @@ public class TestThriftSpnegoHttpServer extends TestThriftHttpServer { } @Override - void talkToThriftServer(String url, int customHeaderSize) throws Exception { + protected void talkToThriftServer(String url, int customHeaderSize) throws Exception { // Close httpClient and THttpClient automatically on any failures try ( CloseableHttpClient httpClient = createHttpClient(); @@ -227,11 +201,9 @@ public class TestThriftSpnegoHttpServer extends TestThriftHttpServer { TProtocol prot = new TBinaryProtocol(tHttpClient); Hbase.Client client = new Hbase.Client(prot); - if (!tableCreated) { - TestThriftServer.createTestTables(client); - tableCreated = true; - } + TestThriftServer.createTestTables(client); TestThriftServer.checkTableList(client); + TestThriftServer.dropTestTables(client); } } @@ -239,15 +211,17 @@ public class TestThriftSpnegoHttpServer extends TestThriftHttpServer { final Subject clientSubject = JaasKrbUtil.loginUsingKeytab(clientPrincipal, clientKeytab); final Set<Principal> clientPrincipals = clientSubject.getPrincipals(); // Make sure the subject has a principal - assertFalse(clientPrincipals.isEmpty()); + assertFalse("Found no client principals in the clientSubject.", + clientPrincipals.isEmpty()); // Get a TGT for the subject (might have many, different encryption types). The first should // be the default encryption type. Set<KerberosTicket> privateCredentials = clientSubject.getPrivateCredentials(KerberosTicket.class); - assertFalse(privateCredentials.isEmpty()); + assertFalse("Found no private credentials in the clientSubject.", + privateCredentials.isEmpty()); KerberosTicket tgt = privateCredentials.iterator().next(); - assertNotNull(tgt); + assertNotNull("No kerberos ticket found.", tgt); // The name of the principal final String clientPrincipalName = clientPrincipals.iterator().next().getName(); diff --git a/hbase-thrift/src/test/java/org/apache/hadoop/hbase/thrift/TestThriftSpnegoHttpServer.java b/hbase-thrift/src/test/java/org/apache/hadoop/hbase/thrift/TestThriftSpnegoHttpServer.java index 1218d18..2d5c1f2 100644 --- a/hbase-thrift/src/test/java/org/apache/hadoop/hbase/thrift/TestThriftSpnegoHttpServer.java +++ b/hbase-thrift/src/test/java/org/apache/hadoop/hbase/thrift/TestThriftSpnegoHttpServer.java @@ -1,34 +1,27 @@ /* - * Copyright The Apache Software Foundation + * 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 * - * 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 + * 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. + * 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.hadoop.hbase.thrift; -import static org.apache.hadoop.hbase.thrift.ThriftServerRunner.THRIFT_KERBEROS_PRINCIPAL_KEY; -import static org.apache.hadoop.hbase.thrift.ThriftServerRunner.THRIFT_KEYTAB_FILE_KEY; -import static org.apache.hadoop.hbase.thrift.ThriftServerRunner.THRIFT_SPNEGO_KEYTAB_FILE_KEY; -import static org.apache.hadoop.hbase.thrift.ThriftServerRunner.THRIFT_SPNEGO_PRINCIPAL_KEY; -import static org.apache.hadoop.hbase.thrift.ThriftServerRunner.THRIFT_SUPPORT_PROXYUSER_KEY; -import static org.apache.hadoop.hbase.thrift.ThriftServerRunner.USE_HTTP_CONF_KEY; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; import java.io.File; +import java.nio.file.Paths; import java.security.Principal; import java.security.PrivilegedExceptionAction; import java.util.Set; @@ -36,7 +29,6 @@ import java.util.Set; import javax.security.auth.Subject; import javax.security.auth.kerberos.KerberosTicket; -import org.apache.commons.io.FileUtils; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.HBaseClassTestRule; import org.apache.hadoop.hbase.HBaseTestingUtility; @@ -58,7 +50,6 @@ import org.apache.http.impl.auth.SPNegoSchemeFactory; import org.apache.http.impl.client.BasicCredentialsProvider; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; -import org.apache.kerby.kerberos.kerb.KrbException; import org.apache.kerby.kerberos.kerb.client.JaasKrbUtil; import org.apache.kerby.kerberos.kerb.server.SimpleKdcServer; import org.apache.thrift.protocol.TBinaryProtocol; @@ -97,20 +88,10 @@ public class TestThriftSpnegoHttpServer extends TestThriftHttpServer { private static String serverPrincipal; private static String spnegoServerPrincipal; - private static void setupUser(SimpleKdcServer kdc, File keytab, String principal) - throws KrbException { - kdc.createPrincipal(principal); - kdc.exportPrincipal(principal, keytab); - } - private static SimpleKdcServer buildMiniKdc() throws Exception { SimpleKdcServer kdc = new SimpleKdcServer(); - final File target = new File(System.getProperty("user.dir"), "target"); - File kdcDir = new File(target, TestThriftSpnegoHttpServer.class.getSimpleName()); - if (kdcDir.exists()) { - FileUtils.deleteDirectory(kdcDir); - } + File kdcDir = Paths.get(TEST_UTIL.getRandomDir().toString()).toAbsolutePath().toFile(); kdcDir.mkdirs(); kdc.setWorkDir(kdcDir); @@ -151,46 +132,40 @@ public class TestThriftSpnegoHttpServer extends TestThriftHttpServer { conf.setBoolean("ignore.secure.ports.for.testing", true); - conf.setBoolean(THRIFT_SUPPORT_PROXYUSER_KEY, true); - conf.setBoolean(USE_HTTP_CONF_KEY, true); + conf.setBoolean(ThriftServerRunner.THRIFT_SUPPORT_PROXYUSER_KEY, true); + conf.setBoolean(ThriftServerRunner.USE_HTTP_CONF_KEY, true); conf.set("hadoop.proxyuser.hbase.hosts", "*"); conf.set("hadoop.proxyuser.hbase.groups", "*"); - conf.set(THRIFT_KERBEROS_PRINCIPAL_KEY, serverPrincipal); - conf.set(THRIFT_KEYTAB_FILE_KEY, serverKeytab.getAbsolutePath()); - conf.set(THRIFT_SPNEGO_PRINCIPAL_KEY, spnegoServerPrincipal); - conf.set(THRIFT_SPNEGO_KEYTAB_FILE_KEY, spnegoServerKeytab.getAbsolutePath()); + conf.set(ThriftServerRunner.THRIFT_KERBEROS_PRINCIPAL_KEY, serverPrincipal); + conf.set(ThriftServerRunner.THRIFT_KEYTAB_FILE_KEY, serverKeytab.getAbsolutePath()); + conf.set(ThriftServerRunner.THRIFT_SPNEGO_PRINCIPAL_KEY, spnegoServerPrincipal); + conf.set(ThriftServerRunner.THRIFT_SPNEGO_KEYTAB_FILE_KEY, + spnegoServerKeytab.getAbsolutePath()); } @BeforeClass public static void setUpBeforeClass() throws Exception { - final File target = new File(System.getProperty("user.dir"), "target"); - assertTrue(target.exists()); - - File keytabDir = new File(target, TestThriftSpnegoHttpServer.class.getSimpleName() + - "_keytabs"); - if (keytabDir.exists()) { - FileUtils.deleteDirectory(keytabDir); - } - keytabDir.mkdirs(); - kdc = buildMiniKdc(); kdc.start(); + File keytabDir = Paths.get(TEST_UTIL.getRandomDir().toString()).toAbsolutePath().toFile(); + keytabDir.mkdirs(); + clientPrincipal = "client@" + kdc.getKdcConfig().getKdcRealm(); clientKeytab = new File(keytabDir, clientPrincipal + ".keytab"); - setupUser(kdc, clientKeytab, clientPrincipal); + kdc.createAndExportPrincipals(clientKeytab, clientPrincipal); serverPrincipal = "hbase/" + HConstants.LOCALHOST + "@" + kdc.getKdcConfig().getKdcRealm(); serverKeytab = new File(keytabDir, serverPrincipal.replace('/', '_') + ".keytab"); - setupUser(kdc, serverKeytab, serverPrincipal); + // Setup separate SPNEGO keytab spnegoServerPrincipal = "HTTP/" + HConstants.LOCALHOST + "@" + kdc.getKdcConfig().getKdcRealm(); spnegoServerKeytab = new File(keytabDir, spnegoServerPrincipal.replace('/', '_') + ".keytab"); - setupUser(kdc, spnegoServerKeytab, spnegoServerPrincipal); + kdc.createAndExportPrincipals(spnegoServerKeytab, spnegoServerPrincipal); + kdc.createAndExportPrincipals(serverKeytab, serverPrincipal); - TEST_UTIL.getConfiguration().setBoolean(USE_HTTP_CONF_KEY, true); - TEST_UTIL.getConfiguration().setBoolean("hbase.table.sanity.checks", false); + TEST_UTIL.getConfiguration().setBoolean(ThriftServerRunner.USE_HTTP_CONF_KEY, true); addSecurityConfigurations(TEST_UTIL.getConfiguration()); TestThriftHttpServer.setUpBeforeClass(); @@ -203,6 +178,7 @@ public class TestThriftSpnegoHttpServer extends TestThriftHttpServer { try { if (null != kdc) { kdc.stop(); + kdc = null; } } catch (Exception e) { LOG.info("Failed to stop mini KDC", e); @@ -210,7 +186,7 @@ public class TestThriftSpnegoHttpServer extends TestThriftHttpServer { } @Override - void talkToThriftServer(String url, int customHeaderSize) throws Exception { + protected void talkToThriftServer(String url, int customHeaderSize) throws Exception { // Close httpClient and THttpClient automatically on any failures try ( CloseableHttpClient httpClient = createHttpClient(); @@ -227,11 +203,9 @@ public class TestThriftSpnegoHttpServer extends TestThriftHttpServer { TProtocol prot = new TBinaryProtocol(tHttpClient); Hbase.Client client = new Hbase.Client(prot); - if (!tableCreated) { - TestThriftServer.createTestTables(client); - tableCreated = true; - } + TestThriftServer.createTestTables(client); TestThriftServer.checkTableList(client); + TestThriftServer.dropTestTables(client); } } @@ -239,15 +213,17 @@ public class TestThriftSpnegoHttpServer extends TestThriftHttpServer { final Subject clientSubject = JaasKrbUtil.loginUsingKeytab(clientPrincipal, clientKeytab); final Set<Principal> clientPrincipals = clientSubject.getPrincipals(); // Make sure the subject has a principal - assertFalse(clientPrincipals.isEmpty()); + assertFalse("Found no client principals in the clientSubject.", + clientPrincipals.isEmpty()); // Get a TGT for the subject (might have many, different encryption types). The first should // be the default encryption type. Set<KerberosTicket> privateCredentials = clientSubject.getPrivateCredentials(KerberosTicket.class); - assertFalse(privateCredentials.isEmpty()); + assertFalse("Found no private credentials in the clientSubject.", + privateCredentials.isEmpty()); KerberosTicket tgt = privateCredentials.iterator().next(); - assertNotNull(tgt); + assertNotNull("No kerberos ticket found.", tgt); // The name of the principal final String clientPrincipalName = clientPrincipals.iterator().next().getName();