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