YARN-4009. CORS support for ResourceManager REST API. ( Varun Vasudev via jeagles)

(cherry picked from commit f8adeb712d)
This commit is contained in:
Jonathan Eagles 2015-10-23 10:34:08 -05:00
parent 4bb7e68eb6
commit 6db7bfbcfd
16 changed files with 282 additions and 80 deletions

View File

@ -0,0 +1,74 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hadoop.security;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.http.FilterContainer;
import org.apache.hadoop.http.FilterInitializer;
import org.apache.hadoop.security.http.CrossOriginFilter;
public class HttpCrossOriginFilterInitializer extends FilterInitializer {
public static final String PREFIX = "hadoop.http.cross-origin.";
public static final String ENABLED_SUFFIX = "enabled";
private static final Log LOG =
LogFactory.getLog(HttpCrossOriginFilterInitializer.class);
@Override
public void initFilter(FilterContainer container, Configuration conf) {
String key = getEnabledConfigKey();
boolean enabled = conf.getBoolean(key, false);
if (enabled) {
container.addGlobalFilter("Cross Origin Filter",
CrossOriginFilter.class.getName(),
getFilterParameters(conf, getPrefix()));
} else {
LOG.info("CORS filter not enabled. Please set " + key
+ " to 'true' to enable it");
}
}
protected static Map<String, String> getFilterParameters(Configuration conf,
String prefix) {
Map<String, String> filterParams = new HashMap<String, String>();
for (Map.Entry<String, String> entry : conf.getValByRegex(prefix)
.entrySet()) {
String name = entry.getKey();
String value = entry.getValue();
name = name.substring(prefix.length());
filterParams.put(name, value);
}
return filterParams;
}
protected String getPrefix() {
return HttpCrossOriginFilterInitializer.PREFIX;
}
protected String getEnabledConfigKey() {
return getPrefix() + HttpCrossOriginFilterInitializer.ENABLED_SUFFIX;
}
}

View File

@ -16,7 +16,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.apache.hadoop.yarn.server.timeline.webapp; package org.apache.hadoop.security.http;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
@ -246,7 +246,7 @@ public class CrossOriginFilter implements Filter {
if (accessControlRequestHeaders == null) { if (accessControlRequestHeaders == null) {
return true; return true;
} }
String headers[] = accessControlRequestHeaders.trim().split("\\s*,\\s*"); String[] headers = accessControlRequestHeaders.trim().split("\\s*,\\s*");
return allowedHeaders.containsAll(Arrays.asList(headers)); return allowedHeaders.containsAll(Arrays.asList(headers));
} }

View File

@ -1405,6 +1405,42 @@ for ldap providers in the same way as above does.
</description> </description>
</property> </property>
<!-- HTTP CORS support -->
<property>
<description>Enable/disable the cross-origin (CORS) filter.</description>
<name>hadoop.http.cross-origin.enabled</name>
<value>false</value>
</property>
<property>
<description>Comma separated list of origins that are allowed for web
services needing cross-origin (CORS) support. Wildcards (*) and patterns
allowed</description>
<name>hadoop.http.cross-origin.allowed-origins</name>
<value>*</value>
</property>
<property>
<description>Comma separated list of methods that are allowed for web
services needing cross-origin (CORS) support.</description>
<name>hadoop.http.cross-origin.allowed-methods</name>
<value>GET,POST,HEAD</value>
</property>
<property>
<description>Comma separated list of headers that are allowed for web
services needing cross-origin (CORS) support.</description>
<name>hadoop.http.cross-origin.allowed-headers</name>
<value>X-Requested-With,Content-Type,Accept,Origin</value>
</property>
<property>
<description>The number of seconds a pre-flighted request can be cached
for web services needing cross-origin (CORS) support.</description>
<name>hadoop.http.cross-origin.max-age</name>
<value>1800</value>
</property>
<property> <property>
<name>dfs.ha.fencing.methods</name> <name>dfs.ha.fencing.methods</name>
<value></value> <value></value>

View File

