1 /**
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements.  See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership.  The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License.  You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 
19 package org.apache.hadoop.hdfs.security;
20 
21 
22 
23 import java.io.ByteArrayInputStream;
24 import java.io.DataInputStream;
25 import java.io.IOException;
26 import java.net.URI;
27 import java.security.PrivilegedExceptionAction;
28 
29 import org.apache.commons.logging.Log;
30 import org.apache.commons.logging.LogFactory;
31 import org.apache.commons.logging.impl.Log4JLogger;
32 import org.apache.hadoop.conf.Configuration;
33 import org.apache.hadoop.fs.FileSystem;
34 import org.apache.hadoop.hdfs.DFSConfigKeys;
35 import org.apache.hadoop.hdfs.DistributedFileSystem;
36 import org.apache.hadoop.hdfs.MiniDFSCluster;
37 import org.apache.hadoop.hdfs.security.token.delegation.DelegationTokenIdentifier;
38 import org.apache.hadoop.hdfs.security.token.delegation.DelegationTokenSecretManager;
39 import org.apache.hadoop.hdfs.server.namenode.web.resources.NamenodeWebHdfsMethods;
40 import org.apache.hadoop.hdfs.web.WebHdfsFileSystem;
41 import org.apache.hadoop.io.Text;
42 import org.apache.hadoop.security.AccessControlException;
43 import org.apache.hadoop.security.UserGroupInformation;
44 import org.apache.hadoop.security.token.SecretManager.InvalidToken;
45 import org.apache.hadoop.security.token.Token;
46 import org.apache.log4j.Level;
47 import org.junit.After;
48 import org.junit.Assert;
49 import org.junit.Before;
50 import org.junit.Test;
51 
52 public class TestDelegationToken {
53   private static final Log LOG = LogFactory.getLog(TestDelegationToken.class);
54   private MiniDFSCluster cluster;
55   Configuration config;
56 
57   @Before
setUp()58   public void setUp() throws Exception {
59     config = new Configuration();
60     config.setBoolean(DFSConfigKeys.DFS_WEBHDFS_ENABLED_KEY, true);
61     config.setLong(DFSConfigKeys.DFS_NAMENODE_DELEGATION_TOKEN_MAX_LIFETIME_KEY, 10000);
62     config.setLong(DFSConfigKeys.DFS_NAMENODE_DELEGATION_TOKEN_RENEW_INTERVAL_KEY, 5000);
63     config.set("hadoop.security.auth_to_local",
64         "RULE:[2:$1@$0](JobTracker@.*FOO.COM)s/@.*//" +
65         "DEFAULT");
66 
67     FileSystem.setDefaultUri(config, "hdfs://localhost:" + "0");
68     cluster = new MiniDFSCluster(0, config, 0, true, true, true,  null, null, null, null);
69     cluster.waitActive();
70     cluster.getNameNode().getNamesystem().getDelegationTokenSecretManager()
71 				.startThreads();
72     LOG.info("cluster up and running");
73   }
74 
75   @After
tearDown()76   public void tearDown() throws Exception {
77     LOG.info("starting shutting down the cluster");
78     if(cluster!=null) {
79       cluster.shutdown();
80     }
81     LOG.info("finished shutting down the cluster");
82   }
83 
generateDelegationToken( String owner, String renewer)84   private Token<DelegationTokenIdentifier> generateDelegationToken(
85       String owner, String renewer) {
86     DelegationTokenSecretManager dtSecretManager = cluster.getNameNode()
87         .getNamesystem().getDelegationTokenSecretManager();
88     DelegationTokenIdentifier dtId = new DelegationTokenIdentifier(new Text(
89         owner), new Text(renewer), null);
90     return new Token<DelegationTokenIdentifier>(dtId, dtSecretManager);
91   }
92 
93   @Test
testDelegationTokenSecretManager()94   public void testDelegationTokenSecretManager() throws Exception {
95     DelegationTokenSecretManager dtSecretManager = cluster.getNameNode()
96         .getNamesystem().getDelegationTokenSecretManager();
97     Token<DelegationTokenIdentifier> token = generateDelegationToken(
98         "SomeUser", "JobTracker");
99     // Fake renewer should not be able to renew
100     try {
101   	  dtSecretManager.renewToken(token, "FakeRenewer");
102   	  Assert.fail("should have failed");
103     } catch (AccessControlException ace) {
104       // PASS
105     }
106 	  dtSecretManager.renewToken(token, "JobTracker");
107     DelegationTokenIdentifier identifier = new DelegationTokenIdentifier();
108     byte[] tokenId = token.getIdentifier();
109     identifier.readFields(new DataInputStream(
110              new ByteArrayInputStream(tokenId)));
111     Assert.assertTrue(null != dtSecretManager.retrievePassword(identifier));
112     LOG.info("Sleep to expire the token");
113 	  Thread.sleep(6000);
114 	  //Token should be expired
115 	  try {
116 	    dtSecretManager.retrievePassword(identifier);
117 	    //Should not come here
118 	    Assert.fail("Token should have expired");
119 	  } catch (InvalidToken e) {
120 	    //Success
121 	  }
122 	  dtSecretManager.renewToken(token, "JobTracker");
123 	  LOG.info("Sleep beyond the max lifetime");
124 	  Thread.sleep(5000);
125 	  try {
126   	  dtSecretManager.renewToken(token, "JobTracker");
127   	  Assert.fail("should have been expired");
128 	  } catch (InvalidToken it) {
129 	    // PASS
130 	  }
131   }
132 
133   @Test
testCancelDelegationToken()134   public void testCancelDelegationToken() throws Exception {
135     DelegationTokenSecretManager dtSecretManager = cluster.getNameNode()
136         .getNamesystem().getDelegationTokenSecretManager();
137     Token<DelegationTokenIdentifier> token = generateDelegationToken(
138         "SomeUser", "JobTracker");
139     //Fake renewer should not be able to renew
140     try {
141       dtSecretManager.cancelToken(token, "FakeCanceller");
142       Assert.fail("should have failed");
143     } catch (AccessControlException ace) {
144       // PASS
145     }
146     dtSecretManager.cancelToken(token, "JobTracker");
147     try {
148       dtSecretManager.renewToken(token, "JobTracker");
149       Assert.fail("should have failed");
150     } catch (InvalidToken it) {
151       // PASS
152     }
153   }
154 
155   @Test
testDelegationTokenDFSApi()156   public void testDelegationTokenDFSApi() throws Exception {
157     DelegationTokenSecretManager dtSecretManager = cluster.getNameNode()
158         .getNamesystem().getDelegationTokenSecretManager();
159     DistributedFileSystem dfs = (DistributedFileSystem) cluster.getFileSystem();
160     final Token<DelegationTokenIdentifier> token = dfs.getDelegationToken(new Text("JobTracker"));
161     DelegationTokenIdentifier identifier = new DelegationTokenIdentifier();
162     byte[] tokenId = token.getIdentifier();
163     identifier.readFields(new DataInputStream(
164              new ByteArrayInputStream(tokenId)));
165     LOG.info("A valid token should have non-null password, and should be renewed successfully");
166     Assert.assertTrue(null != dtSecretManager.retrievePassword(identifier));
167     dtSecretManager.renewToken(token, "JobTracker");
168     UserGroupInformation.createRemoteUser("JobTracker").doAs(
169         new PrivilegedExceptionAction<Object>() {
170           @Override
171           public Object run() throws Exception {
172             token.renew(config);
173             token.cancel(config);
174             return null;
175           }
176         });
177   }
178 
179   @Test
testDelegationTokenWebHdfsApi()180   public void testDelegationTokenWebHdfsApi() throws Exception {
181     ((Log4JLogger)NamenodeWebHdfsMethods.LOG).getLogger().setLevel(Level.ALL);
182     final DelegationTokenSecretManager dtSecretManager = cluster.getNameNode(
183         ).getNamesystem().getDelegationTokenSecretManager();
184     final String uri = WebHdfsFileSystem.SCHEME  + "://"
185         + config.get("dfs.http.address");
186     //get file system as JobTracker
187     final UserGroupInformation ugi = UserGroupInformation.createUserForTesting(
188         "JobTracker", new String[]{"user"});
189     final WebHdfsFileSystem webhdfs = ugi.doAs(
190         new PrivilegedExceptionAction<WebHdfsFileSystem>() {
191       @Override
192       public WebHdfsFileSystem run() throws Exception {
193         return (WebHdfsFileSystem)FileSystem.get(new URI(uri), config);
194       }
195     });
196 
197     final Token<DelegationTokenIdentifier> token = webhdfs
198         .getDelegationToken("JobTracker");
199     DelegationTokenIdentifier identifier = new DelegationTokenIdentifier();
200     byte[] tokenId = token.getIdentifier();
201     identifier
202         .readFields(new DataInputStream(new ByteArrayInputStream(tokenId)));
203     LOG.info("A valid token should have non-null password, and should be renewed successfully");
204     Assert.assertTrue(null != dtSecretManager.retrievePassword(identifier));
205     dtSecretManager.renewToken(token, "JobTracker");
206     ugi.doAs(new PrivilegedExceptionAction<Object>() {
207       @Override
208       public Object run() throws Exception {
209         token.renew(config);
210         token.cancel(config);
211         return null;
212       }
213     });
214   }
215 
216   @Test
testDelegationTokenWithDoAs()217   public void testDelegationTokenWithDoAs() throws Exception {
218     final DistributedFileSystem dfs = (DistributedFileSystem) cluster.getFileSystem();
219     final Token<DelegationTokenIdentifier> token = dfs.getDelegationToken(new Text(
220         "JobTracker"));
221     final UserGroupInformation longUgi = UserGroupInformation
222         .createRemoteUser("JobTracker/foo.com@FOO.COM");
223     final UserGroupInformation shortUgi = UserGroupInformation
224         .createRemoteUser("JobTracker");
225     LOG.info("cluster at: " + dfs.getUri() +
226              " token for: " + token.getService());
227     longUgi.doAs(new PrivilegedExceptionAction<Object>() {
228       public Object run() throws IOException {
229         final DistributedFileSystem dfs = (DistributedFileSystem) cluster
230             .getFileSystem();
231         try {
232           //try renew with long name
233           dfs.renewDelegationToken(token);
234         } catch (IOException e) {
235           LOG.error("caught unexpected exception out of renew", e);
236           Assert.fail("Could not renew delegation token for user "+longUgi);
237         }
238         return null;
239       }
240     });
241     shortUgi.doAs(new PrivilegedExceptionAction<Object>() {
242       public Object run() throws IOException {
243         final DistributedFileSystem dfs = (DistributedFileSystem) cluster
244             .getFileSystem();
245         dfs.renewDelegationToken(token);
246         return null;
247       }
248     });
249     longUgi.doAs(new PrivilegedExceptionAction<Object>() {
250       public Object run() throws IOException {
251         final DistributedFileSystem dfs = (DistributedFileSystem) cluster
252             .getFileSystem();
253         try {
254           //try cancel with long name
255           dfs.cancelDelegationToken(token);
256         } catch (IOException e) {
257           Assert.fail("Could not cancel delegation token for user "+longUgi);
258         }
259         return null;
260       }
261     });
262   }
263 
264 }
265