HADOOP-14908. CrossOriginFilter should trigger regex on more input (Johannes Alberti via aw)
This commit is contained in:
parent
4111e6c781
commit
4d5dd75b60
@ -37,6 +37,7 @@
|
|||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@ -66,6 +67,7 @@ public class CrossOriginFilter implements Filter {
|
|||||||
// Filter configuration
|
// Filter configuration
|
||||||
public static final String ALLOWED_ORIGINS = "allowed-origins";
|
public static final String ALLOWED_ORIGINS = "allowed-origins";
|
||||||
public static final String ALLOWED_ORIGINS_DEFAULT = "*";
|
public static final String ALLOWED_ORIGINS_DEFAULT = "*";
|
||||||
|
public static final String ALLOWED_ORIGINS_REGEX_PREFIX = "regex:";
|
||||||
public static final String ALLOWED_METHODS = "allowed-methods";
|
public static final String ALLOWED_METHODS = "allowed-methods";
|
||||||
public static final String ALLOWED_METHODS_DEFAULT = "GET,POST,HEAD";
|
public static final String ALLOWED_METHODS_DEFAULT = "GET,POST,HEAD";
|
||||||
public static final String ALLOWED_HEADERS = "allowed-headers";
|
public static final String ALLOWED_HEADERS = "allowed-headers";
|
||||||
@ -194,6 +196,12 @@ private void initializeAllowedOrigins(FilterConfig filterConfig) {
|
|||||||
allowAllOrigins = allowedOrigins.contains("*");
|
allowAllOrigins = allowedOrigins.contains("*");
|
||||||
LOG.info("Allowed Origins: " + StringUtils.join(allowedOrigins, ','));
|
LOG.info("Allowed Origins: " + StringUtils.join(allowedOrigins, ','));
|
||||||
LOG.info("Allow All Origins: " + allowAllOrigins);
|
LOG.info("Allow All Origins: " + allowAllOrigins);
|
||||||
|
List<String> discouragedAllowedOrigins = allowedOrigins.stream()
|
||||||
|
.filter(s -> s.length() > 1 && s.contains("*"))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
for (String discouragedAllowedOrigin : discouragedAllowedOrigins) {
|
||||||
|
LOG.warn("Allowed Origin pattern '" + discouragedAllowedOrigin + "' is discouraged, use the 'regex:' prefix and use a Java regular expression instead.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initializeMaxAge(FilterConfig filterConfig) {
|
private void initializeMaxAge(FilterConfig filterConfig) {
|
||||||
@ -228,13 +236,18 @@ boolean areOriginsAllowed(String originsList) {
|
|||||||
String[] origins = originsList.trim().split("\\s+");
|
String[] origins = originsList.trim().split("\\s+");
|
||||||
for (String origin : origins) {
|
for (String origin : origins) {
|
||||||
for (String allowedOrigin : allowedOrigins) {
|
for (String allowedOrigin : allowedOrigins) {
|
||||||
if (allowedOrigin.contains("*")) {
|
Pattern regexPattern = null;
|
||||||
|
if (allowedOrigin.startsWith(ALLOWED_ORIGINS_REGEX_PREFIX)) {
|
||||||
|
String regex = allowedOrigin.substring(ALLOWED_ORIGINS_REGEX_PREFIX.length());
|
||||||
|
regexPattern = Pattern.compile(regex);
|
||||||
|
} else if (allowedOrigin.contains("*")) {
|
||||||
String regex = allowedOrigin.replace(".", "\\.").replace("*", ".*");
|
String regex = allowedOrigin.replace(".", "\\.").replace("*", ".*");
|
||||||
Pattern p = Pattern.compile(regex);
|
regexPattern = Pattern.compile(regex);
|
||||||
Matcher m = p.matcher(origin);
|
|
||||||
if (m.matches()) {
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (regexPattern != null
|
||||||
|
&& regexPattern.matcher(origin).matches()) {
|
||||||
|
return true;
|
||||||
} else if (allowedOrigin.equals(origin)) {
|
} else if (allowedOrigin.equals(origin)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1861,9 +1861,15 @@
|
|||||||
<property>
|
<property>
|
||||||
<name>hadoop.http.cross-origin.allowed-origins</name>
|
<name>hadoop.http.cross-origin.allowed-origins</name>
|
||||||
<value>*</value>
|
<value>*</value>
|
||||||
<description>Comma separated list of origins that are allowed for web
|
<description>Comma separated list of origins that are allowed for web services
|
||||||
services needing cross-origin (CORS) support. Wildcards (*) and patterns
|
needing cross-origin (CORS) support. If a value in the list contains an
|
||||||
allowed</description>
|
asterix (*), a regex pattern, escaping any dots ('.' -> '\.') and replacing
|
||||||
|
the asterix such that it captures any characters ('*' -> '.*'), is generated.
|
||||||
|
Values prefixed with 'regex:' are interpreted directly as regular expressions,
|
||||||
|
e.g. use the expression 'regex:https?:\/\/foo\.bar:([0-9]+)?' to allow any
|
||||||
|
origin using the 'http' or 'https' protocol in the domain 'foo.bar' on any
|
||||||
|
port. The use of simple wildcards ('*') is discouraged, and only available for
|
||||||
|
backward compatibility.</description>
|
||||||
</property>
|
</property>
|
||||||
|
|
||||||
<property>
|
<property>
|
||||||
|
@ -60,7 +60,7 @@ Add org.apache.hadoop.security.HttpCrossOriginFilterInitializer to hadoop.http.f
|
|||||||
| Property | Default Value | Description |
|
| Property | Default Value | Description |
|
||||||
|:---------------------------------------- |:--------------------------------------------- |:------------------------------------------------------------------------------------- |
|
|:---------------------------------------- |:--------------------------------------------- |:------------------------------------------------------------------------------------- |
|
||||||
| hadoop.http.cross-origin.enabled | `false` | Enables cross origin support for all web-services |
|
| 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-origins | `*` | Comma separated list of origins that are allowed. Values prefixed with `regex:` are interpreted as regular expressions. Values containing wildcards (`*`) are possible as well, here a regular expression is generated, the use is discouraged and support is only available for backward compatibility. |
|
||||||
| hadoop.http.cross-origin.allowed-methods | `GET,POST,HEAD` | Comma separated list of methods that are 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.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 |
|
| hadoop.http.cross-origin.max-age | `1800` | Number of seconds a pre-flighted request can be cached |
|
||||||
|
@ -127,6 +127,85 @@ public void testPatternMatchingOrigins() throws ServletException, IOException {
|
|||||||
Assert.assertFalse(filter.areOriginsAllowed("foo.nomatch1.com foo.nomatch2.com"));
|
Assert.assertFalse(filter.areOriginsAllowed("foo.nomatch1.com foo.nomatch2.com"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRegexPatternMatchingOrigins() throws ServletException, IOException {
|
||||||
|
|
||||||
|
// Setup the configuration settings of the server
|
||||||
|
Map<String, String> conf = new HashMap<String, String>();
|
||||||
|
conf.put(CrossOriginFilter.ALLOWED_ORIGINS, "regex:.*[.]example[.]com");
|
||||||
|
FilterConfig filterConfig = new FilterConfigTest(conf);
|
||||||
|
|
||||||
|
// Object under test
|
||||||
|
CrossOriginFilter filter = new CrossOriginFilter();
|
||||||
|
filter.init(filterConfig);
|
||||||
|
|
||||||
|
// match multiple sub-domains
|
||||||
|
Assert.assertFalse(filter.areOriginsAllowed("example.com"));
|
||||||
|
Assert.assertFalse(filter.areOriginsAllowed("foo:example.com"));
|
||||||
|
Assert.assertTrue(filter.areOriginsAllowed("foo.example.com"));
|
||||||
|
Assert.assertTrue(filter.areOriginsAllowed("foo.bar.example.com"));
|
||||||
|
|
||||||
|
// First origin is allowed
|
||||||
|
Assert.assertTrue(filter.areOriginsAllowed("foo.example.com foo.nomatch.com"));
|
||||||
|
// Second origin is allowed
|
||||||
|
Assert.assertTrue(filter.areOriginsAllowed("foo.nomatch.com foo.example.com"));
|
||||||
|
// No origin in list is allowed
|
||||||
|
Assert.assertFalse(filter.areOriginsAllowed("foo.nomatch1.com foo.nomatch2.com"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testComplexRegexPatternMatchingOrigins() throws ServletException, IOException {
|
||||||
|
|
||||||
|
// Setup the configuration settings of the server
|
||||||
|
Map<String, String> conf = new HashMap<String, String>();
|
||||||
|
conf.put(CrossOriginFilter.ALLOWED_ORIGINS, "regex:https?:\\/\\/sub1[.]example[.]com(:[0-9]+)?");
|
||||||
|
FilterConfig filterConfig = new FilterConfigTest(conf);
|
||||||
|
|
||||||
|
// Object under test
|
||||||
|
CrossOriginFilter filter = new CrossOriginFilter();
|
||||||
|
filter.init(filterConfig);
|
||||||
|
|
||||||
|
Assert.assertTrue(filter.areOriginsAllowed("http://sub1.example.com"));
|
||||||
|
Assert.assertTrue(filter.areOriginsAllowed("https://sub1.example.com"));
|
||||||
|
Assert.assertTrue(filter.areOriginsAllowed("http://sub1.example.com:1234"));
|
||||||
|
Assert.assertTrue(filter.areOriginsAllowed("https://sub1.example.com:8080"));
|
||||||
|
|
||||||
|
// No origin in list is allowed
|
||||||
|
Assert.assertFalse(filter.areOriginsAllowed("foo.nomatch1.com foo.nomatch2.com"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMixedRegexPatternMatchingOrigins() throws ServletException, IOException {
|
||||||
|
|
||||||
|
// Setup the configuration settings of the server
|
||||||
|
Map<String, String> conf = new HashMap<String, String>();
|
||||||
|
conf.put(CrossOriginFilter.ALLOWED_ORIGINS, "regex:https?:\\/\\/sub1[.]example[.]com(:[0-9]+)?, "
|
||||||
|
+ "*.example2.com");
|
||||||
|
FilterConfig filterConfig = new FilterConfigTest(conf);
|
||||||
|
|
||||||
|
// Object under test
|
||||||
|
CrossOriginFilter filter = new CrossOriginFilter();
|
||||||
|
filter.init(filterConfig);
|
||||||
|
|
||||||
|
Assert.assertTrue(filter.areOriginsAllowed("http://sub1.example.com"));
|
||||||
|
Assert.assertTrue(filter.areOriginsAllowed("https://sub1.example.com"));
|
||||||
|
Assert.assertTrue(filter.areOriginsAllowed("http://sub1.example.com:1234"));
|
||||||
|
Assert.assertTrue(filter.areOriginsAllowed("https://sub1.example.com:8080"));
|
||||||
|
|
||||||
|
// match multiple sub-domains
|
||||||
|
Assert.assertFalse(filter.areOriginsAllowed("example2.com"));
|
||||||
|
Assert.assertFalse(filter.areOriginsAllowed("foo:example2.com"));
|
||||||
|
Assert.assertTrue(filter.areOriginsAllowed("foo.example2.com"));
|
||||||
|
Assert.assertTrue(filter.areOriginsAllowed("foo.bar.example2.com"));
|
||||||
|
|
||||||
|
// First origin is allowed
|
||||||
|
Assert.assertTrue(filter.areOriginsAllowed("foo.example2.com foo.nomatch.com"));
|
||||||
|
// Second origin is allowed
|
||||||
|
Assert.assertTrue(filter.areOriginsAllowed("foo.nomatch.com foo.example2.com"));
|
||||||
|
// No origin in list is allowed
|
||||||
|
Assert.assertFalse(filter.areOriginsAllowed("foo.nomatch1.com foo.nomatch2.com"));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDisallowedOrigin() throws ServletException, IOException {
|
public void testDisallowedOrigin() throws ServletException, IOException {
|
||||||
|
|
||||||
|
@ -156,7 +156,7 @@ and cluster operators.
|
|||||||
| `yarn.timeline-service.webapp.https.address` | The https address of the Timeline service web application. Defaults to `${yarn.timeline-service.hostname}:8190`. |
|
| `yarn.timeline-service.webapp.https.address` | The https address of the Timeline service web application. Defaults to `${yarn.timeline-service.hostname}:8190`. |
|
||||||
| `yarn.timeline-service.bind-host` | The actual address the server will bind to. If this optional address is set, the RPC and webapp servers will bind to this address and the port specified in `yarn.timeline-service.address` and `yarn.timeline-service.webapp.address`, respectively. This is most useful for making the service listen on all interfaces by setting to `0.0.0.0`. |
|
| `yarn.timeline-service.bind-host` | The actual address the server will bind to. If this optional address is set, the RPC and webapp servers will bind to this address and the port specified in `yarn.timeline-service.address` and `yarn.timeline-service.webapp.address`, respectively. This is most useful for making the service listen on all interfaces by setting to `0.0.0.0`. |
|
||||||
| `yarn.timeline-service.http-cross-origin.enabled` | Enables cross-origin support (CORS) for web services where cross-origin web response headers are needed. For example, javascript making a web services request to the timeline server. Defaults to `false`. |
|
| `yarn.timeline-service.http-cross-origin.enabled` | Enables cross-origin support (CORS) for web services where cross-origin web response headers are needed. For example, javascript making a web services request to the timeline server. Defaults to `false`. |
|
||||||
| `yarn.timeline-service.http-cross-origin.allowed-origins` | Comma separated list of origins that are allowed for web services needing cross-origin (CORS) support. Wildcards `(*)` and patterns allowed. Defaults to `*`. |
|
| `yarn.timeline-service.http-cross-origin.allowed-origins` | Comma separated list of origins that are allowed. Values prefixed with `regex:` are interpreted as regular expressions. Values containing wildcards (`*`) are possible as well, here a regular expression is generated, the use is discouraged and support is only available for backward compatibility. Defaults to `*`. |
|
||||||
| `yarn.timeline-service.http-cross-origin.allowed-methods` | Comma separated list of methods that are allowed for web services needing cross-origin (CORS) support. Defaults to `GET,POST,HEAD`. |
|
| `yarn.timeline-service.http-cross-origin.allowed-methods` | Comma separated list of methods that are allowed for web services needing cross-origin (CORS) support. Defaults to `GET,POST,HEAD`. |
|
||||||
| `yarn.timeline-service.http-cross-origin.allowed-headers` | Comma separated list of headers that are allowed for web services needing cross-origin (CORS) support. Defaults to `X-Requested-With,Content-Type,Accept,Origin`. |
|
| `yarn.timeline-service.http-cross-origin.allowed-headers` | Comma separated list of headers that are allowed for web services needing cross-origin (CORS) support. Defaults to `X-Requested-With,Content-Type,Accept,Origin`. |
|
||||||
| `yarn.timeline-service.http-cross-origin.max-age` | The number of seconds a pre-flighted request can be cached for web services needing cross-origin (CORS) support. Defaults to `1800`. |
|
| `yarn.timeline-service.http-cross-origin.max-age` | The number of seconds a pre-flighted request can be cached for web services needing cross-origin (CORS) support. Defaults to `1800`. |
|
||||||
|
Loading…
Reference in New Issue
Block a user