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