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