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.yarn.server.timeline.security; 20 21 import static org.junit.Assert.assertTrue; 22 23 import java.io.File; 24 import java.security.PrivilegedExceptionAction; 25 import java.util.Arrays; 26 import java.util.Collection; 27 import java.util.concurrent.Callable; 28 29 import org.apache.hadoop.conf.Configuration; 30 import org.apache.hadoop.fs.CommonConfigurationKeysPublic; 31 import org.apache.hadoop.fs.FileUtil; 32 import org.apache.hadoop.http.HttpConfig; 33 import org.apache.hadoop.io.Text; 34 import org.apache.hadoop.minikdc.MiniKdc; 35 import org.apache.hadoop.security.UserGroupInformation; 36 import org.apache.hadoop.security.authentication.KerberosTestUtils; 37 import org.apache.hadoop.security.authentication.client.AuthenticationException; 38 import org.apache.hadoop.security.authentication.server.KerberosAuthenticationHandler; 39 import org.apache.hadoop.security.authorize.AuthorizationException; 40 import org.apache.hadoop.security.ssl.KeyStoreTestUtil; 41 import org.apache.hadoop.security.token.Token; 42 import org.apache.hadoop.yarn.api.records.timeline.TimelineDomain; 43 import org.apache.hadoop.yarn.api.records.timeline.TimelineEntity; 44 import org.apache.hadoop.yarn.api.records.timeline.TimelinePutResponse; 45 import org.apache.hadoop.yarn.client.api.TimelineClient; 46 import org.apache.hadoop.yarn.conf.YarnConfiguration; 47 import org.apache.hadoop.yarn.security.client.TimelineDelegationTokenIdentifier; 48 import org.apache.hadoop.yarn.server.applicationhistoryservice.ApplicationHistoryServer; 49 import org.apache.hadoop.yarn.server.timeline.MemoryTimelineStore; 50 import org.apache.hadoop.yarn.server.timeline.TimelineStore; 51 import org.junit.AfterClass; 52 import org.junit.Assert; 53 import org.junit.BeforeClass; 54 import org.junit.Test; 55 import org.junit.runner.RunWith; 56 import org.junit.runners.Parameterized; 57 58 @RunWith(Parameterized.class) 59 public class TestTimelineAuthenticationFilter { 60 61 private static final String FOO_USER = "foo"; 62 private static final String BAR_USER = "bar"; 63 private static final String HTTP_USER = "HTTP"; 64 65 private static final File testRootDir = new File( 66 System.getProperty("test.build.dir", "target/test-dir"), 67 TestTimelineAuthenticationFilter.class.getName() + "-root"); 68 private static File httpSpnegoKeytabFile = new File( 69 KerberosTestUtils.getKeytabFile()); 70 private static String httpSpnegoPrincipal = 71 KerberosTestUtils.getServerPrincipal(); 72 private static final String BASEDIR = 73 System.getProperty("test.build.dir", "target/test-dir") + "/" 74 + TestTimelineAuthenticationFilter.class.getSimpleName(); 75 76 @Parameterized.Parameters withSsl()77 public static Collection<Object[]> withSsl() { 78 return Arrays.asList(new Object[][] { { false }, { true } }); 79 } 80 81 private static MiniKdc testMiniKDC; 82 private static String keystoresDir; 83 private static String sslConfDir; 84 private static ApplicationHistoryServer testTimelineServer; 85 private static Configuration conf; 86 private static boolean withSsl; 87 TestTimelineAuthenticationFilter(boolean withSsl)88 public TestTimelineAuthenticationFilter(boolean withSsl) { 89 TestTimelineAuthenticationFilter.withSsl = withSsl; 90 } 91 92 @BeforeClass setup()93 public static void setup() { 94 try { 95 testMiniKDC = new MiniKdc(MiniKdc.createConf(), testRootDir); 96 testMiniKDC.start(); 97 testMiniKDC.createPrincipal( 98 httpSpnegoKeytabFile, HTTP_USER + "/localhost"); 99 } catch (Exception e) { 100 assertTrue("Couldn't setup MiniKDC", false); 101 } 102 103 try { 104 testTimelineServer = new ApplicationHistoryServer(); 105 conf = new Configuration(false); 106 conf.setStrings(TimelineAuthenticationFilterInitializer.PREFIX + "type", 107 "kerberos"); 108 conf.set(TimelineAuthenticationFilterInitializer.PREFIX + 109 KerberosAuthenticationHandler.PRINCIPAL, httpSpnegoPrincipal); 110 conf.set(TimelineAuthenticationFilterInitializer.PREFIX + 111 KerberosAuthenticationHandler.KEYTAB, 112 httpSpnegoKeytabFile.getAbsolutePath()); 113 conf.set(CommonConfigurationKeysPublic.HADOOP_SECURITY_AUTHENTICATION, 114 "kerberos"); 115 conf.set(YarnConfiguration.TIMELINE_SERVICE_PRINCIPAL, 116 httpSpnegoPrincipal); 117 conf.set(YarnConfiguration.TIMELINE_SERVICE_KEYTAB, 118 httpSpnegoKeytabFile.getAbsolutePath()); 119 conf.setBoolean(YarnConfiguration.TIMELINE_SERVICE_ENABLED, true); 120 conf.setClass(YarnConfiguration.TIMELINE_SERVICE_STORE, 121 MemoryTimelineStore.class, TimelineStore.class); 122 conf.set(YarnConfiguration.TIMELINE_SERVICE_ADDRESS, 123 "localhost:10200"); 124 conf.set(YarnConfiguration.TIMELINE_SERVICE_WEBAPP_ADDRESS, 125 "localhost:8188"); 126 conf.set(YarnConfiguration.TIMELINE_SERVICE_WEBAPP_HTTPS_ADDRESS, 127 "localhost:8190"); 128 conf.set("hadoop.proxyuser.HTTP.hosts", "*"); 129 conf.set("hadoop.proxyuser.HTTP.users", FOO_USER); 130 conf.setInt(YarnConfiguration.TIMELINE_SERVICE_CLIENT_MAX_RETRIES, 1); 131 132 if (withSsl) { 133 conf.set(YarnConfiguration.YARN_HTTP_POLICY_KEY, 134 HttpConfig.Policy.HTTPS_ONLY.name()); 135 File base = new File(BASEDIR); 136 FileUtil.fullyDelete(base); 137 base.mkdirs(); 138 keystoresDir = new File(BASEDIR).getAbsolutePath(); 139 sslConfDir = 140 KeyStoreTestUtil.getClasspathDir(TestTimelineAuthenticationFilter.class); 141 KeyStoreTestUtil.setupSSLConfig(keystoresDir, sslConfDir, conf, false); 142 } 143 144 UserGroupInformation.setConfiguration(conf); 145 testTimelineServer.init(conf); 146 testTimelineServer.start(); 147 } catch (Exception e) { 148 assertTrue("Couldn't setup TimelineServer", false); 149 } 150 } 151 createTimelineClientForUGI()152 private TimelineClient createTimelineClientForUGI() { 153 TimelineClient client = TimelineClient.createTimelineClient(); 154 client.init(conf); 155 client.start(); 156 return client; 157 } 158 159 @AfterClass tearDown()160 public static void tearDown() throws Exception { 161 if (testMiniKDC != null) { 162 testMiniKDC.stop(); 163 } 164 165 if (testTimelineServer != null) { 166 testTimelineServer.stop(); 167 } 168 169 if (withSsl) { 170 KeyStoreTestUtil.cleanupSSLConfig(keystoresDir, sslConfDir); 171 File base = new File(BASEDIR); 172 FileUtil.fullyDelete(base); 173 } 174 } 175 176 @Test testPutTimelineEntities()177 public void testPutTimelineEntities() throws Exception { 178 KerberosTestUtils.doAs(HTTP_USER + "/localhost", new Callable<Void>() { 179 @Override 180 public Void call() throws Exception { 181 TimelineClient client = createTimelineClientForUGI(); 182 TimelineEntity entityToStore = new TimelineEntity(); 183 entityToStore.setEntityType( 184 TestTimelineAuthenticationFilter.class.getName()); 185 entityToStore.setEntityId("entity1"); 186 entityToStore.setStartTime(0L); 187 TimelinePutResponse putResponse = client.putEntities(entityToStore); 188 Assert.assertEquals(0, putResponse.getErrors().size()); 189 TimelineEntity entityToRead = 190 testTimelineServer.getTimelineStore().getEntity( 191 "entity1", TestTimelineAuthenticationFilter.class.getName(), null); 192 Assert.assertNotNull(entityToRead); 193 return null; 194 } 195 }); 196 } 197 198 @Test testPutDomains()199 public void testPutDomains() throws Exception { 200 KerberosTestUtils.doAs(HTTP_USER + "/localhost", new Callable<Void>() { 201 @Override 202 public Void call() throws Exception { 203 TimelineClient client = createTimelineClientForUGI(); 204 TimelineDomain domainToStore = new TimelineDomain(); 205 domainToStore.setId(TestTimelineAuthenticationFilter.class.getName()); 206 domainToStore.setReaders("*"); 207 domainToStore.setWriters("*"); 208 client.putDomain(domainToStore); 209 TimelineDomain domainToRead = 210 testTimelineServer.getTimelineStore().getDomain( 211 TestTimelineAuthenticationFilter.class.getName()); 212 Assert.assertNotNull(domainToRead); 213 return null; 214 } 215 }); 216 } 217 218 @Test testDelegationTokenOperations()219 public void testDelegationTokenOperations() throws Exception { 220 TimelineClient httpUserClient = 221 KerberosTestUtils.doAs(HTTP_USER + "/localhost", new Callable<TimelineClient>() { 222 @Override 223 public TimelineClient call() throws Exception { 224 return createTimelineClientForUGI(); 225 } 226 }); 227 UserGroupInformation httpUser = 228 KerberosTestUtils.doAs(HTTP_USER + "/localhost", new Callable<UserGroupInformation>() { 229 @Override 230 public UserGroupInformation call() throws Exception { 231 return UserGroupInformation.getCurrentUser(); 232 } 233 }); 234 // Let HTTP user to get the delegation for itself 235 Token<TimelineDelegationTokenIdentifier> token = 236 httpUserClient.getDelegationToken(httpUser.getShortUserName()); 237 Assert.assertNotNull(token); 238 TimelineDelegationTokenIdentifier tDT = token.decodeIdentifier(); 239 Assert.assertNotNull(tDT); 240 Assert.assertEquals(new Text(HTTP_USER), tDT.getOwner()); 241 242 // Renew token 243 Assert.assertFalse(token.getService().toString().isEmpty()); 244 // Renew the token from the token service address 245 long renewTime1 = httpUserClient.renewDelegationToken(token); 246 Thread.sleep(100); 247 token.setService(new Text()); 248 Assert.assertTrue(token.getService().toString().isEmpty()); 249 // If the token service address is not avaiable, it still can be renewed 250 // from the configured address 251 long renewTime2 = httpUserClient.renewDelegationToken(token); 252 Assert.assertTrue(renewTime1 < renewTime2); 253 254 // Cancel token 255 Assert.assertTrue(token.getService().toString().isEmpty()); 256 // If the token service address is not avaiable, it still can be canceled 257 // from the configured address 258 httpUserClient.cancelDelegationToken(token); 259 // Renew should not be successful because the token is canceled 260 try { 261 httpUserClient.renewDelegationToken(token); 262 Assert.fail(); 263 } catch (Exception e) { 264 Assert.assertTrue(e.getMessage().contains( 265 "Renewal request for unknown token")); 266 } 267 268 // Let HTTP user to get the delegation token for FOO user 269 UserGroupInformation fooUgi = UserGroupInformation.createProxyUser( 270 FOO_USER, httpUser); 271 TimelineClient fooUserClient = fooUgi.doAs( 272 new PrivilegedExceptionAction<TimelineClient>() { 273 @Override 274 public TimelineClient run() throws Exception { 275 return createTimelineClientForUGI(); 276 } 277 }); 278 token = fooUserClient.getDelegationToken(httpUser.getShortUserName()); 279 Assert.assertNotNull(token); 280 tDT = token.decodeIdentifier(); 281 Assert.assertNotNull(tDT); 282 Assert.assertEquals(new Text(FOO_USER), tDT.getOwner()); 283 Assert.assertEquals(new Text(HTTP_USER), tDT.getRealUser()); 284 285 // Renew token as the renewer 286 final Token<TimelineDelegationTokenIdentifier> tokenToRenew = token; 287 renewTime1 = httpUserClient.renewDelegationToken(tokenToRenew); 288 renewTime2 = httpUserClient.renewDelegationToken(tokenToRenew); 289 Assert.assertTrue(renewTime1 < renewTime2); 290 291 // Cancel token 292 Assert.assertFalse(tokenToRenew.getService().toString().isEmpty()); 293 // Cancel the token from the token service address 294 fooUserClient.cancelDelegationToken(tokenToRenew); 295 296 // Renew should not be successful because the token is canceled 297 try { 298 httpUserClient.renewDelegationToken(tokenToRenew); 299 Assert.fail(); 300 } catch (Exception e) { 301 Assert.assertTrue( 302 e.getMessage().contains("Renewal request for unknown token")); 303 } 304 305 // Let HTTP user to get the delegation token for BAR user 306 UserGroupInformation barUgi = UserGroupInformation.createProxyUser( 307 BAR_USER, httpUser); 308 TimelineClient barUserClient = barUgi.doAs( 309 new PrivilegedExceptionAction<TimelineClient>() { 310 @Override 311 public TimelineClient run() { 312 return createTimelineClientForUGI(); 313 } 314 }); 315 316 try { 317 barUserClient.getDelegationToken(httpUser.getShortUserName()); 318 Assert.fail(); 319 } catch (Exception e) { 320 Assert.assertTrue(e.getCause() instanceof AuthorizationException || e.getCause() instanceof AuthenticationException); 321 } 322 } 323 } 324