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