@ -60,3 +60,17 @@ IMPORTANT: when using IP addresses, browsers ignore cookies with domain settings
`hadoop.http.authentication.kerberos.principal`: Indicates the Kerberos principal to be used for HTTP endpoint when using 'kerberos' authentication. The principal short name must be `HTTP` per Kerberos HTTP SPNEGO specification. The default value is `HTTP/_HOST@$LOCALHOST`, where `_HOST` -if present- is replaced with bind address of the HTTP server. `hadoop.http.authentication.kerberos.principal`: Indicates the Kerberos principal to be used for HTTP endpoint when using 'kerberos' authentication. The principal short name must be `HTTP` per Kerberos HTTP SPNEGO specification. The default value is `HTTP/_HOST@$LOCALHOST`, where `_HOST` -if present- is replaced with bind address of the HTTP server.
`hadoop.http.authentication.kerberos.keytab`: Location of the keytab file with the credentials for the Kerberos principal used for the HTTP endpoint. The default value is `$user.home/hadoop.keytab`.i `hadoop.http.authentication.kerberos.keytab`: Location of the keytab file with the credentials for the Kerberos principal used for the HTTP endpoint. The default value is `$user.home/hadoop.keytab`.i
CORS
----
To enable cross-origin support (CORS), please set the following configuration parameters:
Add org.apache.hadoop.security.HttpCrossOriginFilterInitializer to hadoop.http.filter.initializers in core-site.xml. You will also need to set the following properties in core-site.xml -
| Property | Default Value | Description |
|:---- |:---- |:---- |:---- |
| hadoop.http.cross-origin.enabled | false | Enables cross origin support for all web-services |
| hadoop.http.cross-origin.allowed-origins | \* | Comma separated list of origins that are allowed, wildcards (\*) and patterns allowed |
| hadoop.http.cross-origin.allowed-methods | GET,POST,HEAD | Comma separated list of methods that are allowed |
| hadoop.http.cross-origin.allowed-headers | X-Requested-With,Content-Type,Accept,Origin | Comma separated list of headers that are allowed |
| hadoop.http.cross-origin.max-age | 1800 | Number of seconds a pre-flighted request can be cached |

View File

