1 /**
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
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
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.
17  */
18 package org.apache.hadoop.hbase.security.token;
19 
20 import static org.junit.Assert.assertEquals;
21 import static org.junit.Assert.assertTrue;
22 
23 import java.io.File;
24 import java.io.IOException;
25 import java.util.Properties;
26 
27 import org.apache.hadoop.conf.Configuration;
28 import org.apache.hadoop.fs.Path;
29 import org.apache.hadoop.hbase.HBaseTestingUtility;
30 import org.apache.hadoop.hbase.HConstants;
31 import org.apache.hadoop.hbase.LocalHBaseCluster;
32 import org.apache.hadoop.hbase.TableName;
33 import org.apache.hadoop.hbase.client.Connection;
34 import org.apache.hadoop.hbase.client.ConnectionFactory;
35 import org.apache.hadoop.hbase.client.Table;
36 import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
37 import org.apache.hadoop.hbase.http.ssl.KeyStoreTestUtil;
38 import org.apache.hadoop.hbase.ipc.AsyncRpcClient;
39 import org.apache.hadoop.hbase.ipc.CoprocessorRpcChannel;
40 import org.apache.hadoop.hbase.ipc.RpcClient;
41 import org.apache.hadoop.hbase.ipc.RpcClientFactory;
42 import org.apache.hadoop.hbase.ipc.RpcClientImpl;
43 import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
44 import org.apache.hadoop.hbase.protobuf.generated.AuthenticationProtos;
45 import org.apache.hadoop.hbase.protobuf.generated.AuthenticationProtos.GetAuthenticationTokenRequest;
46 import org.apache.hadoop.hbase.protobuf.generated.AuthenticationProtos.WhoAmIRequest;
47 import org.apache.hadoop.hbase.protobuf.generated.AuthenticationProtos.WhoAmIResponse;
48 import org.apache.hadoop.hbase.security.AccessDeniedException;
49 import org.apache.hadoop.hbase.security.HBaseKerberosUtils;
50 import org.apache.hadoop.hbase.testclassification.MediumTests;
51 import org.apache.hadoop.hbase.util.FSUtils;
52 import org.apache.hadoop.hdfs.DFSConfigKeys;
53 import org.apache.hadoop.http.HttpConfig;
54 import org.apache.hadoop.minikdc.MiniKdc;
55 import org.apache.hadoop.security.UserGroupInformation;
56 import org.apache.hadoop.security.UserGroupInformation.AuthenticationMethod;
57 import org.apache.hadoop.security.token.Token;
58 import org.apache.hadoop.security.token.TokenIdentifier;
59 import org.junit.AfterClass;
60 import org.junit.BeforeClass;
61 import org.junit.Test;
62 import org.junit.experimental.categories.Category;
63 
64 import com.google.protobuf.ServiceException;
65 
66 @Category(MediumTests.class)
67 public class TestGenerateDelegationToken {
68 
69   private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
70 
71   private static LocalHBaseCluster CLUSTER;
72 
73   private static final File KEYTAB_FILE = new File(TEST_UTIL.getDataTestDir("keytab").toUri()
74       .getPath());
75   private static MiniKdc KDC;
76 
77   private static String HOST = "localhost";
78 
79   private static String USERNAME;
80 
81   private static String PRINCIPAL;
82 
83   private static String HTTP_PRINCIPAL;
84 
setHdfsSecuredConfiguration(Configuration conf)85   private static void setHdfsSecuredConfiguration(Configuration conf) throws Exception {
86     // change XXX_USER_NAME_KEY to XXX_KERBEROS_PRINCIPAL_KEY after we drop support for hadoop-2.4.1
87     conf.set(DFSConfigKeys.DFS_NAMENODE_USER_NAME_KEY, PRINCIPAL + "@" + KDC.getRealm());
88     conf.set(DFSConfigKeys.DFS_NAMENODE_KEYTAB_FILE_KEY, KEYTAB_FILE.getAbsolutePath());
89     conf.set(DFSConfigKeys.DFS_DATANODE_USER_NAME_KEY, PRINCIPAL + "@" + KDC.getRealm());
90     conf.set(DFSConfigKeys.DFS_DATANODE_KEYTAB_FILE_KEY, KEYTAB_FILE.getAbsolutePath());
91     conf.set(DFSConfigKeys.DFS_WEB_AUTHENTICATION_KERBEROS_PRINCIPAL_KEY, HTTP_PRINCIPAL + "@"
92         + KDC.getRealm());
93     conf.setBoolean(DFSConfigKeys.DFS_BLOCK_ACCESS_TOKEN_ENABLE_KEY, true);
94     conf.set("dfs.http.policy", HttpConfig.Policy.HTTPS_ONLY.name());
95     conf.set(DFSConfigKeys.DFS_NAMENODE_HTTPS_ADDRESS_KEY, "localhost:0");
96     conf.set(DFSConfigKeys.DFS_DATANODE_HTTPS_ADDRESS_KEY, "localhost:0");
97 
98     File keystoresDir = new File(TEST_UTIL.getDataTestDir("keystore").toUri().getPath());
99     keystoresDir.mkdirs();
100     String sslConfDir = KeyStoreTestUtil.getClasspathDir(TestGenerateDelegationToken.class);
101     KeyStoreTestUtil.setupSSLConfig(keystoresDir.getAbsolutePath(), sslConfDir, conf, false);
102 
103     conf.setBoolean("ignore.secure.ports.for.testing", true);
104   }
105 
106   @BeforeClass
setUp()107   public static void setUp() throws Exception {
108     Properties conf = MiniKdc.createConf();
109     conf.put(MiniKdc.DEBUG, true);
110     KDC = new MiniKdc(conf, new File(TEST_UTIL.getDataTestDir("kdc").toUri().getPath()));
111     KDC.start();
112     USERNAME = UserGroupInformation.getLoginUser().getShortUserName();
113     PRINCIPAL = USERNAME + "/" + HOST;
114     HTTP_PRINCIPAL = "HTTP/" + HOST;
115     KDC.createPrincipal(KEYTAB_FILE, PRINCIPAL, HTTP_PRINCIPAL);
116     TEST_UTIL.startMiniZKCluster();
117 
118     HBaseKerberosUtils.setKeytabFileForTesting(KEYTAB_FILE.getAbsolutePath());
119     HBaseKerberosUtils.setPrincipalForTesting(PRINCIPAL + "@" + KDC.getRealm());
120     HBaseKerberosUtils.setSecuredConfiguration(TEST_UTIL.getConfiguration());
121     setHdfsSecuredConfiguration(TEST_UTIL.getConfiguration());
122     UserGroupInformation.setConfiguration(TEST_UTIL.getConfiguration());
123     TEST_UTIL.getConfiguration().setStrings(CoprocessorHost.REGION_COPROCESSOR_CONF_KEY,
124       TokenProvider.class.getName());
125     TEST_UTIL.startMiniDFSCluster(1);
126     Path rootdir = TEST_UTIL.getDataTestDirOnTestFS("TestGenerateDelegationToken");
127     FSUtils.setRootDir(TEST_UTIL.getConfiguration(), rootdir);
128     CLUSTER = new LocalHBaseCluster(TEST_UTIL.getConfiguration(), 1);
129     CLUSTER.startup();
130   }
131 
132   @AfterClass
tearDown()133   public static void tearDown() throws Exception {
134     if (CLUSTER != null) {
135       CLUSTER.shutdown();
136     }
137     CLUSTER.join();
138     if (KDC != null) {
139       KDC.stop();
140     }
141     TEST_UTIL.shutdownMiniCluster();
142   }
143 
testTokenAuth(Class<? extends RpcClient> rpcImplClass)144   private void testTokenAuth(Class<? extends RpcClient> rpcImplClass) throws IOException,
145       ServiceException {
146     TEST_UTIL.getConfiguration().set(RpcClientFactory.CUSTOM_RPC_CLIENT_IMPL_CONF_KEY,
147       rpcImplClass.getName());
148     try (Connection conn = ConnectionFactory.createConnection(TEST_UTIL.getConfiguration());
149         Table table = conn.getTable(TableName.META_TABLE_NAME)) {
150       CoprocessorRpcChannel rpcChannel = table.coprocessorService(HConstants.EMPTY_START_ROW);
151       AuthenticationProtos.AuthenticationService.BlockingInterface service =
152           AuthenticationProtos.AuthenticationService.newBlockingStub(rpcChannel);
153       WhoAmIResponse response = service.whoAmI(null, WhoAmIRequest.getDefaultInstance());
154       assertEquals(USERNAME, response.getUsername());
155       assertEquals(AuthenticationMethod.TOKEN.name(), response.getAuthMethod());
156       try {
157         service.getAuthenticationToken(null, GetAuthenticationTokenRequest.getDefaultInstance());
158       } catch (ServiceException e) {
159         AccessDeniedException exc = (AccessDeniedException) ProtobufUtil.getRemoteException(e);
160         assertTrue(exc.getMessage().contains(
161           "Token generation only allowed for Kerberos authenticated clients"));
162       }
163     }
164   }
165 
166   @Test
test()167   public void test() throws Exception {
168     try (Connection conn = ConnectionFactory.createConnection(TEST_UTIL.getConfiguration())) {
169       Token<? extends TokenIdentifier> token = TokenUtil.obtainToken(conn);
170       UserGroupInformation.getCurrentUser().addToken(token);
171       testTokenAuth(RpcClientImpl.class);
172       testTokenAuth(AsyncRpcClient.class);
173     }
174 
175   }
176 }
177