Skip to content

Commit 468b6cb

Browse files
committed
Add support for configuring RemoteIpValve’s internalProxies
Closes gh-1522
1 parent 5ba86a1 commit 468b6cb

File tree

4 files changed

+114
-13
lines changed

4 files changed

+114
-13
lines changed

spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
*
4747
* @author Dave Syer
4848
* @author Stephane Nicoll
49+
* @author Andy Wilkinson
4950
*/
5051
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = false)
5152
public class ServerProperties implements EmbeddedServletContainerCustomizer {
@@ -161,6 +162,11 @@ public static class Tomcat {
161162

162163
private boolean accessLogEnabled = false;
163164

165+
private String internalProxies = "10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|" // 10/8
166+
+ "192\\.168\\.\\d{1,3}\\.\\d{1,3}|" // 192.168/16
167+
+ "169\\.254\\.\\d{1,3}\\.\\d{1,3}|" // 169.254/16
168+
+ "127\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}"; // 127/8
169+
164170
private String protocolHeader = "x-forwarded-proto";
165171

166172
private String remoteIpHeader = "x-forwarded-for";
@@ -223,6 +229,14 @@ public void setAccessLogPattern(String accessLogPattern) {
223229
this.accessLogPattern = accessLogPattern;
224230
}
225231

232+
public String getInternalProxies() {
233+
return this.internalProxies;
234+
}
235+
236+
public void setInternalProxies(String internalProxies) {
237+
this.internalProxies = internalProxies;
238+
}
239+
226240
public String getProtocolHeader() {
227241
return this.protocolHeader;
228242
}
@@ -266,6 +280,7 @@ public void customize(Context context) {
266280
RemoteIpValve valve = new RemoteIpValve();
267281
valve.setRemoteIpHeader(remoteIpHeader);
268282
valve.setProtocolHeader(protocolHeader);
283+
valve.setInternalProxies(getInternalProxies());
269284
factory.addContextValves(valve);
270285
}
271286

spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/ServerPropertiesTests.java

Lines changed: 69 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,18 @@
2121
import java.util.HashMap;
2222
import java.util.Map;
2323

24+
import org.apache.catalina.Valve;
25+
import org.apache.catalina.valves.RemoteIpValve;
2426
import org.junit.Test;
2527
import org.springframework.beans.MutablePropertyValues;
2628
import org.springframework.boot.bind.RelaxedDataBinder;
2729
import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
30+
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
2831

32+
import static org.hamcrest.core.IsInstanceOf.instanceOf;
2933
import static org.junit.Assert.assertEquals;
3034
import static org.junit.Assert.assertFalse;
35+
import static org.junit.Assert.assertThat;
3136
import static org.mockito.Mockito.mock;
3237
import static org.mockito.Mockito.never;
3338
import static org.mockito.Mockito.verify;
@@ -37,6 +42,7 @@
3742
*
3843
* @author Dave Syer
3944
* @author Stephane Nicoll
45+
* @author Andy Wilkinson
4046
*/
4147
public class ServerPropertiesTests {
4248

@@ -84,13 +90,15 @@ public void testTomcatBinding() throws Exception {
8490
map.put("server.tomcat.access_log_pattern", "%h %t '%r' %s %b");
8591
map.put("server.tomcat.protocol_header", "X-Forwarded-Protocol");
8692
map.put("server.tomcat.remote_ip_header", "Remote-Ip");
87-
new RelaxedDataBinder(this.properties, "server").bind(new MutablePropertyValues(
88-
map));
93+
map.put("server.tomcat.internal_proxies", "10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}");
94+
bindProperties(map);
8995
assertEquals("%h %t '%r' %s %b", this.properties.getTomcat()
9096
.getAccessLogPattern());
9197
assertEquals("Remote-Ip", this.properties.getTomcat().getRemoteIpHeader());
9298
assertEquals("X-Forwarded-Protocol", this.properties.getTomcat()
9399
.getProtocolHeader());
100+
assertEquals("10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}", this.properties.getTomcat()
101+
.getInternalProxies());
94102
}
95103

96104
@Test
@@ -112,18 +120,74 @@ public void testCustomizeTomcatPort() throws Exception {
112120
public void testCustomizeUriEncoding() throws Exception {
113121
Map<String, String> map = new HashMap<String, String>();
114122
map.put("server.tomcat.uriEncoding", "US-ASCII");
115-
new RelaxedDataBinder(this.properties, "server").bind(new MutablePropertyValues(
116-
map));
123+
bindProperties(map);
117124
assertEquals("US-ASCII", this.properties.getTomcat().getUriEncoding());
118125
}
119126

120127
@Test
121128
public void testCustomizeTomcatHeaderSize() throws Exception {
122129
Map<String, String> map = new HashMap<String, String>();
123130
map.put("server.tomcat.maxHttpHeaderSize", "9999");
131+
bindProperties(map);
132+
assertEquals(9999, this.properties.getTomcat().getMaxHttpHeaderSize());
133+
}
134+
135+
@Test
136+
public void disableTomcatRemoteIpValve() throws Exception {
137+
Map<String, String> map = new HashMap<String, String>();
138+
map.put("server.tomcat.remote_ip_header", "");
139+
map.put("server.tomcat.protocol_header", "");
140+
bindProperties(map);
141+
142+
TomcatEmbeddedServletContainerFactory container = new TomcatEmbeddedServletContainerFactory();
143+
this.properties.customize(container);
144+
145+
assertEquals(0, container.getValves().size());
146+
}
147+
148+
@Test
149+
public void defaultTomcatRemoteIpValve() throws Exception {
150+
TomcatEmbeddedServletContainerFactory container = new TomcatEmbeddedServletContainerFactory();
151+
this.properties.customize(container);
152+
153+
assertEquals(1, container.getValves().size());
154+
Valve valve = container.getValves().iterator().next();
155+
assertThat(valve, instanceOf(RemoteIpValve.class));
156+
RemoteIpValve remoteIpValve = (RemoteIpValve) valve;
157+
assertEquals("x-forwarded-proto", remoteIpValve.getProtocolHeader());
158+
assertEquals("x-forwarded-for", remoteIpValve.getRemoteIpHeader());
159+
160+
String expectedInternalProxies = "10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|" // 10/8
161+
+ "192\\.168\\.\\d{1,3}\\.\\d{1,3}|" // 192.168/16
162+
+ "169\\.254\\.\\d{1,3}\\.\\d{1,3}|" // 169.254/16
163+
+ "127\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}"; // 127/8
164+
165+
assertEquals(expectedInternalProxies, remoteIpValve.getInternalProxies());
166+
}
167+
168+
@Test
169+
public void customTomcatRemoteIpValve() throws Exception {
170+
Map<String, String> map = new HashMap<String, String>();
171+
map.put("server.tomcat.remote_ip_header", "x-my-remote-ip-header");
172+
map.put("server.tomcat.protocol_header", "x-my-protocol-header");
173+
map.put("server.tomcat.internal_proxies", "192.168.0.1");
174+
bindProperties(map);
175+
176+
TomcatEmbeddedServletContainerFactory container = new TomcatEmbeddedServletContainerFactory();
177+
this.properties.customize(container);
178+
179+
assertEquals(1, container.getValves().size());
180+
Valve valve = container.getValves().iterator().next();
181+
assertThat(valve, instanceOf(RemoteIpValve.class));
182+
RemoteIpValve remoteIpValve = (RemoteIpValve) valve;
183+
assertEquals("x-my-protocol-header", remoteIpValve.getProtocolHeader());
184+
assertEquals("x-my-remote-ip-header", remoteIpValve.getRemoteIpHeader());
185+
assertEquals("192.168.0.1", remoteIpValve.getInternalProxies());
186+
}
187+
188+
private void bindProperties(Map<String, String> map) {
124189
new RelaxedDataBinder(this.properties, "server").bind(new MutablePropertyValues(
125190
map));
126-
assertEquals(9999, this.properties.getTomcat().getMaxHttpHeaderSize());
127191
}
128192

129193
}

spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ content into your application; rather pick only the properties that you need.
5858
server.servlet-path= # the servlet path, defaults to '/'
5959
server.tomcat.access-log-pattern= # log pattern of the access log
6060
server.tomcat.access-log-enabled=false # is access logging enabled
61+
server.tomcat.internal-proxies=10\.\d{1,3}\.\d{1,3}\.\d{1,3}|\
62+
192\.168\.\d{1,3}\.\d{1,3}|\
63+
169\.254\.\d{1,3}\.\d{1,3}|\
64+
127\.\d{1,3}\.\d{1,3}\.\d{1,3} # regular expression matching trusted IP addresses
6165
server.tomcat.protocol-header=x-forwarded-proto # ssl forward headers
6266
server.tomcat.remote-ip-header=x-forwarded-for
6367
server.tomcat.basedir=/tmp # base dir (usually not needed, defaults to tmp)

spring-boot-docs/src/main/asciidoc/howto.adoc

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -494,20 +494,38 @@ HTTPS connector:
494494

495495
[[howto-use-tomcat-behind-a-proxy-server]]
496496
=== Use Tomcat behind a front-end proxy server
497-
Spring Boot will automatically configure Tomcat's `RemoteIpValve` if it detects some
498-
environment settings. This allows you to transparently use the standard `x-forwarded-for`
499-
and `x-forwarded-proto` headers that most front-end proxy servers add.
497+
Spring Boot will automatically configure Tomcat's `RemoteIpValve`. This allows you to
498+
transparently use the standard `x-forwarded-for` and `x-forwarded-proto` headers that
499+
most front-end proxy servers add. If your proxy uses different headers you can
500+
customize the valve's configuration by adding some entries to `application.properties`,
501+
e.g.
500502

501-
You can switch on the valve by adding some entries to application.properties, e.g.
503+
[indent=0]
504+
----
505+
server.tomcat.remote_ip_header=x-your-remote-ip-header
506+
server.tomcat.protocol_header=x-your-protocol-header
507+
----
508+
509+
The valve is also configured with a default regular expression that matches internal
510+
proxies that are to be trusted. By default, IP addresses in 10/8, 192.168/16, 169.254/16
511+
and 127/8 are trusted. You can customize the valve's configuration by adding an entry
512+
to `application.properties`, e.g.
502513

503514
[indent=0]
504515
----
505-
server.tomcat.remote_ip_header=x-forwarded-for
506-
server.tomcat.protocol_header=x-forwarded-proto
516+
server.tomcat.internal_proxies=192\.168\.\d{1,3}\.\d{1,3}
507517
----
508518

509-
Alternatively, you can add the `RemoteIpValve` yourself by adding a
510-
`TomcatEmbeddedServletContainerFactory` bean.
519+
Alternatively, you can take complete control of the configuration of the `RemoteIpValve`
520+
by configuring and adding it in a `TomcatEmbeddedServletContainerFactory` bean.
521+
522+
Lastly, you can switch off the valve by adding some entries to `application.properties`:
523+
524+
[indent=0]
525+
----
526+
server.tomcat.remote_ip_header=
527+
server.tomcat.protocol_header=
528+
----
511529

512530

513531

0 commit comments

Comments
 (0)