@ -16,7 +16,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.apache.hadoop.yarn.server.timeline.webapp; package org.apache.hadoop.security;
import java.util.Map; import java.util.Map;
@ -25,21 +25,22 @@ import org.apache.hadoop.conf.Configuration;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
public class TestCrossOriginFilterInitializer { public class TestHttpCrossOriginFilterInitializer {
@Test @Test
public void testGetFilterParameters() { public void testGetFilterParameters() {
// Initialize configuration object // Initialize configuration object
Configuration conf = new Configuration(); Configuration conf = new Configuration();
conf.set(CrossOriginFilterInitializer.PREFIX + "rootparam", "rootvalue"); conf.set(HttpCrossOriginFilterInitializer.PREFIX + "rootparam",
conf.set(CrossOriginFilterInitializer.PREFIX + "nested.param", "rootvalue");
conf.set(HttpCrossOriginFilterInitializer.PREFIX + "nested.param",
"nestedvalue"); "nestedvalue");
conf.set("outofscopeparam", "outofscopevalue"); conf.set("outofscopeparam", "outofscopevalue");
// call function under test // call function under test
Map<String, String> filterParameters = Map<String, String> filterParameters = HttpCrossOriginFilterInitializer
CrossOriginFilterInitializer.getFilterParameters(conf); .getFilterParameters(conf, HttpCrossOriginFilterInitializer.PREFIX);
// retrieve values // retrieve values
String rootvalue = filterParameters.get("rootparam"); String rootvalue = filterParameters.get("rootparam");

View File

@ -16,7 +16,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.apache.hadoop.yarn.server.timeline.webapp; package org.apache.hadoop.security.http;
import java.io.IOException; import java.io.IOException;
import java.util.Collections; import java.util.Collections;
@ -31,13 +31,13 @@ import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.apache.hadoop.security.http.CrossOriginFilter;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.mockito.Mockito;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
public class TestCrossOriginFilter { public class TestCrossOriginFilter {
@ -50,20 +50,20 @@ public class TestCrossOriginFilter {
FilterConfig filterConfig = new FilterConfigTest(conf); FilterConfig filterConfig = new FilterConfigTest(conf);
// Origin is not specified for same origin requests // Origin is not specified for same origin requests
HttpServletRequest mockReq = mock(HttpServletRequest.class); HttpServletRequest mockReq = Mockito.mock(HttpServletRequest.class);
when(mockReq.getHeader(CrossOriginFilter.ORIGIN)).thenReturn(null); Mockito.when(mockReq.getHeader(CrossOriginFilter.ORIGIN)).thenReturn(null);
// Objects to verify interactions based on request // Objects to verify interactions based on request
HttpServletResponse mockRes = mock(HttpServletResponse.class); HttpServletResponse mockRes = Mockito.mock(HttpServletResponse.class);
FilterChain mockChain = mock(FilterChain.class); FilterChain mockChain = Mockito.mock(FilterChain.class);
// Object under test // Object under test
CrossOriginFilter filter = new CrossOriginFilter(); CrossOriginFilter filter = new CrossOriginFilter();
filter.init(filterConfig); filter.init(filterConfig);
filter.doFilter(mockReq, mockRes, mockChain); filter.doFilter(mockReq, mockRes, mockChain);
verifyZeroInteractions(mockRes); Mockito.verifyZeroInteractions(mockRes);
verify(mockChain).doFilter(mockReq, mockRes); Mockito.verify(mockChain).doFilter(mockReq, mockRes);
} }
@Test @Test
@ -91,11 +91,12 @@ public class TestCrossOriginFilter {
String encodedResponseSplitOrigin = String encodedResponseSplitOrigin =
CrossOriginFilter.encodeHeader(httpResponseSplitOrigin); CrossOriginFilter.encodeHeader(httpResponseSplitOrigin);
Assert.assertEquals("Http response split origin should be protected against", Assert.assertEquals("Http response split origin should be protected against",
validOrigin, encodedResponseSplitOrigin); validOrigin, encodedResponseSplitOrigin);
// Test Origin List // Test Origin List
String validOriginList = "http://foo.example.com:12345 http://bar.example.com:12345"; String validOriginList = "http://foo.example.com:12345 http://bar.example.com:12345";
String encodedValidOriginList = CrossOriginFilter.encodeHeader(validOriginList); String encodedValidOriginList = CrossOriginFilter
.encodeHeader(validOriginList);
Assert.assertEquals("Valid origin list encoding should match exactly", Assert.assertEquals("Valid origin list encoding should match exactly",
validOriginList, encodedValidOriginList); validOriginList, encodedValidOriginList);
} }
@ -135,20 +136,20 @@ public class TestCrossOriginFilter {
FilterConfig filterConfig = new FilterConfigTest(conf); FilterConfig filterConfig = new FilterConfigTest(conf);
// Origin is not specified for same origin requests // Origin is not specified for same origin requests
HttpServletRequest mockReq = mock(HttpServletRequest.class); HttpServletRequest mockReq = Mockito.mock(HttpServletRequest.class);
when(mockReq.getHeader(CrossOriginFilter.ORIGIN)).thenReturn("example.org"); Mockito.when(mockReq.getHeader(CrossOriginFilter.ORIGIN)).thenReturn("example.org");
// Objects to verify interactions based on request // Objects to verify interactions based on request
HttpServletResponse mockRes = mock(HttpServletResponse.class); HttpServletResponse mockRes = Mockito.mock(HttpServletResponse.class);
FilterChain mockChain = mock(FilterChain.class); FilterChain mockChain = Mockito.mock(FilterChain.class);
// Object under test // Object under test
CrossOriginFilter filter = new CrossOriginFilter(); CrossOriginFilter filter = new CrossOriginFilter();
filter.init(filterConfig); filter.init(filterConfig);
filter.doFilter(mockReq, mockRes, mockChain); filter.doFilter(mockReq, mockRes, mockChain);
verifyZeroInteractions(mockRes); Mockito.verifyZeroInteractions(mockRes);
verify(mockChain).doFilter(mockReq, mockRes); Mockito.verify(mockChain).doFilter(mockReq, mockRes);
} }
@Test @Test
@ -160,22 +161,23 @@ public class TestCrossOriginFilter {
FilterConfig filterConfig = new FilterConfigTest(conf); FilterConfig filterConfig = new FilterConfigTest(conf);
// Origin is not specified for same origin requests // Origin is not specified for same origin requests
HttpServletRequest mockReq = mock(HttpServletRequest.class); HttpServletRequest mockReq = Mockito.mock(HttpServletRequest.class);
when(mockReq.getHeader(CrossOriginFilter.ORIGIN)).thenReturn("example.com"); Mockito.when(mockReq.getHeader(CrossOriginFilter.ORIGIN)).thenReturn("example.com");
when(mockReq.getHeader(CrossOriginFilter.ACCESS_CONTROL_REQUEST_METHOD)) Mockito.when(
mockReq.getHeader(CrossOriginFilter.ACCESS_CONTROL_REQUEST_METHOD))
.thenReturn("DISALLOWED_METHOD"); .thenReturn("DISALLOWED_METHOD");
// Objects to verify interactions based on request // Objects to verify interactions based on request
HttpServletResponse mockRes = mock(HttpServletResponse.class); HttpServletResponse mockRes = Mockito.mock(HttpServletResponse.class);
FilterChain mockChain = mock(FilterChain.class); FilterChain mockChain = Mockito.mock(FilterChain.class);
// Object under test // Object under test
CrossOriginFilter filter = new CrossOriginFilter(); CrossOriginFilter filter = new CrossOriginFilter();
filter.init(filterConfig); filter.init(filterConfig);
filter.doFilter(mockReq, mockRes, mockChain); filter.doFilter(mockReq, mockRes, mockChain);
verifyZeroInteractions(mockRes); Mockito.verifyZeroInteractions(mockRes);
verify(mockChain).doFilter(mockReq, mockRes); Mockito.verify(mockChain).doFilter(mockReq, mockRes);
} }
@Test @Test
@ -187,24 +189,26 @@ public class TestCrossOriginFilter {
FilterConfig filterConfig = new FilterConfigTest(conf); FilterConfig filterConfig = new FilterConfigTest(conf);
// Origin is not specified for same origin requests // Origin is not specified for same origin requests
HttpServletRequest mockReq = mock(HttpServletRequest.class); HttpServletRequest mockReq = Mockito.mock(HttpServletRequest.class);
when(mockReq.getHeader(CrossOriginFilter.ORIGIN)).thenReturn("example.com"); Mockito.when(mockReq.getHeader(CrossOriginFilter.ORIGIN)).thenReturn("example.com");
when(mockReq.getHeader(CrossOriginFilter.ACCESS_CONTROL_REQUEST_METHOD)) Mockito.when(
mockReq.getHeader(CrossOriginFilter.ACCESS_CONTROL_REQUEST_METHOD))
.thenReturn("GET"); .thenReturn("GET");
when(mockReq.getHeader(CrossOriginFilter.ACCESS_CONTROL_REQUEST_HEADERS)) Mockito.when(
mockReq.getHeader(CrossOriginFilter.ACCESS_CONTROL_REQUEST_HEADERS))
.thenReturn("Disallowed-Header"); .thenReturn("Disallowed-Header");
// Objects to verify interactions based on request // Objects to verify interactions based on request
HttpServletResponse mockRes = mock(HttpServletResponse.class); HttpServletResponse mockRes = Mockito.mock(HttpServletResponse.class);
FilterChain mockChain = mock(FilterChain.class); FilterChain mockChain = Mockito.mock(FilterChain.class);
// Object under test // Object under test
CrossOriginFilter filter = new CrossOriginFilter(); CrossOriginFilter filter = new CrossOriginFilter();
filter.init(filterConfig); filter.init(filterConfig);
filter.doFilter(mockReq, mockRes, mockChain); filter.doFilter(mockReq, mockRes, mockChain);
verifyZeroInteractions(mockRes); Mockito.verifyZeroInteractions(mockRes);
verify(mockChain).doFilter(mockReq, mockRes); Mockito.verify(mockChain).doFilter(mockReq, mockRes);
} }
@Test @Test
@ -216,32 +220,34 @@ public class TestCrossOriginFilter {
FilterConfig filterConfig = new FilterConfigTest(conf); FilterConfig filterConfig = new FilterConfigTest(conf);
// Origin is not specified for same origin requests // Origin is not specified for same origin requests
HttpServletRequest mockReq = mock(HttpServletRequest.class); HttpServletRequest mockReq = Mockito.mock(HttpServletRequest.class);
when(mockReq.getHeader(CrossOriginFilter.ORIGIN)).thenReturn("example.com"); Mockito.when(mockReq.getHeader(CrossOriginFilter.ORIGIN)).thenReturn("example.com");
when(mockReq.getHeader(CrossOriginFilter.ACCESS_CONTROL_REQUEST_METHOD)) Mockito.when(
mockReq.getHeader(CrossOriginFilter.ACCESS_CONTROL_REQUEST_METHOD))
.thenReturn("GET"); .thenReturn("GET");
when(mockReq.getHeader(CrossOriginFilter.ACCESS_CONTROL_REQUEST_HEADERS)) Mockito.when(
mockReq.getHeader(CrossOriginFilter.ACCESS_CONTROL_REQUEST_HEADERS))
.thenReturn("X-Requested-With"); .thenReturn("X-Requested-With");
// Objects to verify interactions based on request // Objects to verify interactions based on request
HttpServletResponse mockRes = mock(HttpServletResponse.class); HttpServletResponse mockRes = Mockito.mock(HttpServletResponse.class);
FilterChain mockChain = mock(FilterChain.class); FilterChain mockChain = Mockito.mock(FilterChain.class);
// Object under test // Object under test
CrossOriginFilter filter = new CrossOriginFilter(); CrossOriginFilter filter = new CrossOriginFilter();
filter.init(filterConfig); filter.init(filterConfig);
filter.doFilter(mockReq, mockRes, mockChain); filter.doFilter(mockReq, mockRes, mockChain);
verify(mockRes).setHeader(CrossOriginFilter.ACCESS_CONTROL_ALLOW_ORIGIN, Mockito.verify(mockRes).setHeader(CrossOriginFilter.ACCESS_CONTROL_ALLOW_ORIGIN,
"example.com"); "example.com");
verify(mockRes).setHeader( Mockito.verify(mockRes).setHeader(
CrossOriginFilter.ACCESS_CONTROL_ALLOW_CREDENTIALS, CrossOriginFilter.ACCESS_CONTROL_ALLOW_CREDENTIALS,
Boolean.TRUE.toString()); Boolean.TRUE.toString());
verify(mockRes).setHeader(CrossOriginFilter.ACCESS_CONTROL_ALLOW_METHODS, Mockito.verify(mockRes).setHeader(CrossOriginFilter.ACCESS_CONTROL_ALLOW_METHODS,
filter.getAllowedMethodsHeader()); filter.getAllowedMethodsHeader());
verify(mockRes).setHeader(CrossOriginFilter.ACCESS_CONTROL_ALLOW_HEADERS, Mockito.verify(mockRes).setHeader(CrossOriginFilter.ACCESS_CONTROL_ALLOW_HEADERS,
filter.getAllowedHeadersHeader()); filter.getAllowedHeadersHeader());
verify(mockChain).doFilter(mockReq, mockRes); Mockito.verify(mockChain).doFilter(mockReq, mockRes);
} }
@Test @Test

