From 49a7d70f532086a8ba5a7c40873da656707d405a Mon Sep 17 00:00:00 2001 From: Jonathan Eagles Date: Fri, 23 Oct 2015 10:34:08 -0500 Subject: [PATCH] YARN-4009. CORS support for ResourceManager REST API. ( Varun Vasudev via jeagles) (cherry picked from commit f8adeb712dc834c27cec15c04a986f2f635aba83) --- .../HttpCrossOriginFilterInitializer.java | 74 +++++++++++++++ .../security/http}/CrossOriginFilter.java | 4 +- .../src/main/resources/core-default.xml | 36 ++++++++ .../src/site/markdown/HttpAuthentication.md | 14 +++ .../TestHttpCrossOriginFilterInitializer.java | 13 +-- .../security/http}/TestCrossOriginFilter.java | 92 ++++++++++--------- hadoop-yarn-project/CHANGES.txt | 2 + .../hadoop/yarn/conf/YarnConfiguration.java | 12 ++- .../src/main/resources/yarn-default.xml | 16 ++++ .../ApplicationHistoryServer.java | 14 ++- .../webapp/CrossOriginFilterInitializer.java | 40 ++++---- .../hadoop-yarn-server-common/pom.xml | 5 + .../server/nodemanager/webapp/WebServer.java | 10 +- .../resourcemanager/ResourceManager.java | 14 ++- .../src/site/markdown/NodeManagerRest.md | 10 +- .../src/site/markdown/ResourceManagerRest.md | 8 ++ 16 files changed, 283 insertions(+), 81 deletions(-) create mode 100644 hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/HttpCrossOriginFilterInitializer.java rename {hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/src/main/java/org/apache/hadoop/yarn/server/timeline/webapp => hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/http}/CrossOriginFilter.java (98%) rename hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/src/test/java/org/apache/hadoop/yarn/server/timeline/webapp/TestCrossOriginFilterInitializer.java => hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/TestHttpCrossOriginFilterInitializer.java (81%) rename {hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/src/test/java/org/apache/hadoop/yarn/server/timeline/webapp => hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/http}/TestCrossOriginFilter.java (76%) diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/HttpCrossOriginFilterInitializer.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/HttpCrossOriginFilterInitializer.java new file mode 100644 index 00000000000..f9c18166648 --- /dev/null +++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/HttpCrossOriginFilterInitializer.java @@ -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 getFilterParameters(Configuration conf, + String prefix) { + Map filterParams = new HashMap(); + for (Map.Entry 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; + } +} diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/src/main/java/org/apache/hadoop/yarn/server/timeline/webapp/CrossOriginFilter.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/http/CrossOriginFilter.java similarity index 98% rename from hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/src/main/java/org/apache/hadoop/yarn/server/timeline/webapp/CrossOriginFilter.java rename to hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/http/CrossOriginFilter.java index 9edaefbdfe3..ea7876224bd 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/src/main/java/org/apache/hadoop/yarn/server/timeline/webapp/CrossOriginFilter.java +++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/http/CrossOriginFilter.java @@ -16,7 +16,7 @@ * limitations under the License. */ -package org.apache.hadoop.yarn.server.timeline.webapp; +package org.apache.hadoop.security.http; import java.io.IOException; import java.util.ArrayList; @@ -246,7 +246,7 @@ public class CrossOriginFilter implements Filter { if (accessControlRequestHeaders == null) { return true; } - String headers[] = accessControlRequestHeaders.trim().split("\\s*,\\s*"); + String[] headers = accessControlRequestHeaders.trim().split("\\s*,\\s*"); return allowedHeaders.containsAll(Arrays.asList(headers)); } diff --git a/hadoop-common-project/hadoop-common/src/main/resources/core-default.xml b/hadoop-common-project/hadoop-common/src/main/resources/core-default.xml index d9a90bac3e6..4ca15b93b5d 100644 --- a/hadoop-common-project/hadoop-common/src/main/resources/core-default.xml +++ b/hadoop-common-project/hadoop-common/src/main/resources/core-default.xml @@ -1311,6 +1311,42 @@ for ldap providers in the same way as above does. + + + Enable/disable the cross-origin (CORS) filter. + hadoop.http.cross-origin.enabled + false + + + + Comma separated list of origins that are allowed for web + services needing cross-origin (CORS) support. Wildcards (*) and patterns + allowed + hadoop.http.cross-origin.allowed-origins + * + + + + Comma separated list of methods that are allowed for web + services needing cross-origin (CORS) support. + hadoop.http.cross-origin.allowed-methods + GET,POST,HEAD + + + + Comma separated list of headers that are allowed for web + services needing cross-origin (CORS) support. + hadoop.http.cross-origin.allowed-headers + X-Requested-With,Content-Type,Accept,Origin + + + + The number of seconds a pre-flighted request can be cached + for web services needing cross-origin (CORS) support. + hadoop.http.cross-origin.max-age + 1800 + + dfs.ha.fencing.methods diff --git a/hadoop-common-project/hadoop-common/src/site/markdown/HttpAuthentication.md b/hadoop-common-project/hadoop-common/src/site/markdown/HttpAuthentication.md index e0a2693a33a..b3c43ce023f 100644 --- a/hadoop-common-project/hadoop-common/src/site/markdown/HttpAuthentication.md +++ b/hadoop-common-project/hadoop-common/src/site/markdown/HttpAuthentication.md @@ -56,3 +56,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.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 | diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/src/test/java/org/apache/hadoop/yarn/server/timeline/webapp/TestCrossOriginFilterInitializer.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/TestHttpCrossOriginFilterInitializer.java similarity index 81% rename from hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/src/test/java/org/apache/hadoop/yarn/server/timeline/webapp/TestCrossOriginFilterInitializer.java rename to hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/TestHttpCrossOriginFilterInitializer.java index cf26368acdf..3db6ef1b1e8 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/src/test/java/org/apache/hadoop/yarn/server/timeline/webapp/TestCrossOriginFilterInitializer.java +++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/TestHttpCrossOriginFilterInitializer.java @@ -16,7 +16,7 @@ * limitations under the License. */ -package org.apache.hadoop.yarn.server.timeline.webapp; +package org.apache.hadoop.security; import java.util.Map; @@ -25,21 +25,22 @@ import org.apache.hadoop.conf.Configuration; import org.junit.Assert; import org.junit.Test; -public class TestCrossOriginFilterInitializer { +public class TestHttpCrossOriginFilterInitializer { @Test public void testGetFilterParameters() { // Initialize configuration object Configuration conf = new Configuration(); - conf.set(CrossOriginFilterInitializer.PREFIX + "rootparam", "rootvalue"); - conf.set(CrossOriginFilterInitializer.PREFIX + "nested.param", + conf.set(HttpCrossOriginFilterInitializer.PREFIX + "rootparam", + "rootvalue"); + conf.set(HttpCrossOriginFilterInitializer.PREFIX + "nested.param", "nestedvalue"); conf.set("outofscopeparam", "outofscopevalue"); // call function under test - Map filterParameters = - CrossOriginFilterInitializer.getFilterParameters(conf); + Map filterParameters = HttpCrossOriginFilterInitializer + .getFilterParameters(conf, HttpCrossOriginFilterInitializer.PREFIX); // retrieve values String rootvalue = filterParameters.get("rootparam"); diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/src/test/java/org/apache/hadoop/yarn/server/timeline/webapp/TestCrossOriginFilter.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/http/TestCrossOriginFilter.java similarity index 76% rename from hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/src/test/java/org/apache/hadoop/yarn/server/timeline/webapp/TestCrossOriginFilter.java rename to hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/http/TestCrossOriginFilter.java index 5e093418d75..5c9b413e6e4 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/src/test/java/org/apache/hadoop/yarn/server/timeline/webapp/TestCrossOriginFilter.java +++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/http/TestCrossOriginFilter.java @@ -16,7 +16,7 @@ * limitations under the License. */ -package org.apache.hadoop.yarn.server.timeline.webapp; +package org.apache.hadoop.security.http; import java.io.IOException; import java.util.Collections; @@ -31,13 +31,13 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.apache.hadoop.security.http.CrossOriginFilter; import org.junit.Assert; 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.verify; -import static org.mockito.Mockito.verifyZeroInteractions; public class TestCrossOriginFilter { @@ -50,20 +50,20 @@ public class TestCrossOriginFilter { FilterConfig filterConfig = new FilterConfigTest(conf); // Origin is not specified for same origin requests - HttpServletRequest mockReq = mock(HttpServletRequest.class); - when(mockReq.getHeader(CrossOriginFilter.ORIGIN)).thenReturn(null); + HttpServletRequest mockReq = Mockito.mock(HttpServletRequest.class); + Mockito.when(mockReq.getHeader(CrossOriginFilter.ORIGIN)).thenReturn(null); // Objects to verify interactions based on request - HttpServletResponse mockRes = mock(HttpServletResponse.class); - FilterChain mockChain = mock(FilterChain.class); + HttpServletResponse mockRes = Mockito.mock(HttpServletResponse.class); + FilterChain mockChain = Mockito.mock(FilterChain.class); // Object under test CrossOriginFilter filter = new CrossOriginFilter(); filter.init(filterConfig); filter.doFilter(mockReq, mockRes, mockChain); - verifyZeroInteractions(mockRes); - verify(mockChain).doFilter(mockReq, mockRes); + Mockito.verifyZeroInteractions(mockRes); + Mockito.verify(mockChain).doFilter(mockReq, mockRes); } @Test @@ -91,11 +91,12 @@ public class TestCrossOriginFilter { String encodedResponseSplitOrigin = CrossOriginFilter.encodeHeader(httpResponseSplitOrigin); Assert.assertEquals("Http response split origin should be protected against", - validOrigin, encodedResponseSplitOrigin); + validOrigin, encodedResponseSplitOrigin); // Test Origin List 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", validOriginList, encodedValidOriginList); } @@ -135,20 +136,20 @@ public class TestCrossOriginFilter { FilterConfig filterConfig = new FilterConfigTest(conf); // Origin is not specified for same origin requests - HttpServletRequest mockReq = mock(HttpServletRequest.class); - when(mockReq.getHeader(CrossOriginFilter.ORIGIN)).thenReturn("example.org"); + HttpServletRequest mockReq = Mockito.mock(HttpServletRequest.class); + Mockito.when(mockReq.getHeader(CrossOriginFilter.ORIGIN)).thenReturn("example.org"); // Objects to verify interactions based on request - HttpServletResponse mockRes = mock(HttpServletResponse.class); - FilterChain mockChain = mock(FilterChain.class); + HttpServletResponse mockRes = Mockito.mock(HttpServletResponse.class); + FilterChain mockChain = Mockito.mock(FilterChain.class); // Object under test CrossOriginFilter filter = new CrossOriginFilter(); filter.init(filterConfig); filter.doFilter(mockReq, mockRes, mockChain); - verifyZeroInteractions(mockRes); - verify(mockChain).doFilter(mockReq, mockRes); + Mockito.verifyZeroInteractions(mockRes); + Mockito.verify(mockChain).doFilter(mockReq, mockRes); } @Test @@ -160,22 +161,23 @@ public class TestCrossOriginFilter { FilterConfig filterConfig = new FilterConfigTest(conf); // Origin is not specified for same origin requests - HttpServletRequest mockReq = mock(HttpServletRequest.class); - when(mockReq.getHeader(CrossOriginFilter.ORIGIN)).thenReturn("example.com"); - when(mockReq.getHeader(CrossOriginFilter.ACCESS_CONTROL_REQUEST_METHOD)) + HttpServletRequest mockReq = Mockito.mock(HttpServletRequest.class); + Mockito.when(mockReq.getHeader(CrossOriginFilter.ORIGIN)).thenReturn("example.com"); + Mockito.when( + mockReq.getHeader(CrossOriginFilter.ACCESS_CONTROL_REQUEST_METHOD)) .thenReturn("DISALLOWED_METHOD"); // Objects to verify interactions based on request - HttpServletResponse mockRes = mock(HttpServletResponse.class); - FilterChain mockChain = mock(FilterChain.class); + HttpServletResponse mockRes = Mockito.mock(HttpServletResponse.class); + FilterChain mockChain = Mockito.mock(FilterChain.class); // Object under test CrossOriginFilter filter = new CrossOriginFilter(); filter.init(filterConfig); filter.doFilter(mockReq, mockRes, mockChain); - verifyZeroInteractions(mockRes); - verify(mockChain).doFilter(mockReq, mockRes); + Mockito.verifyZeroInteractions(mockRes); + Mockito.verify(mockChain).doFilter(mockReq, mockRes); } @Test @@ -187,24 +189,26 @@ public class TestCrossOriginFilter { FilterConfig filterConfig = new FilterConfigTest(conf); // Origin is not specified for same origin requests - HttpServletRequest mockReq = mock(HttpServletRequest.class); - when(mockReq.getHeader(CrossOriginFilter.ORIGIN)).thenReturn("example.com"); - when(mockReq.getHeader(CrossOriginFilter.ACCESS_CONTROL_REQUEST_METHOD)) + HttpServletRequest mockReq = Mockito.mock(HttpServletRequest.class); + Mockito.when(mockReq.getHeader(CrossOriginFilter.ORIGIN)).thenReturn("example.com"); + Mockito.when( + mockReq.getHeader(CrossOriginFilter.ACCESS_CONTROL_REQUEST_METHOD)) .thenReturn("GET"); - when(mockReq.getHeader(CrossOriginFilter.ACCESS_CONTROL_REQUEST_HEADERS)) + Mockito.when( + mockReq.getHeader(CrossOriginFilter.ACCESS_CONTROL_REQUEST_HEADERS)) .thenReturn("Disallowed-Header"); // Objects to verify interactions based on request - HttpServletResponse mockRes = mock(HttpServletResponse.class); - FilterChain mockChain = mock(FilterChain.class); + HttpServletResponse mockRes = Mockito.mock(HttpServletResponse.class); + FilterChain mockChain = Mockito.mock(FilterChain.class); // Object under test CrossOriginFilter filter = new CrossOriginFilter(); filter.init(filterConfig); filter.doFilter(mockReq, mockRes, mockChain); - verifyZeroInteractions(mockRes); - verify(mockChain).doFilter(mockReq, mockRes); + Mockito.verifyZeroInteractions(mockRes); + Mockito.verify(mockChain).doFilter(mockReq, mockRes); } @Test @@ -216,32 +220,34 @@ public class TestCrossOriginFilter { FilterConfig filterConfig = new FilterConfigTest(conf); // Origin is not specified for same origin requests - HttpServletRequest mockReq = mock(HttpServletRequest.class); - when(mockReq.getHeader(CrossOriginFilter.ORIGIN)).thenReturn("example.com"); - when(mockReq.getHeader(CrossOriginFilter.ACCESS_CONTROL_REQUEST_METHOD)) + HttpServletRequest mockReq = Mockito.mock(HttpServletRequest.class); + Mockito.when(mockReq.getHeader(CrossOriginFilter.ORIGIN)).thenReturn("example.com"); + Mockito.when( + mockReq.getHeader(CrossOriginFilter.ACCESS_CONTROL_REQUEST_METHOD)) .thenReturn("GET"); - when(mockReq.getHeader(CrossOriginFilter.ACCESS_CONTROL_REQUEST_HEADERS)) + Mockito.when( + mockReq.getHeader(CrossOriginFilter.ACCESS_CONTROL_REQUEST_HEADERS)) .thenReturn("X-Requested-With"); // Objects to verify interactions based on request - HttpServletResponse mockRes = mock(HttpServletResponse.class); - FilterChain mockChain = mock(FilterChain.class); + HttpServletResponse mockRes = Mockito.mock(HttpServletResponse.class); + FilterChain mockChain = Mockito.mock(FilterChain.class); // Object under test CrossOriginFilter filter = new CrossOriginFilter(); filter.init(filterConfig); filter.doFilter(mockReq, mockRes, mockChain); - verify(mockRes).setHeader(CrossOriginFilter.ACCESS_CONTROL_ALLOW_ORIGIN, + Mockito.verify(mockRes).setHeader(CrossOriginFilter.ACCESS_CONTROL_ALLOW_ORIGIN, "example.com"); - verify(mockRes).setHeader( + Mockito.verify(mockRes).setHeader( CrossOriginFilter.ACCESS_CONTROL_ALLOW_CREDENTIALS, Boolean.TRUE.toString()); - verify(mockRes).setHeader(CrossOriginFilter.ACCESS_CONTROL_ALLOW_METHODS, + Mockito.verify(mockRes).setHeader(CrossOriginFilter.ACCESS_CONTROL_ALLOW_METHODS, filter.getAllowedMethodsHeader()); - verify(mockRes).setHeader(CrossOriginFilter.ACCESS_CONTROL_ALLOW_HEADERS, + Mockito.verify(mockRes).setHeader(CrossOriginFilter.ACCESS_CONTROL_ALLOW_HEADERS, filter.getAllowedHeadersHeader()); - verify(mockChain).doFilter(mockReq, mockRes); + Mockito.verify(mockChain).doFilter(mockReq, mockRes); } @Test diff --git a/hadoop-yarn-project/CHANGES.txt b/hadoop-yarn-project/CHANGES.txt index 05bc3d34972..251e6afa789 100644 --- a/hadoop-yarn-project/CHANGES.txt +++ b/hadoop-yarn-project/CHANGES.txt @@ -8,6 +8,8 @@ Release 2.7.2 - UNRELEASED IMPROVEMENTS + YARN-4009. CORS support for ResourceManager REST API. ( Varun Vasudev via jeagles) + YARN-3170. YARN architecture document needs updating. (Brahma Reddy Battula via ozawa) diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/conf/YarnConfiguration.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/conf/YarnConfiguration.java index 0908e653ab1..3f85642ef5a 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/conf/YarnConfiguration.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/conf/YarnConfiguration.java @@ -333,6 +333,11 @@ public class YarnConfiguration extends Configuration { public static final boolean DEFAULT_RM_WEBAPP_DELEGATION_TOKEN_AUTH_FILTER = 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.*/ public static final String RM_CONTAINER_ALLOC_EXPIRY_INTERVAL_MS = RM_PREFIX + "rm.container-allocation.expiry-interval-ms"; @@ -852,7 +857,12 @@ public class YarnConfiguration extends Configuration { public static final int DEFAULT_NM_WEBAPP_HTTPS_PORT = 8044; public static final String DEFAULT_NM_WEBAPP_HTTPS_ADDRESS = "0.0.0.0:" + 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 containers.*/ public final static String NM_CONTAINER_MON_INTERVAL_MS = NM_PREFIX + "container-monitor.interval-ms"; diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/resources/yarn-default.xml b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/resources/yarn-default.xml index db37deb08a9..eb72c9d99d8 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/resources/yarn-default.xml +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/resources/yarn-default.xml @@ -218,6 +218,14 @@ true + + 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. + yarn.resourcemanager.webapp.cross-origin.enabled + false + + How long to wait until a node manager is considered dead. yarn.nm.liveness-monitor.expiry-interval-ms @@ -1671,4 +1679,12 @@ yarn.nodemanager.log-aggregation.roll-monitoring-interval-seconds -1 + + + 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. + yarn.nodemanager.webapp.cross-origin.enabled + false + diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/src/main/java/org/apache/hadoop/yarn/server/applicationhistoryservice/ApplicationHistoryServer.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/src/main/java/org/apache/hadoop/yarn/server/applicationhistoryservice/ApplicationHistoryServer.java index ff0209dfd51..5777b1be163 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/src/main/java/org/apache/hadoop/yarn/server/applicationhistoryservice/ApplicationHistoryServer.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/src/main/java/org/apache/hadoop/yarn/server/applicationhistoryservice/ApplicationHistoryServer.java @@ -30,6 +30,7 @@ import org.apache.hadoop.http.HttpServer2; import org.apache.hadoop.metrics2.lib.DefaultMetricsSystem; import org.apache.hadoop.metrics2.source.JvmMetrics; import org.apache.hadoop.security.AuthenticationFilterInitializer; +import org.apache.hadoop.security.HttpCrossOriginFilterInitializer; import org.apache.hadoop.security.SecurityUtil; import org.apache.hadoop.service.CompositeService; import org.apache.hadoop.service.Service; @@ -242,10 +243,17 @@ public class ApplicationHistoryServer extends CompositeService { if(conf.getBoolean(YarnConfiguration .TIMELINE_SERVICE_HTTP_CROSS_ORIGIN_ENABLED, YarnConfiguration .TIMELINE_SERVICE_HTTP_CROSS_ORIGIN_ENABLED_DEFAULT)) { - if (initializers.length() != 0) { - initializers += ","; + if (initializers.contains(HttpCrossOriginFilterInitializer.class.getName())) { + 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; } } diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/src/main/java/org/apache/hadoop/yarn/server/timeline/webapp/CrossOriginFilterInitializer.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/src/main/java/org/apache/hadoop/yarn/server/timeline/webapp/CrossOriginFilterInitializer.java index 148cc631ad7..1e6a7ffe6dd 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/src/main/java/org/apache/hadoop/yarn/server/timeline/webapp/CrossOriginFilterInitializer.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-applicationhistoryservice/src/main/java/org/apache/hadoop/yarn/server/timeline/webapp/CrossOriginFilterInitializer.java @@ -18,35 +18,35 @@ 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.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 = "yarn.timeline-service.http-cross-origin."; + @Override + protected String getPrefix() { + return PREFIX; + } + @Override public void initFilter(FilterContainer container, Configuration conf) { - container.addGlobalFilter("Cross Origin Filter", - CrossOriginFilter.class.getName(), getFilterParameters(conf)); - } + // setup the filter + // use the keys with "yarn.timeline-service.http-cross-origin" prefix to + // override the ones with the "hadoop.http.cross-origin" prefix. - static Map getFilterParameters(Configuration conf) { - Map filterParams = - new HashMap(); - for (Map.Entry entry : conf.getValByRegex(PREFIX) - .entrySet()) { - String name = entry.getKey(); - String value = entry.getValue(); - name = name.substring(PREFIX.length()); - filterParams.put(name, value); - } - return filterParams; + Map filterParameters = + getFilterParameters(conf, HttpCrossOriginFilterInitializer.PREFIX); + filterParameters.putAll(getFilterParameters(conf, getPrefix())); + + container.addGlobalFilter("Cross Origin Filter", + CrossOriginFilter.class.getName(), filterParameters); } -} +} \ No newline at end of file diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-common/pom.xml b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-common/pom.xml index f0b3c19e7be..04072a833ba 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-common/pom.xml +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-common/pom.xml @@ -81,6 +81,11 @@ junit test + + org.mockito + mockito-all + test + org.apache.zookeeper zookeeper diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/webapp/WebServer.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/webapp/WebServer.java index ca2f239e223..a3c5da95f61 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/webapp/WebServer.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/java/org/apache/hadoop/yarn/server/nodemanager/webapp/WebServer.java @@ -23,6 +23,7 @@ import static org.apache.hadoop.yarn.util.StringHelper.pajoin; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.http.HttpConfig; +import org.apache.hadoop.security.HttpCrossOriginFilterInitializer; import org.apache.hadoop.service.AbstractService; import org.apache.hadoop.yarn.conf.YarnConfiguration; import org.apache.hadoop.yarn.exceptions.YarnRuntimeException; @@ -58,7 +59,14 @@ public class WebServer extends AbstractService { String bindAddress = WebAppUtils.getWebAppBindURL(getConfig(), YarnConfiguration.NM_BIND_HOST, 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); try { this.webApp = diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/ResourceManager.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/ResourceManager.java index 6c1e2daa5cf..9e590812dc3 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/ResourceManager.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/ResourceManager.java @@ -28,10 +28,7 @@ import org.apache.hadoop.ha.HAServiceProtocol.HAServiceState; import org.apache.hadoop.http.lib.StaticUserWebFilter; import org.apache.hadoop.metrics2.lib.DefaultMetricsSystem; import org.apache.hadoop.metrics2.source.JvmMetrics; -import org.apache.hadoop.security.AuthenticationFilterInitializer; -import org.apache.hadoop.security.Groups; -import org.apache.hadoop.security.SecurityUtil; -import org.apache.hadoop.security.UserGroupInformation; +import org.apache.hadoop.security.*; import org.apache.hadoop.security.authentication.server.KerberosAuthenticationHandler; import org.apache.hadoop.security.authorize.ProxyUsers; import org.apache.hadoop.service.AbstractService; @@ -841,6 +838,9 @@ public class ResourceManager extends CompositeService implements Recoverable { // 4. hadoop.http.filter.initializers container AuthenticationFilterInitializer Configuration conf = getConfig(); + boolean enableCorsFilter = + conf.getBoolean(YarnConfiguration.RM_WEBAPP_ENABLE_CORS_FILTER, + YarnConfiguration.DEFAULT_RM_WEBAPP_ENABLE_CORS_FILTER); boolean useYarnAuthenticationFilter = conf.getBoolean( YarnConfiguration.RM_WEBAPP_DELEGATION_TOKEN_AUTH_FILTER, @@ -852,6 +852,12 @@ public class ResourceManager extends CompositeService implements Recoverable { Class[] initializersClasses = conf.getClasses(filterInitializerConfKey); + // setup CORS + if (enableCorsFilter) { + conf.setBoolean(HttpCrossOriginFilterInitializer.PREFIX + + HttpCrossOriginFilterInitializer.ENABLED_SUFFIX, true); + } + boolean hasHadoopAuthFilterInitializer = false; boolean hasRMAuthFilterInitializer = false; if (initializersClasses != null) { diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-site/src/site/markdown/NodeManagerRest.md b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-site/src/site/markdown/NodeManagerRest.md index acafd28c34c..2613b6316c0 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-site/src/site/markdown/NodeManagerRest.md +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-site/src/site/markdown/NodeManagerRest.md @@ -16,6 +16,7 @@ NodeManager REST API's ======================= * [Overview](#Overview) +* [Enabling CORS support](#Enabling_CORS_support) * [NodeManager Information API](#NodeManager_Information_API) * [Applications API](#Applications_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. +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 --------------------------- @@ -540,4 +548,4 @@ Response Body: http://host.domain.com:8042/node/containerlogs/container_1326121700862_0007_01_000001/user1 host.domain.com:8041 -``` \ No newline at end of file +``` diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-site/src/site/markdown/ResourceManagerRest.md b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-site/src/site/markdown/ResourceManagerRest.md index b1591bb5ea6..7746dd48ef4 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-site/src/site/markdown/ResourceManagerRest.md +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-site/src/site/markdown/ResourceManagerRest.md @@ -16,6 +16,7 @@ ResourceManager REST API's. =========================== * [Overview](#Overview) +* [Enabling CORS support](#Enabling_CORS_support) * [Cluster Information API](#Cluster_Information_API) * [Cluster Metrics API](#Cluster_Metrics_API) * [Cluster Scheduler API](#Cluster_Scheduler_API) @@ -37,6 +38,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. +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 -----------------------