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