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