Add support for configuring RemoteIpValve’s internalProxies

Closes gh-1522
This commit is contained in:
Andy Wilkinson 2014-09-16 15:39:11 +01:00
parent 5ba86a103d
commit 468b6cb1f7
4 changed files with 116 additions and 15 deletions

View File

@ -46,6 +46,7 @@ import org.springframework.util.StringUtils;
*
* @author Dave Syer
* @author Stephane Nicoll
* @author Andy Wilkinson
*/
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = false)
public class ServerProperties implements EmbeddedServletContainerCustomizer {
@ -161,6 +162,11 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer {
private boolean accessLogEnabled = false;
private String internalProxies = "10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|" // 10/8
+ "192\\.168\\.\\d{1,3}\\.\\d{1,3}|" // 192.168/16
+ "169\\.254\\.\\d{1,3}\\.\\d{1,3}|" // 169.254/16
+ "127\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}"; // 127/8
private String protocolHeader = "x-forwarded-proto";
private String remoteIpHeader = "x-forwarded-for";
@ -223,6 +229,14 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer {
this.accessLogPattern = accessLogPattern;
}
public String getInternalProxies() {
return this.internalProxies;
}
public void setInternalProxies(String internalProxies) {
this.internalProxies = internalProxies;
}
public String getProtocolHeader() {
return this.protocolHeader;
}
@ -266,6 +280,7 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer {
RemoteIpValve valve = new RemoteIpValve();
valve.setRemoteIpHeader(remoteIpHeader);
valve.setProtocolHeader(protocolHeader);
valve.setInternalProxies(getInternalProxies());
factory.addContextValves(valve);
}

View File

@ -21,13 +21,18 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.apache.catalina.Valve;
import org.apache.catalina.valves.RemoteIpValve;
import org.junit.Test;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.boot.bind.RelaxedDataBinder;
import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
import static org.hamcrest.core.IsInstanceOf.instanceOf;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@ -37,6 +42,7 @@ import static org.mockito.Mockito.verify;
*
* @author Dave Syer
* @author Stephane Nicoll
* @author Andy Wilkinson
*/
public class ServerPropertiesTests {
@ -84,13 +90,15 @@ public class ServerPropertiesTests {
map.put("server.tomcat.access_log_pattern", "%h %t '%r' %s %b");
map.put("server.tomcat.protocol_header", "X-Forwarded-Protocol");
map.put("server.tomcat.remote_ip_header", "Remote-Ip");
new RelaxedDataBinder(this.properties, "server").bind(new MutablePropertyValues(
map));
map.put("server.tomcat.internal_proxies", "10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}");
bindProperties(map);
assertEquals("%h %t '%r' %s %b", this.properties.getTomcat()
.getAccessLogPattern());
assertEquals("Remote-Ip", this.properties.getTomcat().getRemoteIpHeader());
assertEquals("X-Forwarded-Protocol", this.properties.getTomcat()
.getProtocolHeader());
assertEquals("10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}", this.properties.getTomcat()
.getInternalProxies());
}
@Test
@ -112,8 +120,7 @@ public class ServerPropertiesTests {
public void testCustomizeUriEncoding() throws Exception {
Map<String, String> map = new HashMap<String, String>();
map.put("server.tomcat.uriEncoding", "US-ASCII");
new RelaxedDataBinder(this.properties, "server").bind(new MutablePropertyValues(
map));
bindProperties(map);
assertEquals("US-ASCII", this.properties.getTomcat().getUriEncoding());
}
@ -121,9 +128,66 @@ public class ServerPropertiesTests {
public void testCustomizeTomcatHeaderSize() throws Exception {
Map<String, String> map = new HashMap<String, String>();
map.put("server.tomcat.maxHttpHeaderSize", "9999");
new RelaxedDataBinder(this.properties, "server").bind(new MutablePropertyValues(
map));
bindProperties(map);
assertEquals(9999, this.properties.getTomcat().getMaxHttpHeaderSize());
}
@Test
public void disableTomcatRemoteIpValve() throws Exception {
Map<String, String> map = new HashMap<String, String>();
map.put("server.tomcat.remote_ip_header", "");
map.put("server.tomcat.protocol_header", "");
bindProperties(map);
TomcatEmbeddedServletContainerFactory container = new TomcatEmbeddedServletContainerFactory();
this.properties.customize(container);
assertEquals(0, container.getValves().size());
}
@Test
public void defaultTomcatRemoteIpValve() throws Exception {
TomcatEmbeddedServletContainerFactory container = new TomcatEmbeddedServletContainerFactory();
this.properties.customize(container);
assertEquals(1, container.getValves().size());
Valve valve = container.getValves().iterator().next();
assertThat(valve, instanceOf(RemoteIpValve.class));
RemoteIpValve remoteIpValve = (RemoteIpValve) valve;
assertEquals("x-forwarded-proto", remoteIpValve.getProtocolHeader());
assertEquals("x-forwarded-for", remoteIpValve.getRemoteIpHeader());
String expectedInternalProxies = "10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|" // 10/8
+ "192\\.168\\.\\d{1,3}\\.\\d{1,3}|" // 192.168/16
+ "169\\.254\\.\\d{1,3}\\.\\d{1,3}|" // 169.254/16
+ "127\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}"; // 127/8
assertEquals(expectedInternalProxies, remoteIpValve.getInternalProxies());
}
@Test
public void customTomcatRemoteIpValve() throws Exception {
Map<String, String> map = new HashMap<String, String>();
map.put("server.tomcat.remote_ip_header", "x-my-remote-ip-header");
map.put("server.tomcat.protocol_header", "x-my-protocol-header");
map.put("server.tomcat.internal_proxies", "192.168.0.1");
bindProperties(map);
TomcatEmbeddedServletContainerFactory container = new TomcatEmbeddedServletContainerFactory();
this.properties.customize(container);
assertEquals(1, container.getValves().size());
Valve valve = container.getValves().iterator().next();
assertThat(valve, instanceOf(RemoteIpValve.class));
RemoteIpValve remoteIpValve = (RemoteIpValve) valve;
assertEquals("x-my-protocol-header", remoteIpValve.getProtocolHeader());
assertEquals("x-my-remote-ip-header", remoteIpValve.getRemoteIpHeader());
assertEquals("192.168.0.1", remoteIpValve.getInternalProxies());
}
private void bindProperties(Map<String, String> map) {
new RelaxedDataBinder(this.properties, "server").bind(new MutablePropertyValues(
map));
}
}

View File

@ -58,6 +58,10 @@ content into your application; rather pick only the properties that you need.
server.servlet-path= # the servlet path, defaults to '/'
server.tomcat.access-log-pattern= # log pattern of the access log
server.tomcat.access-log-enabled=false # is access logging enabled
server.tomcat.internal-proxies=10\.\d{1,3}\.\d{1,3}\.\d{1,3}|\
192\.168\.\d{1,3}\.\d{1,3}|\
169\.254\.\d{1,3}\.\d{1,3}|\
127\.\d{1,3}\.\d{1,3}\.\d{1,3} # regular expression matching trusted IP addresses
server.tomcat.protocol-header=x-forwarded-proto # ssl forward headers
server.tomcat.remote-ip-header=x-forwarded-for
server.tomcat.basedir=/tmp # base dir (usually not needed, defaults to tmp)

View File

@ -494,20 +494,38 @@ HTTPS connector:
[[howto-use-tomcat-behind-a-proxy-server]]
=== Use Tomcat behind a front-end proxy server
Spring Boot will automatically configure Tomcat's `RemoteIpValve` if it detects some
environment settings. This allows you to transparently use the standard `x-forwarded-for`
and `x-forwarded-proto` headers that most front-end proxy servers add.
You can switch on the valve by adding some entries to application.properties, e.g.
Spring Boot will automatically configure Tomcat's `RemoteIpValve`. This allows you to
transparently use the standard `x-forwarded-for` and `x-forwarded-proto` headers that
most front-end proxy servers add. If your proxy uses different headers you can
customize the valve's configuration by adding some entries to `application.properties`,
e.g.
[indent=0]
----
server.tomcat.remote_ip_header=x-forwarded-for
server.tomcat.protocol_header=x-forwarded-proto
server.tomcat.remote_ip_header=x-your-remote-ip-header
server.tomcat.protocol_header=x-your-protocol-header
----
Alternatively, you can add the `RemoteIpValve` yourself by adding a
`TomcatEmbeddedServletContainerFactory` bean.
The valve is also configured with a default regular expression that matches internal
proxies that are to be trusted. By default, IP addresses in 10/8, 192.168/16, 169.254/16
and 127/8 are trusted. You can customize the valve's configuration by adding an entry
to `application.properties`, e.g.
[indent=0]
----
server.tomcat.internal_proxies=192\.168\.\d{1,3}\.\d{1,3}
----
Alternatively, you can take complete control of the configuration of the `RemoteIpValve`
by configuring and adding it in a `TomcatEmbeddedServletContainerFactory` bean.
Lastly, you can switch off the valve by adding some entries to `application.properties`:
[indent=0]
----
server.tomcat.remote_ip_header=
server.tomcat.protocol_header=
----