1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.security.token;
20
21 import static org.apache.hadoop.fs.CommonConfigurationKeys.HADOOP_SECURITY_AUTHORIZATION;
22 import static org.junit.Assert.assertEquals;
23 import static org.junit.Assert.assertFalse;
24 import static org.junit.Assert.assertNotNull;
25 import static org.junit.Assert.assertTrue;
26
27 import java.io.IOException;
28 import java.net.InetSocketAddress;
29 import java.security.PrivilegedExceptionAction;
30 import java.util.ArrayList;
31 import java.util.List;
32 import java.util.concurrent.ConcurrentMap;
33 import java.util.concurrent.ExecutorService;
34
35 import org.apache.commons.logging.Log;
36 import org.apache.commons.logging.LogFactory;
37 import org.apache.hadoop.conf.Configuration;
38 import org.apache.hadoop.hbase.ClusterId;
39 import org.apache.hadoop.hbase.Coprocessor;
40 import org.apache.hadoop.hbase.HBaseTestingUtility;
41 import org.apache.hadoop.hbase.HConstants;
42 import org.apache.hadoop.hbase.HRegionInfo;
43 import org.apache.hadoop.hbase.testclassification.MediumTests;
44 import org.apache.hadoop.hbase.Server;
45 import org.apache.hadoop.hbase.ServerName;
46 import org.apache.hadoop.hbase.TableName;
47 import org.apache.hadoop.hbase.catalog.CatalogTracker;
48 import org.apache.hadoop.hbase.client.HConnection;
49 import org.apache.hadoop.hbase.client.HConnectionManager;
50 import org.apache.hadoop.hbase.client.HTableInterface;
51 import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment;
52 import org.apache.hadoop.hbase.ipc.BlockingRpcCallback;
53 import org.apache.hadoop.hbase.ipc.FifoRpcScheduler;
54 import org.apache.hadoop.hbase.ipc.RequestContext;
55 import org.apache.hadoop.hbase.ipc.RpcClient;
56 import org.apache.hadoop.hbase.ipc.RpcServer;
57 import org.apache.hadoop.hbase.ipc.RpcServer.BlockingServiceAndInterface;
58 import org.apache.hadoop.hbase.ipc.RpcServerInterface;
59 import org.apache.hadoop.hbase.ipc.ServerRpcController;
60 import org.apache.hadoop.hbase.protobuf.generated.AuthenticationProtos;
61 import org.apache.hadoop.hbase.regionserver.HRegion;
62 import org.apache.hadoop.hbase.regionserver.RegionServerServices;
63 import org.apache.hadoop.hbase.security.SecurityInfo;
64 import org.apache.hadoop.hbase.security.User;
65 import org.apache.hadoop.hbase.util.Bytes;
66 import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
67 import org.apache.hadoop.hbase.util.Sleeper;
68 import org.apache.hadoop.hbase.util.Strings;
69 import org.apache.hadoop.hbase.util.Threads;
70 import org.apache.hadoop.hbase.util.Writables;
71 import org.apache.hadoop.hbase.zookeeper.ZKClusterId;
72 import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
73 import org.apache.hadoop.net.DNS;
74 import org.apache.hadoop.security.UserGroupInformation;
75 import org.apache.hadoop.security.authorize.PolicyProvider;
76 import org.apache.hadoop.security.authorize.Service;
77 import org.apache.hadoop.security.token.SecretManager;
78 import org.apache.hadoop.security.token.Token;
79 import org.apache.hadoop.security.token.TokenIdentifier;
80 import org.junit.AfterClass;
81 import org.junit.BeforeClass;
82 import org.junit.Test;
83 import org.junit.experimental.categories.Category;
84
85 import com.google.protobuf.BlockingRpcChannel;
86 import com.google.protobuf.BlockingService;
87 import com.google.protobuf.RpcController;
88 import com.google.protobuf.ServiceException;
89
90
91
92
93 @Category(MediumTests.class)
94 public class TestTokenAuthentication {
95 static {
96
97
98 System.setProperty("java.security.krb5.realm", "hbase");
99 System.setProperty("java.security.krb5.kdc", "blah");
100 }
101 private static Log LOG = LogFactory.getLog(TestTokenAuthentication.class);
102
103 public interface AuthenticationServiceSecurityInfo {}
104
105
106
107
108 private static class TokenServer extends TokenProvider
109 implements AuthenticationProtos.AuthenticationService.BlockingInterface, Runnable, Server {
110 private static Log LOG = LogFactory.getLog(TokenServer.class);
111 private Configuration conf;
112 private RpcServerInterface rpcServer;
113 private InetSocketAddress isa;
114 private ZooKeeperWatcher zookeeper;
115 private Sleeper sleeper;
116 private boolean started = false;
117 private boolean aborted = false;
118 private boolean stopped = false;
119 private long startcode;
120
121 public TokenServer(Configuration conf) throws IOException {
122 this.conf = conf;
123 this.startcode = EnvironmentEdgeManager.currentTimeMillis();
124
125 String hostname =
126 Strings.domainNamePointerToHostName(DNS.getDefaultHost("default", "default"));
127 int port = 0;
128
129 InetSocketAddress initialIsa = new InetSocketAddress(hostname, port);
130 if (initialIsa.getAddress() == null) {
131 throw new IllegalArgumentException("Failed resolve of " + initialIsa);
132 }
133 final List<BlockingServiceAndInterface> sai =
134 new ArrayList<BlockingServiceAndInterface>(1);
135 BlockingService service =
136 AuthenticationProtos.AuthenticationService.newReflectiveBlockingService(this);
137 sai.add(new BlockingServiceAndInterface(service,
138 AuthenticationProtos.AuthenticationService.BlockingInterface.class));
139 this.rpcServer =
140 new RpcServer(this, "tokenServer", sai, initialIsa, conf, new FifoRpcScheduler(conf, 1));
141 this.isa = this.rpcServer.getListenerAddress();
142 this.sleeper = new Sleeper(1000, this);
143 }
144
145 @Override
146 public Configuration getConfiguration() {
147 return conf;
148 }
149
150 @Override
151 public CatalogTracker getCatalogTracker() {
152 return null;
153 }
154
155 @Override
156 public ZooKeeperWatcher getZooKeeper() {
157 return zookeeper;
158 }
159
160 @Override
161 public boolean isAborted() {
162 return aborted;
163 }
164
165 @Override
166 public ServerName getServerName() {
167 return ServerName.valueOf(isa.getHostName(), isa.getPort(), startcode);
168 }
169
170 @Override
171 public void abort(String reason, Throwable error) {
172 LOG.fatal("Aborting on: "+reason, error);
173 this.aborted = true;
174 this.stopped = true;
175 sleeper.skipSleepCycle();
176 }
177
178 private void initialize() throws IOException {
179
180 Configuration zkConf = new Configuration(conf);
181 zkConf.set(User.HBASE_SECURITY_CONF_KEY, "simple");
182 this.zookeeper = new ZooKeeperWatcher(zkConf, TokenServer.class.getSimpleName(),
183 this, true);
184 this.rpcServer.start();
185
186
187 final RegionServerServices mockServices = TEST_UTIL.createMockRegionServerService(rpcServer);
188
189
190 super.start(new RegionCoprocessorEnvironment() {
191 @Override
192 public HRegion getRegion() { return null; }
193
194 @Override
195 public RegionServerServices getRegionServerServices() {
196 return mockServices;
197 }
198
199 @Override
200 public ConcurrentMap<String, Object> getSharedData() { return null; }
201
202 @Override
203 public int getVersion() { return 0; }
204
205 @Override
206 public String getHBaseVersion() { return null; }
207
208 @Override
209 public Coprocessor getInstance() { return null; }
210
211 @Override
212 public int getPriority() { return 0; }
213
214 @Override
215 public int getLoadSequence() { return 0; }
216
217 @Override
218 public Configuration getConfiguration() { return conf; }
219
220 @Override
221 public HTableInterface getTable(TableName tableName) throws IOException
222 { return null; }
223
224 @Override
225 public HTableInterface getTable(TableName tableName, ExecutorService service)
226 throws IOException {
227 return null;
228 }
229
230 @Override
231 public ClassLoader getClassLoader() {
232 return Thread.currentThread().getContextClassLoader();
233 }
234
235 @Override
236 public HRegionInfo getRegionInfo() {
237 return null;
238 }
239 });
240
241 started = true;
242 }
243
244 public void run() {
245 try {
246 initialize();
247 while (!stopped) {
248 this.sleeper.sleep();
249 }
250 } catch (Exception e) {
251 abort(e.getMessage(), e);
252 }
253 this.rpcServer.stop();
254 }
255
256 public boolean isStarted() {
257 return started;
258 }
259
260 @Override
261 public void stop(String reason) {
262 LOG.info("Stopping due to: "+reason);
263 this.stopped = true;
264 sleeper.skipSleepCycle();
265 }
266
267 @Override
268 public boolean isStopped() {
269 return stopped;
270 }
271
272 public InetSocketAddress getAddress() {
273 return isa;
274 }
275
276 public SecretManager<? extends TokenIdentifier> getSecretManager() {
277 return ((RpcServer)rpcServer).getSecretManager();
278 }
279
280 @Override
281 public AuthenticationProtos.GetAuthenticationTokenResponse getAuthenticationToken(
282 RpcController controller, AuthenticationProtos.GetAuthenticationTokenRequest request)
283 throws ServiceException {
284 LOG.debug("Authentication token request from "+RequestContext.getRequestUserName());
285
286 ServerRpcController serverController = new ServerRpcController();
287 BlockingRpcCallback<AuthenticationProtos.GetAuthenticationTokenResponse> callback =
288 new BlockingRpcCallback<AuthenticationProtos.GetAuthenticationTokenResponse>();
289 getAuthenticationToken(serverController, request, callback);
290 try {
291 serverController.checkFailed();
292 return callback.get();
293 } catch (IOException ioe) {
294 throw new ServiceException(ioe);
295 }
296 }
297
298 @Override
299 public AuthenticationProtos.WhoAmIResponse whoAmI(
300 RpcController controller, AuthenticationProtos.WhoAmIRequest request)
301 throws ServiceException {
302 LOG.debug("whoAmI() request from "+RequestContext.getRequestUserName());
303
304 ServerRpcController serverController = new ServerRpcController();
305 BlockingRpcCallback<AuthenticationProtos.WhoAmIResponse> callback =
306 new BlockingRpcCallback<AuthenticationProtos.WhoAmIResponse>();
307 whoAmI(serverController, request, callback);
308 try {
309 serverController.checkFailed();
310 return callback.get();
311 } catch (IOException ioe) {
312 throw new ServiceException(ioe);
313 }
314 }
315 }
316
317
318 private static HBaseTestingUtility TEST_UTIL;
319 private static TokenServer server;
320 private static Thread serverThread;
321 private static AuthenticationTokenSecretManager secretManager;
322 private static ClusterId clusterId = new ClusterId();
323
324 @BeforeClass
325 public static void setupBeforeClass() throws Exception {
326 TEST_UTIL = new HBaseTestingUtility();
327 TEST_UTIL.startMiniZKCluster();
328
329 SecurityInfo.addInfo(AuthenticationProtos.AuthenticationService.getDescriptor().getName(),
330 new SecurityInfo("hbase.test.kerberos.principal",
331 AuthenticationProtos.TokenIdentifier.Kind.HBASE_AUTH_TOKEN));
332
333 Configuration conf = TEST_UTIL.getConfiguration();
334 conf.set("hadoop.security.authentication", "kerberos");
335 conf.set("hbase.security.authentication", "kerberos");
336 conf.setBoolean(HADOOP_SECURITY_AUTHORIZATION, true);
337 server = new TokenServer(conf);
338 serverThread = new Thread(server);
339 Threads.setDaemonThreadRunning(serverThread, "TokenServer:"+server.getServerName().toString());
340
341 while (!server.isStarted() && !server.isStopped()) {
342 Thread.sleep(10);
343 }
344 server.rpcServer.refreshAuthManager(new PolicyProvider() {
345 @Override
346 public Service[] getServices() {
347 return new Service [] {
348 new Service("security.client.protocol.acl",
349 AuthenticationProtos.AuthenticationService.BlockingInterface.class)};
350 }
351 });
352 ZKClusterId.setClusterId(server.getZooKeeper(), clusterId);
353 secretManager = (AuthenticationTokenSecretManager)server.getSecretManager();
354 while(secretManager.getCurrentKey() == null) {
355 Thread.sleep(1);
356 }
357 }
358
359 @AfterClass
360 public static void tearDownAfterClass() throws Exception {
361 server.stop("Test complete");
362 Threads.shutdown(serverThread);
363 TEST_UTIL.shutdownMiniZKCluster();
364 }
365
366 @Test
367 public void testTokenCreation() throws Exception {
368 Token<AuthenticationTokenIdentifier> token =
369 secretManager.generateToken("testuser");
370
371 AuthenticationTokenIdentifier ident = new AuthenticationTokenIdentifier();
372 Writables.getWritable(token.getIdentifier(), ident);
373 assertEquals("Token username should match", "testuser",
374 ident.getUsername());
375 byte[] passwd = secretManager.retrievePassword(ident);
376 assertTrue("Token password and password from secret manager should match",
377 Bytes.equals(token.getPassword(), passwd));
378 }
379
380 @Test
381 public void testTokenAuthentication() throws Exception {
382 UserGroupInformation testuser =
383 UserGroupInformation.createUserForTesting("testuser", new String[]{"testgroup"});
384
385 testuser.setAuthenticationMethod(
386 UserGroupInformation.AuthenticationMethod.TOKEN);
387 final Configuration conf = TEST_UTIL.getConfiguration();
388 UserGroupInformation.setConfiguration(conf);
389 Token<AuthenticationTokenIdentifier> token =
390 secretManager.generateToken("testuser");
391 LOG.debug("Got token: " + token.toString());
392 testuser.addToken(token);
393
394
395 testuser.doAs(new PrivilegedExceptionAction<Object>() {
396 public Object run() throws Exception {
397 Configuration c = server.getConfiguration();
398 RpcClient rpcClient = new RpcClient(c, clusterId.toString());
399 ServerName sn =
400 ServerName.valueOf(server.getAddress().getHostName(), server.getAddress().getPort(),
401 System.currentTimeMillis());
402 try {
403 BlockingRpcChannel channel = rpcClient.createBlockingRpcChannel(sn,
404 User.getCurrent(), HConstants.DEFAULT_HBASE_RPC_TIMEOUT);
405 AuthenticationProtos.AuthenticationService.BlockingInterface stub =
406 AuthenticationProtos.AuthenticationService.newBlockingStub(channel);
407 AuthenticationProtos.WhoAmIResponse response =
408 stub.whoAmI(null, AuthenticationProtos.WhoAmIRequest.getDefaultInstance());
409 String myname = response.getUsername();
410 assertEquals("testuser", myname);
411 String authMethod = response.getAuthMethod();
412 assertEquals("TOKEN", authMethod);
413 } finally {
414 rpcClient.stop();
415 }
416 return null;
417 }
418 });
419 }
420
421 @Test
422 public void testUseExistingToken() throws Exception {
423 User user = User.createUserForTesting(TEST_UTIL.getConfiguration(), "testuser2",
424 new String[]{"testgroup"});
425 Token<AuthenticationTokenIdentifier> token =
426 secretManager.generateToken(user.getName());
427 assertNotNull(token);
428 user.addToken(token);
429
430
431 Token<AuthenticationTokenIdentifier> firstToken =
432 new AuthenticationTokenSelector().selectToken(token.getService(), user.getTokens());
433 assertNotNull(firstToken);
434 assertEquals(token, firstToken);
435
436 HConnection conn = HConnectionManager.createConnection(TEST_UTIL.getConfiguration());
437 try {
438 assertFalse(TokenUtil.addTokenIfMissing(conn, user));
439
440 Token<AuthenticationTokenIdentifier> secondToken =
441 new AuthenticationTokenSelector().selectToken(token.getService(), user.getTokens());
442 assertEquals(firstToken, secondToken);
443 } finally {
444 conn.close();
445 }
446 }
447 }