View File

@ -939,6 +939,8 @@ Release 2.7.2 - UNRELEASED
IMPROVEMENTS IMPROVEMENTS
YARN-4009. CORS support for ResourceManager REST API. ( Varun Vasudev via jeagles)
YARN-3170. YARN architecture document needs updating. (Brahma Reddy Battula YARN-3170. YARN architecture document needs updating. (Brahma Reddy Battula
via ozawa) via ozawa)

View File

@ -337,6 +337,11 @@ public class YarnConfiguration extends Configuration {
public static final boolean DEFAULT_RM_WEBAPP_DELEGATION_TOKEN_AUTH_FILTER = public static final boolean DEFAULT_RM_WEBAPP_DELEGATION_TOKEN_AUTH_FILTER =
true; true;
/** Enable cross origin (CORS) support. **/
public static final String RM_WEBAPP_ENABLE_CORS_FILTER =
RM_PREFIX + "webapp.cross-origin.enabled";
public static final boolean DEFAULT_RM_WEBAPP_ENABLE_CORS_FILTER = false;
/** How long to wait until a container is considered dead.*/ /** How long to wait until a container is considered dead.*/
public static final String RM_CONTAINER_ALLOC_EXPIRY_INTERVAL_MS = public static final String RM_CONTAINER_ALLOC_EXPIRY_INTERVAL_MS =
RM_PREFIX + "rm.container-allocation.expiry-interval-ms"; RM_PREFIX + "rm.container-allocation.expiry-interval-ms";
@ -974,6 +979,11 @@ public class YarnConfiguration extends Configuration {
public static final String DEFAULT_NM_WEBAPP_HTTPS_ADDRESS = "0.0.0.0:" public static final String DEFAULT_NM_WEBAPP_HTTPS_ADDRESS = "0.0.0.0:"
+ DEFAULT_NM_WEBAPP_HTTPS_PORT; + DEFAULT_NM_WEBAPP_HTTPS_PORT;
/** Enable/disable CORS filter. */
public static final String NM_WEBAPP_ENABLE_CORS_FILTER =
NM_PREFIX + "webapp.cross-origin.enabled";
public static final boolean DEFAULT_NM_WEBAPP_ENABLE_CORS_FILTER = false;
/** How often to monitor resource in a node.*/ /** How often to monitor resource in a node.*/
public static final String NM_RESOURCE_MON_INTERVAL_MS = public static final String NM_RESOURCE_MON_INTERVAL_MS =
NM_PREFIX + "resource-monitor.interval-ms"; NM_PREFIX + "resource-monitor.interval-ms";

View File

@ -254,6 +254,14 @@
<value>true</value> <value>true</value>
</property> </property>
<property>
<description>Flag to enable cross-origin (CORS) support in the RM. This flag
requires the CORS filter initializer to be added to the filter initializers
list in core-site.xml.</description>
<name>yarn.resourcemanager.webapp.cross-origin.enabled</name>
<value>false</value>
</property>
<property> <property>
<description>How long to wait until a node manager is considered dead.</description> <description>How long to wait until a node manager is considered dead.</description>
<name>yarn.nm.liveness-monitor.expiry-interval-ms</name> <name>yarn.nm.liveness-monitor.expiry-interval-ms</name>
@ -2317,6 +2325,14 @@
<value>false</value> <value>false</value>
</property> </property>
<property>
<description>Flag to enable cross-origin (CORS) support in the NM. This flag
requires the CORS filter initializer to be added to the filter initializers
list in core-site.xml.</description>
<name>yarn.nodemanager.webapp.cross-origin.enabled</name>
<value>false</value>
</property>
<property> <property>
<description> <description>
Defines maximum application priority in a cluster. Defines maximum application priority in a cluster.

View File

@ -30,6 +30,7 @@ import org.apache.hadoop.http.HttpServer2;
import org.apache.hadoop.metrics2.lib.DefaultMetricsSystem; import org.apache.hadoop.metrics2.lib.DefaultMetricsSystem;
import org.apache.hadoop.metrics2.source.JvmMetrics; import org.apache.hadoop.metrics2.source.JvmMetrics;
import org.apache.hadoop.security.AuthenticationFilterInitializer; import org.apache.hadoop.security.AuthenticationFilterInitializer;
import org.apache.hadoop.security.HttpCrossOriginFilterInitializer;
import org.apache.hadoop.security.SecurityUtil; import org.apache.hadoop.security.SecurityUtil;
import org.apache.hadoop.service.CompositeService; import org.apache.hadoop.service.CompositeService;
import org.apache.hadoop.service.Service; import org.apache.hadoop.service.Service;
@ -252,10 +253,17 @@ public class ApplicationHistoryServer extends CompositeService {
if(conf.getBoolean(YarnConfiguration if(conf.getBoolean(YarnConfiguration
.TIMELINE_SERVICE_HTTP_CROSS_ORIGIN_ENABLED, YarnConfiguration .TIMELINE_SERVICE_HTTP_CROSS_ORIGIN_ENABLED, YarnConfiguration
.TIMELINE_SERVICE_HTTP_CROSS_ORIGIN_ENABLED_DEFAULT)) { .TIMELINE_SERVICE_HTTP_CROSS_ORIGIN_ENABLED_DEFAULT)) {
if (initializers.length() != 0) { if (initializers.contains(HttpCrossOriginFilterInitializer.class.getName())) {
initializers += ","; initializers =
initializers.replaceAll(HttpCrossOriginFilterInitializer.class.getName(),
CrossOriginFilterInitializer.class.getName());
}
else {
if (initializers.length() != 0) {
initializers += ",";
}
initializers += CrossOriginFilterInitializer.class.getName();
} }
initializers += CrossOriginFilterInitializer.class.getName();
modifiedInitializers = true; modifiedInitializers = true;
} }
} }

View File

@ -18,35 +18,35 @@
package org.apache.hadoop.yarn.server.timeline.webapp; package org.apache.hadoop.yarn.server.timeline.webapp;
import java.util.HashMap;
import java.util.Map;
import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.http.FilterContainer; import org.apache.hadoop.http.FilterContainer;
import org.apache.hadoop.http.FilterInitializer; import org.apache.hadoop.security.HttpCrossOriginFilterInitializer;
import org.apache.hadoop.security.http.CrossOriginFilter;
public class CrossOriginFilterInitializer extends FilterInitializer { import java.util.Map;
public class CrossOriginFilterInitializer extends HttpCrossOriginFilterInitializer {
public static final String PREFIX = public static final String PREFIX =
"yarn.timeline-service.http-cross-origin."; "yarn.timeline-service.http-cross-origin.";
@Override
protected String getPrefix() {
return PREFIX;
}
@Override @Override
public void initFilter(FilterContainer container, Configuration conf) { public void initFilter(FilterContainer container, Configuration conf) {
container.addGlobalFilter("Cross Origin Filter", // setup the filter
CrossOriginFilter.class.getName(), getFilterParameters(conf)); // use the keys with "yarn.timeline-service.http-cross-origin" prefix to
} // override the ones with the "hadoop.http.cross-origin" prefix.
static Map<String, String> getFilterParameters(Configuration conf) { Map<String, String> filterParameters =
Map<String, String> filterParams = getFilterParameters(conf, HttpCrossOriginFilterInitializer.PREFIX);
new HashMap<String, String>(); filterParameters.putAll(getFilterParameters(conf, getPrefix()));
for (Map.Entry<String, String> entry : conf.getValByRegex(PREFIX)
.entrySet()) { container.addGlobalFilter("Cross Origin Filter",
String name = entry.getKey(); CrossOriginFilter.class.getName(), filterParameters);
String value = entry.getValue();
name = name.substring(PREFIX.length());
filterParams.put(name, value);
}
return filterParams;
} }
} }

View File

@ -81,6 +81,11 @@
<artifactId>junit</artifactId> <artifactId>junit</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.apache.zookeeper</groupId> <groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId> <artifactId>zookeeper</artifactId>

View File

@ -22,6 +22,7 @@ import static org.apache.hadoop.yarn.util.StringHelper.pajoin;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.security.HttpCrossOriginFilterInitializer;
import org.apache.hadoop.service.AbstractService; import org.apache.hadoop.service.AbstractService;
import org.apache.hadoop.yarn.conf.YarnConfiguration; import org.apache.hadoop.yarn.conf.YarnConfiguration;
import org.apache.hadoop.yarn.exceptions.YarnRuntimeException; import org.apache.hadoop.yarn.exceptions.YarnRuntimeException;
@ -59,7 +60,14 @@ public class WebServer extends AbstractService {
String bindAddress = WebAppUtils.getWebAppBindURL(getConfig(), String bindAddress = WebAppUtils.getWebAppBindURL(getConfig(),
YarnConfiguration.NM_BIND_HOST, YarnConfiguration.NM_BIND_HOST,
WebAppUtils.getNMWebAppURLWithoutScheme(getConfig())); WebAppUtils.getNMWebAppURLWithoutScheme(getConfig()));
boolean enableCors = getConfig()
.getBoolean(YarnConfiguration.NM_WEBAPP_ENABLE_CORS_FILTER,
YarnConfiguration.DEFAULT_NM_WEBAPP_ENABLE_CORS_FILTER);
if (enableCors) {
getConfig().setBoolean(HttpCrossOriginFilterInitializer.PREFIX
+ HttpCrossOriginFilterInitializer.ENABLED_SUFFIX, true);
}
LOG.info("Instantiating NMWebApp at " + bindAddress); LOG.info("Instantiating NMWebApp at " + bindAddress);
try { try {
this.webApp = this.webApp =

View File

@ -28,10 +28,7 @@ import org.apache.hadoop.ha.HAServiceProtocol.HAServiceState;
import org.apache.hadoop.http.lib.StaticUserWebFilter; import org.apache.hadoop.http.lib.StaticUserWebFilter;
import org.apache.hadoop.metrics2.lib.DefaultMetricsSystem; import org.apache.hadoop.metrics2.lib.DefaultMetricsSystem;
import org.apache.hadoop.metrics2.source.JvmMetrics; import org.apache.hadoop.metrics2.source.JvmMetrics;
import org.apache.hadoop.security.AuthenticationFilterInitializer; import org.apache.hadoop.security.*;
import org.apache.hadoop.security.Groups;
import org.apache.hadoop.security.SecurityUtil;
import org.apache.hadoop.security.UserGroupInformation;
import org.apache.hadoop.security.authentication.server.KerberosAuthenticationHandler; import org.apache.hadoop.security.authentication.server.KerberosAuthenticationHandler;
import org.apache.hadoop.security.authorize.ProxyUsers; import org.apache.hadoop.security.authorize.ProxyUsers;
import org.apache.hadoop.service.AbstractService; import org.apache.hadoop.service.AbstractService;
@ -866,6 +863,9 @@ public class ResourceManager extends CompositeService implements Recoverable {
// 4. hadoop.http.filter.initializers container AuthenticationFilterInitializer // 4. hadoop.http.filter.initializers container AuthenticationFilterInitializer
Configuration conf = getConfig(); Configuration conf = getConfig();
boolean enableCorsFilter =
conf.getBoolean(YarnConfiguration.RM_WEBAPP_ENABLE_CORS_FILTER,
YarnConfiguration.DEFAULT_RM_WEBAPP_ENABLE_CORS_FILTER);
boolean useYarnAuthenticationFilter = boolean useYarnAuthenticationFilter =
conf.getBoolean( conf.getBoolean(
YarnConfiguration.RM_WEBAPP_DELEGATION_TOKEN_AUTH_FILTER, YarnConfiguration.RM_WEBAPP_DELEGATION_TOKEN_AUTH_FILTER,
@ -877,6 +877,12 @@ public class ResourceManager extends CompositeService implements Recoverable {
Class<?>[] initializersClasses = Class<?>[] initializersClasses =
conf.getClasses(filterInitializerConfKey); conf.getClasses(filterInitializerConfKey);
// setup CORS
if (enableCorsFilter) {
conf.setBoolean(HttpCrossOriginFilterInitializer.PREFIX
+ HttpCrossOriginFilterInitializer.ENABLED_SUFFIX, true);
}
boolean hasHadoopAuthFilterInitializer = false; boolean hasHadoopAuthFilterInitializer = false;
boolean hasRMAuthFilterInitializer = false; boolean hasRMAuthFilterInitializer = false;
if (initializersClasses != null) { if (initializersClasses != null) {

View File

@ -16,6 +16,7 @@ NodeManager REST API's
======================= =======================
* [Overview](#Overview) * [Overview](#Overview)
* [Enabling CORS support](#Enabling_CORS_support)
* [NodeManager Information API](#NodeManager_Information_API) * [NodeManager Information API](#NodeManager_Information_API)
* [Applications API](#Applications_API) * [Applications API](#Applications_API)
* [Application API](#Application_API) * [Application API](#Application_API)
@ -27,6 +28,13 @@ Overview
The NodeManager REST API's allow the user to get status on the node and information about applications and containers running on that node. The NodeManager REST API's allow the user to get status on the node and information about applications and containers running on that node.
Enabling CORS support
---------------------
To enable cross-origin support (CORS) for the NM only(without enabling it for the RM), please set the following configuration parameters:
In core-site.xml, add org.apache.hadoop.security.HttpCrossOriginFilterInitializer to hadoop.http.filter.initializers.
In yarn-site.xml, set yarn.nodemanager.webapp.cross-origin.enabled to true.
NodeManager Information API NodeManager Information API
--------------------------- ---------------------------
@ -540,4 +548,4 @@ Response Body:
<containerLogsLink>http://host.domain.com:8042/node/containerlogs/container_1326121700862_0007_01_000001/user1</containerLogsLink> <containerLogsLink>http://host.domain.com:8042/node/containerlogs/container_1326121700862_0007_01_000001/user1</containerLogsLink>
<nodeId>host.domain.com:8041</nodeId> <nodeId>host.domain.com:8041</nodeId>
</container> </container>
``` ```

View File

@ -16,6 +16,7 @@ ResourceManager REST API's.
=========================== ===========================
* [Overview](#Overview) * [Overview](#Overview)
* [Enabling CORS support](#Enabling_CORS_support)
* [Cluster Information API](#Cluster_Information_API) * [Cluster Information API](#Cluster_Information_API)
* [Cluster Metrics API](#Cluster_Metrics_API) * [Cluster Metrics API](#Cluster_Metrics_API)
* [Cluster Scheduler API](#Cluster_Scheduler_API) * [Cluster Scheduler API](#Cluster_Scheduler_API)
@ -38,6 +39,13 @@ Overview
The ResourceManager REST API's allow the user to get information about the cluster - status on the cluster, metrics on the cluster, scheduler information, information about nodes in the cluster, and information about applications on the cluster. The ResourceManager REST API's allow the user to get information about the cluster - status on the cluster, metrics on the cluster, scheduler information, information about nodes in the cluster, and information about applications on the cluster.
Enabling CORS support
---------------------
To enable cross-origin support (CORS) for the RM only(without enabling it for the NM), please set the following configuration parameters:
In core-site.xml, add org.apache.hadoop.security.HttpCrossOriginFilterInitializer to hadoop.http.filter.initializers.
In yarn-site.xml, set yarn.resourcemanager.webapp.cross-origin.enabled to true.
Cluster Information API Cluster Information API
----------------------- -----------------------