YARN-10833. Set the X-FRAME-OPTIONS header for the default contexts. (#3203)
* YARN-10833. Set the X-FRAME-OPTIONS header for the default contexts. * fixup: YARN-10833. Set the X-FRAME-OPTIONS header for the default contexts. Co-authored-by: Benjamin Teke <bteke@cloudera.com>
This commit is contained in:
parent
63dfd84947
commit
05b6a1a06a
|
@ -41,7 +41,6 @@ import org.apache.hadoop.http.HttpServer2;
|
|||
import org.apache.hadoop.security.UserGroupInformation;
|
||||
import org.apache.hadoop.security.authorize.AccessControlList;
|
||||
import org.apache.hadoop.security.http.RestCsrfPreventionFilter;
|
||||
import org.apache.hadoop.security.http.XFrameOptionsFilter;
|
||||
import org.apache.hadoop.yarn.api.ApplicationClientProtocol;
|
||||
import org.apache.hadoop.yarn.conf.YarnConfiguration;
|
||||
import org.apache.hadoop.yarn.webapp.util.WebAppUtils;
|
||||
|
@ -325,6 +324,19 @@ public class WebApps {
|
|||
YarnConfiguration.YARN_ADMIN_ACL,
|
||||
YarnConfiguration.DEFAULT_YARN_ADMIN_ACL)))
|
||||
.setPathSpec(pathList.toArray(new String[0]));
|
||||
|
||||
// Set the X-FRAME-OPTIONS header, use the HttpServer2 default if
|
||||
// the header value is not specified
|
||||
Map<String, String> xfsParameters =
|
||||
getConfigParameters(xfsConfigPrefix);
|
||||
|
||||
if (xfsParameters != null) {
|
||||
String xFrameOptions = xfsParameters.get("xframe-options");
|
||||
if (xFrameOptions != null) {
|
||||
builder.configureXFrame(hasXFSEnabled())
|
||||
.setXFrameOption(xFrameOptions);
|
||||
}
|
||||
}
|
||||
// Get port ranges from config.
|
||||
IntegerRanges ranges = null;
|
||||
if (portRangeConfigKey != null) {
|
||||
|
@ -395,15 +407,6 @@ public class WebApps {
|
|||
new String[] {"/*"});
|
||||
}
|
||||
|
||||
params = getConfigParameters(xfsConfigPrefix);
|
||||
|
||||
if (hasXFSEnabled()) {
|
||||
String xfsClassName = XFrameOptionsFilter.class.getName();
|
||||
HttpServer2.defineFilter(server.getWebAppContext(), xfsClassName,
|
||||
xfsClassName, params,
|
||||
new String[] {"/*"});
|
||||
}
|
||||
|
||||
HttpServer2.defineFilter(server.getWebAppContext(), "guice",
|
||||
GuiceFilter.class.getName(), null, new String[] { "/*" });
|
||||
|
||||
|
@ -489,14 +492,6 @@ public class WebApps {
|
|||
HttpServer2.defineFilter(ui2Context, restCsrfClassName,
|
||||
restCsrfClassName, params, new String[]{"/*"});
|
||||
}
|
||||
|
||||
params = getConfigParameters(xfsConfigPrefix);
|
||||
|
||||
if (hasXFSEnabled()) {
|
||||
String xfsClassName = XFrameOptionsFilter.class.getName();
|
||||
HttpServer2.defineFilter(ui2Context, xfsClassName, xfsClassName, params,
|
||||
new String[]{"/*"});
|
||||
}
|
||||
}
|
||||
|
||||
private String inferHostClass() {
|
||||
|
|
|
@ -18,126 +18,97 @@
|
|||
|
||||
package org.apache.hadoop.yarn.webapp;
|
||||
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.servlet.ServletModule;
|
||||
import com.sun.jersey.api.client.ClientResponse;
|
||||
import com.sun.jersey.api.client.WebResource;
|
||||
import com.sun.jersey.guice.spi.container.servlet.GuiceContainer;
|
||||
import com.sun.jersey.test.framework.WebAppDescriptor;
|
||||
import org.apache.hadoop.conf.Configuration;
|
||||
import org.apache.hadoop.security.http.XFrameOptionsFilter;
|
||||
import org.apache.hadoop.http.HttpServer2;
|
||||
import org.apache.hadoop.yarn.conf.YarnConfiguration;
|
||||
import org.apache.hadoop.yarn.server.resourcemanager.MockRM;
|
||||
import org.apache.hadoop.yarn.server.resourcemanager.ResourceManager;
|
||||
import org.apache.hadoop.yarn.server.resourcemanager.scheduler.ResourceScheduler;
|
||||
import org.apache.hadoop.yarn.server.resourcemanager.scheduler.fifo.FifoScheduler;
|
||||
import org.apache.hadoop.yarn.server.resourcemanager.webapp.JAXBContextResolver;
|
||||
import org.apache.hadoop.yarn.server.resourcemanager.webapp.RMWebServices;
|
||||
import org.junit.Before;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import java.io.IOException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* Used TestRMWebServices as an example of web invocations of RM and added
|
||||
* test for XFS Filter.
|
||||
*/
|
||||
public class TestRMWithXFSFilter extends JerseyTestBase {
|
||||
|
||||
public class TestRMWithXFSFilter {
|
||||
private static MockRM rm;
|
||||
|
||||
@Before
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
}
|
||||
|
||||
public TestRMWithXFSFilter() {
|
||||
super(new WebAppDescriptor.Builder(
|
||||
"org.apache.hadoop.yarn.server.resourcemanager.webapp")
|
||||
.contextListenerClass(GuiceServletConfig.class)
|
||||
.filterClass(com.google.inject.servlet.GuiceFilter.class)
|
||||
.contextPath("jersey-guice-filter").servletPath("/").build());
|
||||
private void createMockRm(final Boolean xfsEnabled,
|
||||
final String xfsHeaderValue) {
|
||||
Configuration conf = new Configuration();
|
||||
conf.setClass(YarnConfiguration.RM_SCHEDULER, FifoScheduler.class,
|
||||
ResourceScheduler.class);
|
||||
conf.setBoolean("mockrm.webapp.enabled", true);
|
||||
if (xfsEnabled != null) {
|
||||
conf.setBoolean(YarnConfiguration.YARN_XFS_ENABLED, xfsEnabled);
|
||||
}
|
||||
if (xfsHeaderValue != null) {
|
||||
conf.setStrings(YarnConfiguration.RM_XFS_OPTIONS, xfsHeaderValue);
|
||||
}
|
||||
rm = new MockRM(conf);
|
||||
rm.start();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultBehavior() throws Exception {
|
||||
createInjector();
|
||||
public void testXFrameOptionsDefaultBehaviour() throws Exception {
|
||||
createMockRm(null, null);
|
||||
|
||||
WebResource r = resource();
|
||||
ClientResponse response = r.path("ws").path("v1").path("cluster")
|
||||
.path("info").accept("application/xml")
|
||||
.get(ClientResponse.class);
|
||||
assertEquals("Should have received DENY x-frame options header",
|
||||
"DENY",
|
||||
response.getHeaders().get(XFrameOptionsFilter.X_FRAME_OPTIONS).get(0));
|
||||
}
|
||||
|
||||
protected void createInjector(String headerValue) {
|
||||
createInjector(headerValue, false);
|
||||
}
|
||||
|
||||
|
||||
protected void createInjector() {
|
||||
createInjector(null, false);
|
||||
}
|
||||
|
||||
protected void createInjector(final String headerValue,
|
||||
final boolean explicitlyDisabled) {
|
||||
GuiceServletConfig.setInjector(Guice.createInjector(new ServletModule() {
|
||||
@Override
|
||||
protected void configureServlets() {
|
||||
bind(JAXBContextResolver.class);
|
||||
bind(RMWebServices.class);
|
||||
bind(GenericExceptionHandler.class);
|
||||
Configuration conf = new Configuration();
|
||||
conf.setClass(YarnConfiguration.RM_SCHEDULER, FifoScheduler.class,
|
||||
ResourceScheduler.class);
|
||||
rm = new MockRM(conf);
|
||||
bind(ResourceManager.class).toInstance(rm);
|
||||
serve("/*").with(GuiceContainer.class);
|
||||
XFrameOptionsFilter xfsFilter = new XFrameOptionsFilter();
|
||||
Map<String, String> initParams = new HashMap<>();
|
||||
if (headerValue != null) {
|
||||
initParams.put(XFrameOptionsFilter.CUSTOM_HEADER_PARAM, headerValue);
|
||||
}
|
||||
if (explicitlyDisabled) {
|
||||
initParams.put(
|
||||
"xframe-options-enabled", "false");
|
||||
}
|
||||
|
||||
filter("/*").through(xfsFilter, initParams);
|
||||
}
|
||||
}));
|
||||
URL url = new URL("http://localhost:8088/ws/v1/cluster/info");
|
||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||
String xfoHeader = conn.getHeaderField("X-FRAME-OPTIONS");
|
||||
Assert.assertTrue(xfoHeader.endsWith(HttpServer2.XFrameOption
|
||||
.SAMEORIGIN.toString()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSameOrigin() throws Exception {
|
||||
createInjector("SAMEORIGIN");
|
||||
public void testXFrameOptionsExplicitlyEnabled() throws Exception {
|
||||
createMockRm(true, HttpServer2.XFrameOption
|
||||
.SAMEORIGIN.toString());
|
||||
|
||||
WebResource r = resource();
|
||||
ClientResponse response = r.path("ws").path("v1").path("cluster")
|
||||
.path("info").accept("application/xml")
|
||||
.get(ClientResponse.class);
|
||||
assertEquals("Should have received SAMEORIGIN x-frame options header",
|
||||
"SAMEORIGIN",
|
||||
response.getHeaders().get(XFrameOptionsFilter.X_FRAME_OPTIONS).get(0));
|
||||
URL url = new URL("http://localhost:8088/ws/v1/cluster/info");
|
||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||
String xfoHeader = conn.getHeaderField("X-FRAME-OPTIONS");
|
||||
Assert.assertTrue(xfoHeader.endsWith(HttpServer2.XFrameOption
|
||||
.SAMEORIGIN.toString()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExplicitlyDisabled() throws Exception {
|
||||
createInjector(null, true);
|
||||
public void testXFrameOptionsEnabledDefaultApps() throws Exception {
|
||||
createMockRm(true, HttpServer2.XFrameOption
|
||||
.SAMEORIGIN.toString());
|
||||
|
||||
WebResource r = resource();
|
||||
ClientResponse response = r.path("ws").path("v1").path("cluster")
|
||||
.path("info").accept("application/xml")
|
||||
.get(ClientResponse.class);
|
||||
assertFalse("Should have not received x-frame options header",
|
||||
response.getHeaders().get(XFrameOptionsFilter.X_FRAME_OPTIONS) == null);
|
||||
URL url = new URL("http://localhost:8088/logs");
|
||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||
String xfoHeader = conn.getHeaderField("X-FRAME-OPTIONS");
|
||||
Assert.assertTrue(xfoHeader.endsWith(HttpServer2.XFrameOption
|
||||
.SAMEORIGIN.toString()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testXFrameOptionsDisabled() throws Exception {
|
||||
createMockRm(false, null);
|
||||
|
||||
URL url = new URL("http://localhost:8088/ws/v1/cluster/info");
|
||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||
String xfoHeader = conn.getHeaderField("X-FRAME-OPTIONS");
|
||||
Assert.assertNull("Unexpected X-FRAME-OPTION in header", xfoHeader);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testXFrameOptionsIllegalOption() {
|
||||
IllegalArgumentException e = Assert.assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> createMockRm(true, "otherValue"));
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws IOException {
|
||||
rm.close();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue