Add support for configuring RemoteIpValve’s internalProxies
Closes gh-1522
This commit is contained in:
parent
5ba86a103d
commit
468b6cb1f7
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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=
|
||||
----
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue