Add global zuul sensitiveHeaders option.

Route specific sensitiveHeaders override global. fixes gh-944
parent 4479a2b2
......@@ -1267,6 +1267,8 @@ route, e.g.
url: https://dowstream
----
Sensitive headers can also be set globally setting `zuul.sensitiveHeaders`. If `sensitiveHeaders` is set on a route, this will override the global `sensitiveHeaders` setting.
NOTE: this is the default value for `sensitiveHeaders`, so you don't
need to set it unless you want it to be different. N.B. this is new in
Spring Cloud Netflix 1.1 (in 1.0 the user had no control over headers
......
......@@ -115,6 +115,17 @@ public class ZuulProperties {
*/
private boolean removeSemicolonContent = true;
/**
* List of sensitive headers that are not passed to downstream requests. Defaults
* to a "safe" set of headers that commonly contain user credentials. It's OK to
* remove those from the list if the downstream service is part of the same system
* as the proxy, so they are sharing authentication data. If using a physical URL
* outside your own domain, then generally it would be a bad idea to leak user
* credentials.
*/
private Set<String> sensitiveHeaders = new LinkedHashSet<>(
Arrays.asList("Cookie", "Set-Cookie", "Authorization"));
public Set<String> getIgnoredHeaders() {
Set<String> ignoredHeaders = new LinkedHashSet<>(this.ignoredHeaders);
if (ClassUtils.isPresent(
......@@ -193,8 +204,7 @@ public class ZuulProperties {
* outside your own domain, then generally it would be a bad idea to leak user
* credentials.
*/
private Set<String> sensitiveHeaders = new LinkedHashSet<>(
Arrays.asList("Cookie", "Set-Cookie", "Authorization"));
private Set<String> sensitiveHeaders = new LinkedHashSet<>();
public ZuulRoute(String text) {
String location = null;
......
......@@ -19,6 +19,7 @@ package org.springframework.cloud.netflix.zuul.filters.pre;
import java.net.MalformedURLException;
import java.net.URL;
import org.springframework.cloud.netflix.zuul.filters.ProxyRequestHelper;
import org.springframework.cloud.netflix.zuul.filters.Route;
import org.springframework.cloud.netflix.zuul.filters.RouteLocator;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
......@@ -37,21 +38,18 @@ public class PreDecorationFilter extends ZuulFilter {
private RouteLocator routeLocator;
private boolean addProxyHeaders;
private String dispatcherServletPath;
private String zuulServletPath;
private ZuulProperties properties;
private UrlPathHelper urlPathHelper = new UrlPathHelper();
public PreDecorationFilter(RouteLocator routeLocator,
String dispatcherServletPath, ZuulProperties zuulProperties) {
String dispatcherServletPath, ZuulProperties properties) {
this.routeLocator = routeLocator;
this.addProxyHeaders = zuulProperties.isAddProxyHeaders();
this.urlPathHelper.setRemoveSemicolonContent(zuulProperties.isRemoveSemicolonContent());
this.properties = properties;
this.urlPathHelper.setRemoveSemicolonContent(properties.isRemoveSemicolonContent());
this.dispatcherServletPath = dispatcherServletPath;
this.zuulServletPath = zuulProperties.getServletPath();
}
@Override
......@@ -67,9 +65,8 @@ public class PreDecorationFilter extends ZuulFilter {
@Override
public boolean shouldFilter() {
RequestContext ctx = RequestContext.getCurrentContext();
return !ctx.containsKey("forward.to") // another filter has already forwarded
&& !ctx.containsKey("serviceId"); // another filter has already determined
// serviceId
return !ctx.containsKey("forward.to") // a filter has already forwarded
&& !ctx.containsKey("serviceId"); // a filter has already determined serviceId
}
@Override
......@@ -83,7 +80,11 @@ public class PreDecorationFilter extends ZuulFilter {
if (location != null) {
ctx.put("requestURI", route.getPath());
ctx.put("proxy", route.getId());
ctx.put("ignoredHeaders", route.getSensitiveHeaders());
if (route.getSensitiveHeaders().isEmpty()) {
ctx.put(ProxyRequestHelper.IGNORED_HEADERS, this.properties.getSensitiveHeaders());
} else {
ctx.put(ProxyRequestHelper.IGNORED_HEADERS, route.getSensitiveHeaders());
}
if (route.getRetryable() != null) {
ctx.put("retryable", route.getRetryable());
......@@ -105,7 +106,7 @@ public class PreDecorationFilter extends ZuulFilter {
ctx.setRouteHost(null);
ctx.addOriginResponseHeader("X-Zuul-ServiceId", location);
}
if (this.addProxyHeaders) {
if (this.properties.isAddProxyHeaders()) {
ctx.addZuulRequestHeader("X-Forwarded-Host",
ctx.getRequest().getServerName());
ctx.addZuulRequestHeader("X-Forwarded-Port",
......@@ -129,14 +130,14 @@ public class PreDecorationFilter extends ZuulFilter {
}
else {
log.warn("No route found for uri: " + requestURI);
String fallBackUri = requestURI;
String fallbackPrefix = dispatcherServletPath; //default fallback servlet is DispatcherServlet
if (RequestUtils.isZuulServletRequest()) {
//remove the Zuul servletPath from the requestUri
log.debug("zuulServletPath=" + zuulServletPath);
fallBackUri = fallBackUri.replaceFirst(zuulServletPath, "");
log.debug("zuulServletPath=" + this.properties.getServletPath());
fallBackUri = fallBackUri.replaceFirst(this.properties.getServletPath(), "");
log.debug("Replaced Zuul servlet path:" + fallBackUri);
} else {
//remove the DispatcherServlet servletPath from the requestUri
......
......@@ -16,8 +16,11 @@
package org.springframework.cloud.netflix.zuul.filters;
import java.util.Arrays;
import java.util.Collections;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties.ZuulRoute;
......@@ -29,7 +32,17 @@ import static org.junit.Assert.assertTrue;
*/
public class ZuulPropertiesTests {
private ZuulProperties zuul = new ZuulProperties();
private ZuulProperties zuul;
@Before
public void setup() {
this.zuul = new ZuulProperties();
}
@After
public void teardown() {
this.zuul = null;
}
@Test
public void defaultIgnoredHeaders() {
......@@ -46,17 +59,22 @@ public class ZuulPropertiesTests {
public void defaultSensitiveHeaders() {
ZuulRoute route = new ZuulRoute("foo");
this.zuul.getRoutes().put("foo", route);
assertTrue(this.zuul.getRoutes().get("foo").getSensitiveHeaders()
.contains("Cookie"));
assertTrue(this.zuul.getRoutes().get("foo").getSensitiveHeaders().isEmpty());
assertTrue(this.zuul.getSensitiveHeaders().containsAll(
Arrays.asList("Cookie", "Set-Cookie", "Authorization")));
}
@Test
public void addSensitiveHeaders() {
this.zuul.setSensitiveHeaders(Collections.singleton("x-bar"));
ZuulRoute route = new ZuulRoute("foo");
route.setSensitiveHeaders(Collections.singleton("x-foo"));
this.zuul.getRoutes().put("foo", route);
assertFalse(this.zuul.getRoutes().get("foo").getSensitiveHeaders()
.contains("Cookie"));
ZuulRoute foo = this.zuul.getRoutes().get("foo");
assertTrue(foo.getSensitiveHeaders().contains("x-foo"));
assertFalse(foo.getSensitiveHeaders().contains("Cookie"));
assertTrue(this.zuul.getSensitiveHeaders().contains("x-bar"));
assertFalse(this.zuul.getSensitiveHeaders().contains("Cookie"));
}
}
......@@ -16,12 +16,15 @@
package org.springframework.cloud.netflix.zuul.filters.pre;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.netflix.zuul.filters.ProxyRequestHelper;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties.ZuulRoute;
import org.springframework.cloud.netflix.zuul.filters.discovery.DiscoveryClientRouteLocator;
......@@ -31,6 +34,8 @@ import com.netflix.util.Pair;
import com.netflix.zuul.context.RequestContext;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.MockitoAnnotations.initMocks;
/**
......@@ -236,7 +241,39 @@ public class PreDecorationFilterTests {
this.filter.run();
assertEquals("/special/api/bar/1", ctx.get("forward.to"));
}
}
@Test
public void sensitiveHeadersOverride() throws Exception {
this.properties.setPrefix("/api");
this.properties.setStripPrefix(true);
this.properties.setSensitiveHeaders(Collections.singleton("x-bar"));
this.request.setRequestURI("/api/foo/1");
ZuulRoute route = new ZuulRoute("/foo/**", "foo");
route.setSensitiveHeaders(Collections.singleton("x-foo"));
this.routeLocator.addRoute(route);
this.filter.run();
RequestContext ctx = RequestContext.getCurrentContext();
@SuppressWarnings("unchecked")
Set<String> sensitiveHeaders = (Set<String>) ctx.get(ProxyRequestHelper.IGNORED_HEADERS);
assertTrue("sensitiveHeaders is wrong", sensitiveHeaders.containsAll(Collections.singletonList("x-foo")));
assertFalse("sensitiveHeaders is wrong", sensitiveHeaders.contains("Cookie"));
}
@Test
public void sensitiveHeadersDefaults() throws Exception {
this.properties.setPrefix("/api");
this.properties.setStripPrefix(true);
this.properties.setSensitiveHeaders(Collections.singleton("x-bar"));
this.request.setRequestURI("/api/foo/1");
this.routeLocator.addRoute("/foo/**", "foo");
this.filter.run();
RequestContext ctx = RequestContext.getCurrentContext();
@SuppressWarnings("unchecked")
Set<String> sensitiveHeaders = (Set<String>) ctx.get(ProxyRequestHelper.IGNORED_HEADERS);
assertTrue("sensitiveHeaders is wrong", sensitiveHeaders.containsAll(Collections.singletonList("x-bar")));
assertFalse("sensitiveHeaders is wrong", sensitiveHeaders.contains("Cookie"));
}
private Object getHeader(List<Pair<String, String>> headers, String key) {
String value = null;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment