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.resourcemanager.webapp; 20 21 import static org.junit.Assert.assertEquals; 22 import static org.junit.Assert.assertTrue; 23 import static org.junit.Assert.fail; 24 25 import java.io.File; 26 import java.io.IOException; 27 import java.net.HttpURLConnection; 28 import java.net.URL; 29 import java.util.Arrays; 30 import java.util.Collection; 31 32 import javax.ws.rs.core.MediaType; 33 34 import org.apache.hadoop.conf.Configuration; 35 import org.apache.hadoop.fs.CommonConfigurationKeys; 36 import org.apache.hadoop.fs.CommonConfigurationKeysPublic; 37 import org.apache.hadoop.minikdc.MiniKdc; 38 import org.apache.hadoop.security.UserGroupInformation; 39 import org.apache.hadoop.security.authentication.KerberosTestUtils; 40 import org.apache.hadoop.yarn.conf.YarnConfiguration; 41 import org.apache.hadoop.yarn.server.resourcemanager.MockRM; 42 import org.apache.hadoop.yarn.server.resourcemanager.rmapp.RMApp; 43 import org.apache.hadoop.yarn.server.resourcemanager.scheduler.ResourceScheduler; 44 import org.apache.hadoop.yarn.server.resourcemanager.scheduler.fifo.FifoScheduler; 45 import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.ApplicationSubmissionContextInfo; 46 import org.apache.hadoop.yarn.util.ConverterUtils; 47 import org.junit.AfterClass; 48 import org.junit.BeforeClass; 49 import org.junit.Test; 50 import org.junit.runner.RunWith; 51 import org.junit.runners.Parameterized; 52 import org.junit.runners.Parameterized.Parameters; 53 54 import com.sun.jersey.api.client.ClientResponse.Status; 55 56 /* Just a simple test class to ensure that the RM handles the static web user 57 * correctly for secure and un-secure modes 58 * 59 */ 60 @RunWith(Parameterized.class) 61 public class TestRMWebappAuthentication { 62 63 private static MockRM rm; 64 private static Configuration simpleConf; 65 private static Configuration kerberosConf; 66 67 private static final File testRootDir = new File("target", 68 TestRMWebServicesDelegationTokenAuthentication.class.getName() + "-root"); 69 private static File httpSpnegoKeytabFile = new File( 70 KerberosTestUtils.getKeytabFile()); 71 72 private static boolean miniKDCStarted = false; 73 private static MiniKdc testMiniKDC; 74 75 static { 76 simpleConf = new Configuration(); simpleConf.setInt(YarnConfiguration.RM_AM_MAX_ATTEMPTS, YarnConfiguration.DEFAULT_RM_AM_MAX_ATTEMPTS)77 simpleConf.setInt(YarnConfiguration.RM_AM_MAX_ATTEMPTS, 78 YarnConfiguration.DEFAULT_RM_AM_MAX_ATTEMPTS); simpleConf.setClass(YarnConfiguration.RM_SCHEDULER, FifoScheduler.class, ResourceScheduler.class)79 simpleConf.setClass(YarnConfiguration.RM_SCHEDULER, FifoScheduler.class, 80 ResourceScheduler.class); 81 simpleConf.setBoolean("mockrm.webapp.enabled", true); 82 kerberosConf = new Configuration(); kerberosConf.setInt(YarnConfiguration.RM_AM_MAX_ATTEMPTS, YarnConfiguration.DEFAULT_RM_AM_MAX_ATTEMPTS)83 kerberosConf.setInt(YarnConfiguration.RM_AM_MAX_ATTEMPTS, 84 YarnConfiguration.DEFAULT_RM_AM_MAX_ATTEMPTS); kerberosConf.setClass(YarnConfiguration.RM_SCHEDULER, FifoScheduler.class, ResourceScheduler.class)85 kerberosConf.setClass(YarnConfiguration.RM_SCHEDULER, FifoScheduler.class, 86 ResourceScheduler.class); kerberosConf.setBoolean(YarnConfiguration.YARN_ACL_ENABLE, true)87 kerberosConf.setBoolean(YarnConfiguration.YARN_ACL_ENABLE, true); kerberosConf.set( CommonConfigurationKeysPublic.HADOOP_SECURITY_AUTHENTICATION, R)88 kerberosConf.set( 89 CommonConfigurationKeysPublic.HADOOP_SECURITY_AUTHENTICATION, "kerberos"); kerberosConf.set(YarnConfiguration.RM_KEYTAB, httpSpnegoKeytabFile.getAbsolutePath())90 kerberosConf.set(YarnConfiguration.RM_KEYTAB, 91 httpSpnegoKeytabFile.getAbsolutePath()); 92 kerberosConf.setBoolean("mockrm.webapp.enabled", true); 93 } 94 95 @Parameters params()96 public static Collection params() { 97 return Arrays.asList(new Object[][] { { 1, simpleConf }, 98 { 2, kerberosConf } }); 99 } 100 TestRMWebappAuthentication(int run, Configuration conf)101 public TestRMWebappAuthentication(int run, Configuration conf) { 102 super(); 103 setupAndStartRM(conf); 104 } 105 106 @BeforeClass setUp()107 public static void setUp() { 108 try { 109 testMiniKDC = new MiniKdc(MiniKdc.createConf(), testRootDir); 110 setupKDC(); 111 } catch (Exception e) { 112 assertTrue("Couldn't create MiniKDC", false); 113 } 114 } 115 116 @AfterClass tearDown()117 public static void tearDown() { 118 if (testMiniKDC != null) { 119 testMiniKDC.stop(); 120 } 121 } 122 setupKDC()123 private static void setupKDC() throws Exception { 124 if (!miniKDCStarted) { 125 testMiniKDC.start(); 126 getKdc().createPrincipal(httpSpnegoKeytabFile, "HTTP/localhost", 127 "client", UserGroupInformation.getLoginUser().getShortUserName()); 128 miniKDCStarted = true; 129 } 130 } 131 getKdc()132 private static MiniKdc getKdc() { 133 return testMiniKDC; 134 } 135 setupAndStartRM(Configuration conf)136 private static void setupAndStartRM(Configuration conf) { 137 UserGroupInformation.setConfiguration(conf); 138 rm = new MockRM(conf); 139 } 140 141 // ensure that in a non-secure cluster users can access 142 // the web pages as earlier and submit apps as anonymous 143 // user or by identifying themselves 144 @Test testSimpleAuth()145 public void testSimpleAuth() throws Exception { 146 147 rm.start(); 148 149 // ensure users can access web pages 150 // this should work for secure and non-secure clusters 151 URL url = new URL("http://localhost:8088/cluster"); 152 HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 153 try { 154 conn.getInputStream(); 155 assertEquals(Status.OK.getStatusCode(), conn.getResponseCode()); 156 } catch (Exception e) { 157 fail("Fetching url failed"); 158 } 159 160 if (UserGroupInformation.isSecurityEnabled()) { 161 testAnonymousKerberosUser(); 162 } else { 163 testAnonymousSimpleUser(); 164 } 165 166 rm.stop(); 167 } 168 testAnonymousKerberosUser()169 private void testAnonymousKerberosUser() throws Exception { 170 171 ApplicationSubmissionContextInfo app = 172 new ApplicationSubmissionContextInfo(); 173 String appid = "application_123_0"; 174 app.setApplicationId(appid); 175 String requestBody = 176 TestRMWebServicesDelegationTokenAuthentication 177 .getMarshalledAppInfo(app); 178 179 URL url = 180 new URL("http://localhost:8088/ws/v1/cluster/apps/new-application"); 181 HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 182 TestRMWebServicesDelegationTokenAuthentication.setupConn(conn, "POST", 183 "application/xml", requestBody); 184 185 try { 186 conn.getInputStream(); 187 fail("Anonymous users should not be allowed to get new application ids in secure mode."); 188 } catch (IOException ie) { 189 assertEquals(Status.FORBIDDEN.getStatusCode(), conn.getResponseCode()); 190 } 191 192 url = new URL("http://localhost:8088/ws/v1/cluster/apps"); 193 conn = (HttpURLConnection) url.openConnection(); 194 TestRMWebServicesDelegationTokenAuthentication.setupConn(conn, "POST", 195 "application/xml", requestBody); 196 197 try { 198 conn.getInputStream(); 199 fail("Anonymous users should not be allowed to submit apps in secure mode."); 200 } catch (IOException ie) { 201 assertEquals(Status.FORBIDDEN.getStatusCode(), conn.getResponseCode()); 202 } 203 204 requestBody = "{ \"state\": \"KILLED\"}"; 205 url = 206 new URL( 207 "http://localhost:8088/ws/v1/cluster/apps/application_123_0/state"); 208 conn = (HttpURLConnection) url.openConnection(); 209 TestRMWebServicesDelegationTokenAuthentication.setupConn(conn, "PUT", 210 "application/json", requestBody); 211 212 try { 213 conn.getInputStream(); 214 fail("Anonymous users should not be allowed to kill apps in secure mode."); 215 } catch (IOException ie) { 216 assertEquals(Status.FORBIDDEN.getStatusCode(), conn.getResponseCode()); 217 } 218 } 219 testAnonymousSimpleUser()220 private void testAnonymousSimpleUser() throws Exception { 221 222 ApplicationSubmissionContextInfo app = 223 new ApplicationSubmissionContextInfo(); 224 String appid = "application_123_0"; 225 app.setApplicationId(appid); 226 String requestBody = 227 TestRMWebServicesDelegationTokenAuthentication 228 .getMarshalledAppInfo(app); 229 230 URL url = new URL("http://localhost:8088/ws/v1/cluster/apps"); 231 HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 232 TestRMWebServicesDelegationTokenAuthentication.setupConn(conn, "POST", 233 "application/xml", requestBody); 234 235 conn.getInputStream(); 236 assertEquals(Status.ACCEPTED.getStatusCode(), conn.getResponseCode()); 237 boolean appExists = 238 rm.getRMContext().getRMApps() 239 .containsKey(ConverterUtils.toApplicationId(appid)); 240 assertTrue(appExists); 241 RMApp actualApp = 242 rm.getRMContext().getRMApps() 243 .get(ConverterUtils.toApplicationId(appid)); 244 String owner = actualApp.getUser(); 245 assertEquals( 246 rm.getConfig().get(CommonConfigurationKeys.HADOOP_HTTP_STATIC_USER, 247 CommonConfigurationKeys.DEFAULT_HADOOP_HTTP_STATIC_USER), owner); 248 249 appid = "application_123_1"; 250 app.setApplicationId(appid); 251 requestBody = 252 TestRMWebServicesDelegationTokenAuthentication 253 .getMarshalledAppInfo(app); 254 url = new URL("http://localhost:8088/ws/v1/cluster/apps?user.name=client"); 255 conn = (HttpURLConnection) url.openConnection(); 256 TestRMWebServicesDelegationTokenAuthentication.setupConn(conn, "POST", 257 MediaType.APPLICATION_XML, requestBody); 258 259 conn.getInputStream(); 260 appExists = 261 rm.getRMContext().getRMApps() 262 .containsKey(ConverterUtils.toApplicationId(appid)); 263 assertTrue(appExists); 264 actualApp = 265 rm.getRMContext().getRMApps() 266 .get(ConverterUtils.toApplicationId(appid)); 267 owner = actualApp.getUser(); 268 assertEquals("client", owner); 269 270 } 271 272 } 273