2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
18 package org
.apache
.hadoop
.hbase
.thrift
;
20 import static org
.apache
.hadoop
.hbase
.thrift
.Constants
.THRIFT_SUPPORT_PROXYUSER_KEY
;
21 import static org
.junit
.Assert
.assertFalse
;
22 import static org
.junit
.Assert
.assertNotNull
;
24 import java
.net
.InetAddress
;
25 import java
.nio
.ByteBuffer
;
26 import java
.nio
.file
.Paths
;
27 import java
.security
.Principal
;
28 import java
.security
.PrivilegedExceptionAction
;
29 import java
.util
.List
;
31 import java
.util
.function
.Supplier
;
32 import java
.util
.stream
.Collectors
;
33 import javax
.security
.auth
.Subject
;
34 import javax
.security
.auth
.kerberos
.KerberosTicket
;
35 import org
.apache
.hadoop
.conf
.Configuration
;
36 import org
.apache
.hadoop
.hbase
.HBaseClassTestRule
;
37 import org
.apache
.hadoop
.hbase
.HBaseTestingUtil
;
38 import org
.apache
.hadoop
.hbase
.security
.HBaseKerberosUtils
;
39 import org
.apache
.hadoop
.hbase
.testclassification
.ClientTests
;
40 import org
.apache
.hadoop
.hbase
.testclassification
.LargeTests
;
41 import org
.apache
.hadoop
.hbase
.thrift
.generated
.Hbase
;
42 import org
.apache
.hadoop
.hbase
.util
.Bytes
;
43 import org
.apache
.hadoop
.hbase
.util
.SimpleKdcServerUtil
;
44 import org
.apache
.hadoop
.security
.authentication
.util
.KerberosName
;
45 import org
.apache
.http
.HttpHeaders
;
46 import org
.apache
.http
.auth
.AuthSchemeProvider
;
47 import org
.apache
.http
.auth
.AuthScope
;
48 import org
.apache
.http
.auth
.KerberosCredentials
;
49 import org
.apache
.http
.client
.config
.AuthSchemes
;
50 import org
.apache
.http
.config
.Lookup
;
51 import org
.apache
.http
.config
.RegistryBuilder
;
52 import org
.apache
.http
.impl
.auth
.SPNegoSchemeFactory
;
53 import org
.apache
.http
.impl
.client
.BasicCredentialsProvider
;
54 import org
.apache
.http
.impl
.client
.CloseableHttpClient
;
55 import org
.apache
.http
.impl
.client
.HttpClients
;
56 import org
.apache
.kerby
.kerberos
.kerb
.client
.JaasKrbUtil
;
57 import org
.apache
.kerby
.kerberos
.kerb
.server
.SimpleKdcServer
;
58 import org
.apache
.thrift
.protocol
.TBinaryProtocol
;
59 import org
.apache
.thrift
.protocol
.TProtocol
;
60 import org
.apache
.thrift
.transport
.THttpClient
;
61 import org
.ietf
.jgss
.GSSCredential
;
62 import org
.ietf
.jgss
.GSSManager
;
63 import org
.ietf
.jgss
.GSSName
;
64 import org
.ietf
.jgss
.Oid
;
65 import org
.junit
.AfterClass
;
66 import org
.junit
.Assert
;
67 import org
.junit
.BeforeClass
;
68 import org
.junit
.ClassRule
;
69 import org
.junit
.Test
;
70 import org
.junit
.experimental
.categories
.Category
;
71 import org
.slf4j
.Logger
;
72 import org
.slf4j
.LoggerFactory
;
75 * Start the HBase Thrift HTTP server on a random port through the command-line
76 * interface and talk to it from client side with SPNEGO security enabled.
78 @Category({ClientTests
.class, LargeTests
.class})
79 public class TestThriftSpnegoHttpServer
extends TestThriftHttpServer
{
81 public static final HBaseClassTestRule CLASS_RULE
=
82 HBaseClassTestRule
.forClass(TestThriftSpnegoHttpServer
.class);
84 private static final Logger LOG
=
85 LoggerFactory
.getLogger(TestThriftSpnegoHttpServer
.class);
87 private static SimpleKdcServer kdc
;
88 private static File serverKeytab
;
89 private static File spnegoServerKeytab
;
90 private static File clientKeytab
;
92 private static String clientPrincipal
;
93 private static String serverPrincipal
;
94 private static String spnegoServerPrincipal
;
96 private static void addSecurityConfigurations(Configuration conf
) {
97 KerberosName
.setRules("DEFAULT");
99 HBaseKerberosUtils
.setKeytabFileForTesting(serverKeytab
.getAbsolutePath());
101 conf
.setBoolean(THRIFT_SUPPORT_PROXYUSER_KEY
, true);
102 conf
.setBoolean(Constants
.USE_HTTP_CONF_KEY
, true);
104 conf
.set(Constants
.THRIFT_KERBEROS_PRINCIPAL_KEY
, serverPrincipal
);
105 conf
.set(Constants
.THRIFT_KEYTAB_FILE_KEY
, serverKeytab
.getAbsolutePath());
107 HBaseKerberosUtils
.setSecuredConfiguration(conf
, serverPrincipal
, spnegoServerPrincipal
);
108 conf
.set("hadoop.proxyuser.hbase.hosts", "*");
109 conf
.set("hadoop.proxyuser.hbase.groups", "*");
110 conf
.set(Constants
.THRIFT_SPNEGO_PRINCIPAL_KEY
, spnegoServerPrincipal
);
111 conf
.set(Constants
.THRIFT_SPNEGO_KEYTAB_FILE_KEY
, spnegoServerKeytab
.getAbsolutePath());
115 public static void setUpBeforeClass() throws Exception
{
116 kdc
= SimpleKdcServerUtil
.
117 getRunningSimpleKdcServer(new File(TEST_UTIL
.getDataTestDir().toString()),
118 HBaseTestingUtil
::randomFreePort
);
119 File keytabDir
= Paths
.get(TEST_UTIL
.getRandomDir().toString()).toAbsolutePath().toFile();
120 Assert
.assertTrue(keytabDir
.mkdirs());
122 clientPrincipal
= "client@" + kdc
.getKdcConfig().getKdcRealm();
123 clientKeytab
= new File(keytabDir
, clientPrincipal
+ ".keytab");
124 kdc
.createAndExportPrincipals(clientKeytab
, clientPrincipal
);
126 String hostname
= InetAddress
.getLoopbackAddress().getHostName();
127 serverPrincipal
= "hbase/" + hostname
+ "@" + kdc
.getKdcConfig().getKdcRealm();
128 serverKeytab
= new File(keytabDir
, serverPrincipal
.replace('/', '_') + ".keytab");
130 // Setup separate SPNEGO keytab
131 spnegoServerPrincipal
= "HTTP/" + hostname
+ "@" + kdc
.getKdcConfig().getKdcRealm();
132 spnegoServerKeytab
= new File(keytabDir
, spnegoServerPrincipal
.replace('/', '_') + ".keytab");
133 kdc
.createAndExportPrincipals(spnegoServerKeytab
, spnegoServerPrincipal
);
134 kdc
.createAndExportPrincipals(serverKeytab
, serverPrincipal
);
136 TEST_UTIL
.getConfiguration().setBoolean(Constants
.USE_HTTP_CONF_KEY
, true);
137 addSecurityConfigurations(TEST_UTIL
.getConfiguration());
139 TestThriftHttpServer
.setUpBeforeClass();
142 @Override protected Supplier
<ThriftServer
> getThriftServerSupplier() {
143 return () -> new ThriftServer(TEST_UTIL
.getConfiguration());
147 public static void tearDownAfterClass() throws Exception
{
148 TestThriftHttpServer
.tearDownAfterClass();
155 } catch (Exception e
) {
156 LOG
.info("Failed to stop mini KDC", e
);
161 * Block call through to this method. It is a messy test that fails because of bad config
162 * and then succeeds only the first attempt adds a table which the second attempt doesn't
163 * want to be in place to succeed. Let the super impl of this test be responsible for
164 * verifying we fail if bad header size.
168 @Override public void testRunThriftServerWithHeaderBufferLength() throws Exception
{
169 super.testRunThriftServerWithHeaderBufferLength();
173 protected void talkToThriftServer(String url
, int customHeaderSize
) throws Exception
{
174 // Close httpClient and THttpClient automatically on any failures
176 CloseableHttpClient httpClient
= createHttpClient();
177 THttpClient tHttpClient
= new THttpClient(url
, httpClient
)
180 if (customHeaderSize
> 0) {
181 StringBuilder sb
= new StringBuilder();
182 for (int i
= 0; i
< customHeaderSize
; i
++) {
185 tHttpClient
.setCustomHeader(HttpHeaders
.USER_AGENT
, sb
.toString());
188 TProtocol prot
= new TBinaryProtocol(tHttpClient
);
189 Hbase
.Client client
= new Hbase
.Client(prot
);
190 List
<ByteBuffer
> bbs
= client
.getTableNames();
191 LOG
.info("PRE-EXISTING {}", bbs
.stream().
192 map(b
-> Bytes
.toString(b
.array())).collect(Collectors
.joining(",")));
193 if (!bbs
.isEmpty()) {
194 for (ByteBuffer bb
: bbs
) {
195 client
.disableTable(bb
);
196 client
.deleteTable(bb
);
199 TestThriftServer
.createTestTables(client
);
200 TestThriftServer
.checkTableList(client
);
201 TestThriftServer
.dropTestTables(client
);
205 private CloseableHttpClient
createHttpClient() throws Exception
{
206 final Subject clientSubject
= JaasKrbUtil
.loginUsingKeytab(clientPrincipal
, clientKeytab
);
207 final Set
<Principal
> clientPrincipals
= clientSubject
.getPrincipals();
208 // Make sure the subject has a principal
209 assertFalse("Found no client principals in the clientSubject.",
210 clientPrincipals
.isEmpty());
212 // Get a TGT for the subject (might have many, different encryption types). The first should
213 // be the default encryption type.
214 Set
<KerberosTicket
> privateCredentials
=
215 clientSubject
.getPrivateCredentials(KerberosTicket
.class);
216 assertFalse("Found no private credentials in the clientSubject.",
217 privateCredentials
.isEmpty());
218 KerberosTicket tgt
= privateCredentials
.iterator().next();
219 assertNotNull("No kerberos ticket found.", tgt
);
221 // The name of the principal
222 final String clientPrincipalName
= clientPrincipals
.iterator().next().getName();
224 return Subject
.doAs(clientSubject
, (PrivilegedExceptionAction
<CloseableHttpClient
>) () -> {
225 // Logs in with Kerberos via GSS
226 GSSManager gssManager
= GSSManager
.getInstance();
227 // jGSS Kerberos login constant
228 Oid oid
= new Oid("1.2.840.113554.1.2.2");
229 GSSName gssClient
= gssManager
.createName(clientPrincipalName
, GSSName
.NT_USER_NAME
);
230 GSSCredential credential
= gssManager
.createCredential(gssClient
,
231 GSSCredential
.DEFAULT_LIFETIME
, oid
, GSSCredential
.INITIATE_ONLY
);
233 Lookup
<AuthSchemeProvider
> authRegistry
= RegistryBuilder
.<AuthSchemeProvider
>create()
234 .register(AuthSchemes
.SPNEGO
, new SPNegoSchemeFactory(true, true))
237 BasicCredentialsProvider credentialsProvider
= new BasicCredentialsProvider();
238 credentialsProvider
.setCredentials(AuthScope
.ANY
, new KerberosCredentials(credential
));
240 return HttpClients
.custom()
241 .setDefaultAuthSchemeRegistry(authRegistry
)
242 .setDefaultCredentialsProvider(credentialsProvider
)