You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ma...@apache.org on 2019/07/30 20:47:52 UTC
[tomcat] branch 8.5.x updated: Fix
https://bz.apache.org/bugzilla/show_bug.cgi?id=57665
This is an automated email from the ASF dual-hosted git repository.
markt pushed a commit to branch 8.5.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/8.5.x by this push:
new f94e936 Fix https://bz.apache.org/bugzilla/show_bug.cgi?id=57665
f94e936 is described below
commit f94e936471bb6b0dcb2d4a1ceb1fa69a14c74385
Author: Mark Thomas <ma...@apache.org>
AuthorDate: Tue Jul 30 21:42:37 2019 +0100
Fix https://bz.apache.org/bugzilla/show_bug.cgi?id=57665
Implement X-Forwarded-Host support for RemoteIpFilter and RemoteIpValve
---
java/org/apache/catalina/AccessLog.java | 15 ++-
.../catalina/filters/LocalStrings.properties | 2 +
.../apache/catalina/filters/RemoteIpFilter.java | 113 ++++++++++++++++++--
.../catalina/valves/AbstractAccessLogValve.java | 18 +++-
.../apache/catalina/valves/LocalStrings.properties | 2 +
java/org/apache/catalina/valves/RemoteIpValve.java | 95 ++++++++++++++---
.../catalina/filters/TestRemoteIpFilter.java | 80 ++++++++++++++
.../apache/catalina/valves/TestRemoteIpValve.java | 118 +++++++++++++++++++++
webapps/docs/changelog.xml | 5 +
webapps/docs/config/filter.xml | 13 +++
webapps/docs/config/valve.xml | 13 +++
11 files changed, 446 insertions(+), 28 deletions(-)
diff --git a/java/org/apache/catalina/AccessLog.java b/java/org/apache/catalina/AccessLog.java
index 017a27b..7e6f28d 100644
--- a/java/org/apache/catalina/AccessLog.java
+++ b/java/org/apache/catalina/AccessLog.java
@@ -38,28 +38,35 @@ public interface AccessLog {
* the AccessLog.
*/
public static final String REMOTE_ADDR_ATTRIBUTE =
- "org.apache.catalina.AccessLog.RemoteAddr";
+ "org.apache.catalina.AccessLog.RemoteAddr";
/**
* Name of request attribute used to override remote host name recorded by
* the AccessLog.
*/
public static final String REMOTE_HOST_ATTRIBUTE =
- "org.apache.catalina.AccessLog.RemoteHost";
+ "org.apache.catalina.AccessLog.RemoteHost";
/**
* Name of request attribute used to override the protocol recorded by the
* AccessLog.
*/
public static final String PROTOCOL_ATTRIBUTE =
- "org.apache.catalina.AccessLog.Protocol";
+ "org.apache.catalina.AccessLog.Protocol";
+
+ /**
+ * Name of request attribute used to override the server name recorded by
+ * the AccessLog.
+ */
+ public static final String SERVER_NAME_ATTRIBUTE =
+ "org.apache.catalina.AccessLog.ServerName";
/**
* Name of request attribute used to override the server port recorded by
* the AccessLog.
*/
public static final String SERVER_PORT_ATTRIBUTE =
- "org.apache.catalina.AccessLog.ServerPort";
+ "org.apache.catalina.AccessLog.ServerPort";
/**
diff --git a/java/org/apache/catalina/filters/LocalStrings.properties b/java/org/apache/catalina/filters/LocalStrings.properties
index 41a4bef..3f2b136 100644
--- a/java/org/apache/catalina/filters/LocalStrings.properties
+++ b/java/org/apache/catalina/filters/LocalStrings.properties
@@ -55,6 +55,8 @@ httpHeaderSecurityFilter.committed=Unable to add HTTP headers since response is
remoteCidrFilter.invalid=Invalid configuration provided for [{0}]. See previous messages for details.
remoteCidrFilter.noRemoteIp=Client does not have an IP address. Request denied.
+remoteIpFilter.invalidHostHeader=Invalid value [{0}] found for Host in HTTP header [{1}]
+remoteIpFilter.invalidHostWithPort=Host value [{0}] in HTTP header [{1}] included a port number which will be ignored
remoteIpFilter.invalidNumber=Illegal number for parameter [{0}]: [{1}]
requestFilter.deny=Denied request for [{0}] based on property [{1}]
diff --git a/java/org/apache/catalina/filters/RemoteIpFilter.java b/java/org/apache/catalina/filters/RemoteIpFilter.java
index c6d01bc..d22233f 100644
--- a/java/org/apache/catalina/filters/RemoteIpFilter.java
+++ b/java/org/apache/catalina/filters/RemoteIpFilter.java
@@ -44,6 +44,7 @@ import org.apache.catalina.servlet4preview.http.PushBuilder;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.http.FastHttpDateFormat;
+import org.apache.tomcat.util.http.parser.Host;
import org.apache.tomcat.util.res.StringManager;
/**
@@ -445,6 +446,8 @@ public class RemoteIpFilter implements Filter {
protected final Map<String, List<String>> headers;
+ protected String localName;
+
protected int localPort;
protected String remoteAddr;
@@ -455,15 +458,19 @@ public class RemoteIpFilter implements Filter {
protected boolean secure;
+ protected String serverName;
+
protected int serverPort;
public XForwardedRequest(HttpServletRequest request) {
super(request);
+ this.localName = request.getLocalName();
this.localPort = request.getLocalPort();
this.remoteAddr = request.getRemoteAddr();
this.remoteHost = request.getRemoteHost();
this.scheme = request.getScheme();
this.secure = request.isSecure();
+ this.serverName = request.getServerName();
this.serverPort = request.getServerPort();
headers = new HashMap<>();
@@ -528,6 +535,11 @@ public class RemoteIpFilter implements Filter {
}
@Override
+ public String getLocalName() {
+ return localName;
+ }
+
+ @Override
public int getLocalPort() {
return localPort;
}
@@ -548,6 +560,11 @@ public class RemoteIpFilter implements Filter {
}
@Override
+ public String getServerName() {
+ return serverName;
+ }
+
+ @Override
public int getServerPort() {
return serverPort;
}
@@ -575,6 +592,10 @@ public class RemoteIpFilter implements Filter {
}
+ public void setLocalName(String localName) {
+ this.localName = localName;
+ }
+
public void setLocalPort(int localPort) {
this.localPort = localPort;
}
@@ -595,6 +616,10 @@ public class RemoteIpFilter implements Filter {
this.secure = secure;
}
+ public void setServerName(String serverName) {
+ this.serverName = serverName;
+ }
+
public void setServerPort(int serverPort) {
this.serverPort = serverPort;
}
@@ -654,8 +679,12 @@ public class RemoteIpFilter implements Filter {
protected static final String PROTOCOL_HEADER_HTTPS_VALUE_PARAMETER = "protocolHeaderHttpsValue";
+ protected static final String HOST_HEADER_PARAMETER = "hostHeader";
+
protected static final String PORT_HEADER_PARAMETER = "portHeader";
+ protected static final String CHANGE_LOCAL_NAME_PARAMETER = "changeLocalName";
+
protected static final String CHANGE_LOCAL_PORT_PARAMETER = "changeLocalPort";
protected static final String PROXIES_HEADER_PARAMETER = "proxiesHeader";
@@ -728,6 +757,10 @@ public class RemoteIpFilter implements Filter {
private String protocolHeaderHttpsValue = "https";
+ private String hostHeader = null;
+
+ private boolean changeLocalName = false;
+
private String portHeader = null;
private boolean changeLocalPort = false;
@@ -839,17 +872,37 @@ public class RemoteIpFilter implements Filter {
}
}
+ if (hostHeader != null) {
+ String hostHeaderValue = request.getHeader(hostHeader);
+ if (hostHeaderValue != null) {
+ try {
+ int portIndex = Host.parse(hostHeaderValue);
+ if (portIndex > -1) {
+ log.debug(sm.getString("remoteIpFilter.invalidHostWithPort", hostHeaderValue, hostHeader));
+ hostHeaderValue = hostHeaderValue.substring(0, portIndex);
+ }
+
+ xRequest.setServerName(hostHeaderValue);
+ if (isChangeLocalName()) {
+ xRequest.setLocalName(hostHeaderValue);
+ }
+
+ } catch (IllegalArgumentException iae) {
+ log.debug(sm.getString("remoteIpFilter.invalidHostHeader", hostHeaderValue, hostHeader));
+ }
+ }
+ }
request.setAttribute(Globals.REQUEST_FORWARDED_ATTRIBUTE, Boolean.TRUE);
if (log.isDebugEnabled()) {
- log.debug("Incoming request " + request.getRequestURI() + " with originalRemoteAddr '" + request.getRemoteAddr()
- + "', originalRemoteHost='" + request.getRemoteHost() + "', originalSecure='" + request.isSecure()
- + "', originalScheme='" + request.getScheme() + "', original[" + remoteIpHeader + "]='"
- + concatRemoteIpHeaderValue + "', original[" + protocolHeader + "]='"
- + (protocolHeader == null ? null : request.getHeader(protocolHeader)) + "' will be seen as newRemoteAddr='"
- + xRequest.getRemoteAddr() + "', newRemoteHost='" + xRequest.getRemoteHost() + "', newScheme='"
- + xRequest.getScheme() + "', newSecure='" + xRequest.isSecure() + "', new[" + remoteIpHeader + "]='"
- + xRequest.getHeader(remoteIpHeader) + "', new[" + proxiesHeader + "]='" + xRequest.getHeader(proxiesHeader) + "'");
+ log.debug("Incoming request " + request.getRequestURI() + " with originalRemoteAddr [" + request.getRemoteAddr() +
+ "], originalRemoteHost=[" + request.getRemoteHost() + "], originalSecure=[" + request.isSecure() +
+ "], originalScheme=[" + request.getScheme() + "], originalServerName=[" + request.getServerName() +
+ "], originalServerPort=[" + request.getServerPort() +
+ "] will be seen as newRemoteAddr=[" + xRequest.getRemoteAddr() +
+ "], newRemoteHost=[" + xRequest.getRemoteHost() + "], newSecure=[" + xRequest.isSecure() +
+ "], newScheme=[" + xRequest.getScheme() + "], newServerName=[" + xRequest.getServerName() +
+ "], newServerPort=[" + xRequest.getServerPort() + "]");
}
if (requestAttributesEnabled) {
request.setAttribute(AccessLog.REMOTE_ADDR_ATTRIBUTE,
@@ -860,6 +913,8 @@ public class RemoteIpFilter implements Filter {
xRequest.getRemoteHost());
request.setAttribute(AccessLog.PROTOCOL_ATTRIBUTE,
xRequest.getProtocol());
+ request.setAttribute(AccessLog.SERVER_NAME_ATTRIBUTE,
+ xRequest.getServerName());
request.setAttribute(AccessLog.SERVER_PORT_ATTRIBUTE,
Integer.valueOf(xRequest.getServerPort()));
}
@@ -926,6 +981,10 @@ public class RemoteIpFilter implements Filter {
}
}
+ public boolean isChangeLocalName() {
+ return changeLocalName;
+ }
+
public boolean isChangeLocalPort() {
return changeLocalPort;
}
@@ -985,6 +1044,10 @@ public class RemoteIpFilter implements Filter {
setProtocolHeaderHttpsValue(filterConfig.getInitParameter(PROTOCOL_HEADER_HTTPS_VALUE_PARAMETER));
}
+ if (filterConfig.getInitParameter(HOST_HEADER_PARAMETER) != null) {
+ setHostHeader(filterConfig.getInitParameter(HOST_HEADER_PARAMETER));
+ }
+
if (filterConfig.getInitParameter(PORT_HEADER_PARAMETER) != null) {
setPortHeader(filterConfig.getInitParameter(PORT_HEADER_PARAMETER));
}
@@ -993,6 +1056,10 @@ public class RemoteIpFilter implements Filter {
setChangeLocalPort(Boolean.parseBoolean(filterConfig.getInitParameter(CHANGE_LOCAL_PORT_PARAMETER)));
}
+ if (filterConfig.getInitParameter(CHANGE_LOCAL_NAME_PARAMETER) != null) {
+ setChangeLocalName(Boolean.parseBoolean(filterConfig.getInitParameter(CHANGE_LOCAL_NAME_PARAMETER)));
+ }
+
if (filterConfig.getInitParameter(PROXIES_HEADER_PARAMETER) != null) {
setProxiesHeader(filterConfig.getInitParameter(PROXIES_HEADER_PARAMETER));
}
@@ -1025,6 +1092,22 @@ public class RemoteIpFilter implements Filter {
/**
* <p>
* If <code>true</code>, the return values for both {@link
+ * ServletRequest#getLocalName()} and {@link ServletRequest#getServerName()}
+ * will be modified by this Filter rather than just
+ * {@link ServletRequest#getServerName()}.
+ * </p>
+ * <p>
+ * Default value : <code>false</code>
+ * </p>
+ * @param changeLocalName The new flag value
+ */
+ public void setChangeLocalName(boolean changeLocalName) {
+ this.changeLocalName = changeLocalName;
+ }
+
+ /**
+ * <p>
+ * If <code>true</code>, the return values for both {@link
* ServletRequest#getLocalPort()} and {@link ServletRequest#getServerPort()}
* will be modified by this Filter rather than just
* {@link ServletRequest#getServerPort()}.
@@ -1084,6 +1167,20 @@ public class RemoteIpFilter implements Filter {
/**
* <p>
+ * Header that holds the incoming host, usually named
+ * <code>X-Forwarded-HOst</code>.
+ * </p>
+ * <p>
+ * Default value : <code>null</code>
+ * </p>
+ * @param hostHeader The header name
+ */
+ public void setHostHeader(String hostHeader) {
+ this.hostHeader = hostHeader;
+ }
+
+ /**
+ * <p>
* Header that holds the incoming port, usually named
* <code>X-Forwarded-Port</code>. If <code>null</code>,
* {@link #httpServerPort} or {@link #httpsServerPort} will be used.
diff --git a/java/org/apache/catalina/valves/AbstractAccessLogValve.java b/java/org/apache/catalina/valves/AbstractAccessLogValve.java
index e23f49d..950dbad 100644
--- a/java/org/apache/catalina/valves/AbstractAccessLogValve.java
+++ b/java/org/apache/catalina/valves/AbstractAccessLogValve.java
@@ -1353,11 +1353,25 @@ public abstract class AbstractAccessLogValve extends ValveBase implements Access
/**
* write local server name - %v
*/
- protected static class LocalServerNameElement implements AccessLogElement {
+ protected class LocalServerNameElement implements AccessLogElement {
@Override
public void addElement(CharArrayWriter buf, Date date, Request request,
Response response, long time) {
- buf.append(request.getServerName());
+ String value = null;
+ if (requestAttributesEnabled) {
+ Object serverName = request.getAttribute(SERVER_NAME_ATTRIBUTE);
+ if (serverName != null) {
+ value = serverName.toString();
+ }
+ }
+ if (value == null || value.length() == 0) {
+ value = request.getServerName();
+ }
+ if (value == null || value.length() == 0) {
+ value = "-";
+ }
+
+ buf.append(value);
}
}
diff --git a/java/org/apache/catalina/valves/LocalStrings.properties b/java/org/apache/catalina/valves/LocalStrings.properties
index cc01407..f9f7646 100644
--- a/java/org/apache/catalina/valves/LocalStrings.properties
+++ b/java/org/apache/catalina/valves/LocalStrings.properties
@@ -123,6 +123,8 @@ jdbcAccessLogValve.exception=Exception performing insert access entry
remoteCidrValve.invalid=Invalid configuration provided for [{0}]. See previous messages for details.
remoteCidrValve.noRemoteIp=Client does not have an IP address. Request denied.
+remoteIpValve.invalidHostHeader=Invalid value [{0}] found for Host in HTTP header [{1}]
+remoteIpValve.invalidHostWithPort=Host value [{0}] in HTTP header [{1}] included a port number which will be ignored
remoteIpValve.invalidPortHeader=Invalid value [{0}] found for port in HTTP header [{1}]
requestFilterValve.configInvalid=One or more invalid configuration settings were provided for the Remote[Addr|Host]Valve which prevented the Valve and its parent containers from starting
diff --git a/java/org/apache/catalina/valves/RemoteIpValve.java b/java/org/apache/catalina/valves/RemoteIpValve.java
index cd08cc7..89aa4b4 100644
--- a/java/org/apache/catalina/valves/RemoteIpValve.java
+++ b/java/org/apache/catalina/valves/RemoteIpValve.java
@@ -32,6 +32,7 @@ import org.apache.catalina.connector.Response;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.http.MimeHeaders;
+import org.apache.tomcat.util.http.parser.Host;
/**
* <p>
@@ -394,6 +395,10 @@ public class RemoteIpValve extends ValveBase {
return result.toString();
}
+ private String hostHeader = null;
+
+ private boolean changeLocalName = false;
+
/**
* @see #setHttpServerPort(int)
*/
@@ -404,6 +409,8 @@ public class RemoteIpValve extends ValveBase {
*/
private int httpsServerPort = 443;
+ private String portHeader = null;
+
private boolean changeLocalPort = false;
/**
@@ -429,8 +436,6 @@ public class RemoteIpValve extends ValveBase {
*/
private String protocolHeaderHttpsValue = "https";
- private String portHeader = null;
-
/**
* @see #setProxiesHeader(String)
*/
@@ -461,21 +466,42 @@ public class RemoteIpValve extends ValveBase {
super(true);
}
+ /**
+ * Obtain the name of the HTTP header used to override the value returned
+ * by {@link Request#getServerName()} and (optionally depending on {link
+ * {@link #isChangeLocalName()} {@link Request#getLocalName()}.
+ *
+ * @return The HTTP header name
+ */
+ public String getHostHeader() {
+ return hostHeader;
+ }
- public int getHttpsServerPort() {
- return httpsServerPort;
+ /**
+ * Set the name of the HTTP header used to override the value returned
+ * by {@link Request#getServerName()} and (optionally depending on {link
+ * {@link #isChangeLocalName()} {@link Request#getLocalName()}.
+ *
+ * @param hostHeader The HTTP header name
+ */
+ public void setHostHeader(String hostHeader) {
+ this.hostHeader = hostHeader;
}
- public int getHttpServerPort() {
- return httpServerPort;
+ public boolean isChangeLocalName() {
+ return changeLocalName;
}
- public boolean isChangeLocalPort() {
- return changeLocalPort;
+ public void setChangeLocalName(boolean changeLocalName) {
+ this.changeLocalName = changeLocalName;
}
- public void setChangeLocalPort(boolean changeLocalPort) {
- this.changeLocalPort = changeLocalPort;
+ public int getHttpServerPort() {
+ return httpServerPort;
+ }
+
+ public int getHttpsServerPort() {
+ return httpsServerPort;
}
/**
@@ -500,6 +526,14 @@ public class RemoteIpValve extends ValveBase {
this.portHeader = portHeader;
}
+ public boolean isChangeLocalPort() {
+ return changeLocalPort;
+ }
+
+ public void setChangeLocalPort(boolean changeLocalPort) {
+ this.changeLocalPort = changeLocalPort;
+ }
+
/**
* @see #setInternalProxies(String)
* @return Regular expression that defines the internal proxies
@@ -572,7 +606,10 @@ public class RemoteIpValve extends ValveBase {
final String originalRemoteHost = request.getRemoteHost();
final String originalScheme = request.getScheme();
final boolean originalSecure = request.isSecure();
+ final String originalServerName = request.getServerName();
+ final String originalLocalName = request.getLocalName();
final int originalServerPort = request.getServerPort();
+ final int originalLocalPort = request.getLocalPort();
final String originalProxiesHeader = request.getHeader(proxiesHeader);
final String originalRemoteIpHeader = request.getHeader(remoteIpHeader);
boolean isInternal = internalProxies != null &&
@@ -653,13 +690,38 @@ public class RemoteIpValve extends ValveBase {
}
}
+ if (hostHeader != null) {
+ String hostHeaderValue = request.getHeader(hostHeader);
+ if (hostHeaderValue != null) {
+ try {
+ int portIndex = Host.parse(hostHeaderValue);
+ if (portIndex > -1) {
+ log.debug(sm.getString("remoteIpValve.invalidHostWithPort", hostHeaderValue, hostHeader));
+ hostHeaderValue = hostHeaderValue.substring(0, portIndex);
+ }
+
+ request.getCoyoteRequest().serverName().setString(hostHeaderValue);
+ if (isChangeLocalName()) {
+ request.getCoyoteRequest().localName().setString(hostHeaderValue);
+ }
+
+ } catch (IllegalArgumentException iae) {
+ log.debug(sm.getString("remoteIpValve.invalidHostHeader", hostHeaderValue, hostHeader));
+ }
+ }
+ }
+
request.setAttribute(Globals.REQUEST_FORWARDED_ATTRIBUTE, Boolean.TRUE);
if (log.isDebugEnabled()) {
- log.debug("Incoming request " + request.getRequestURI() + " with originalRemoteAddr '" + originalRemoteAddr
- + "', originalRemoteHost='" + originalRemoteHost + "', originalSecure='" + originalSecure + "', originalScheme='"
- + originalScheme + "' will be seen as newRemoteAddr='" + request.getRemoteAddr() + "', newRemoteHost='"
- + request.getRemoteHost() + "', newScheme='" + request.getScheme() + "', newSecure='" + request.isSecure() + "'");
+ log.debug("Incoming request " + request.getRequestURI() + " with originalRemoteAddr [" + originalRemoteAddr +
+ "], originalRemoteHost=[" + originalRemoteHost + "], originalSecure=[" + originalSecure +
+ "], originalScheme=[" + originalScheme + "], originalServerName=[" + originalServerName +
+ "], originalServerPort=[" + originalServerPort +
+ "] will be seen as newRemoteAddr=[" + request.getRemoteAddr() +
+ "], newRemoteHost=[" + request.getRemoteHost() + "], newSecure=[" + request.isSecure() +
+ "], newScheme=[" + request.getScheme() + "], newServerName=[" + request.getServerName() +
+ "], newServerPort=[" + request.getServerPort() + "]");
}
} else {
if (log.isDebugEnabled()) {
@@ -676,6 +738,8 @@ public class RemoteIpValve extends ValveBase {
request.getRemoteHost());
request.setAttribute(AccessLog.PROTOCOL_ATTRIBUTE,
request.getProtocol());
+ request.setAttribute(AccessLog.SERVER_NAME_ATTRIBUTE,
+ Integer.valueOf(request.getServerName()));
request.setAttribute(AccessLog.SERVER_PORT_ATTRIBUTE,
Integer.valueOf(request.getServerPort()));
}
@@ -686,7 +750,10 @@ public class RemoteIpValve extends ValveBase {
request.setRemoteHost(originalRemoteHost);
request.setSecure(originalSecure);
request.getCoyoteRequest().scheme().setString(originalScheme);
+ request.getCoyoteRequest().serverName().setString(originalServerName);
+ request.getCoyoteRequest().localName().setString(originalLocalName);
request.setServerPort(originalServerPort);
+ request.setLocalPort(originalLocalPort);
MimeHeaders headers = request.getCoyoteRequest().getMimeHeaders();
if (originalProxiesHeader == null || originalProxiesHeader.length() == 0) {
diff --git a/test/org/apache/catalina/filters/TestRemoteIpFilter.java b/test/org/apache/catalina/filters/TestRemoteIpFilter.java
index 3fb6580..2ac4284 100644
--- a/test/org/apache/catalina/filters/TestRemoteIpFilter.java
+++ b/test/org/apache/catalina/filters/TestRemoteIpFilter.java
@@ -97,6 +97,7 @@ public class TestRemoteIpFilter extends TomcatBaseTest {
writer.println("request.remoteHost=" + request.getRemoteHost());
writer.println("request.secure=" + request.isSecure());
writer.println("request.scheme=" + request.getScheme());
+ writer.println("request.serverName=" + request.getServerName());
writer.println("request.serverPort=" + request.getServerPort());
writer.println();
@@ -550,6 +551,85 @@ public class TestRemoteIpFilter extends TomcatBaseTest {
}
@Test
+ public void testInvokeXforwardedHost() throws Exception {
+ // PREPARE
+ FilterDef filterDef = new FilterDef();
+ filterDef.addInitParameter("hostHeader", "x-forwarded-host");
+ filterDef.addInitParameter("portHeader", "x-forwarded-port");
+ filterDef.addInitParameter("protocolHeader", "x-forwarded-proto");
+
+ MockHttpServletRequest request = new MockHttpServletRequest();
+ // client ip
+ request.setRemoteAddr("192.168.0.10");
+ request.setRemoteHost("192.168.0.10");
+ // protocol
+ request.setSecure(false);
+ request.setServerPort(8080);
+ request.setScheme("http");
+ // host and port
+ request.getCoyoteRequest().serverName().setString("10.0.0.1");
+ request.setHeader("x-forwarded-host", "example.com");
+ request.setHeader("x-forwarded-port", "8443");
+ request.setHeader("x-forwarded-proto", "https");
+
+ // TEST
+ HttpServletRequest actualRequest = testRemoteIpFilter(filterDef, request).getRequest();
+
+ // VERIFY
+ // protocol
+ String actualServerName = actualRequest.getServerName();
+ Assert.assertEquals("postInvoke serverName", "example.com", actualServerName);
+
+ String actualScheme = actualRequest.getScheme();
+ Assert.assertEquals("postInvoke scheme", "https", actualScheme);
+
+ int actualServerPort = actualRequest.getServerPort();
+ Assert.assertEquals("postInvoke serverPort", 8443, actualServerPort);
+
+ boolean actualSecure = actualRequest.isSecure();
+ Assert.assertTrue("postInvoke secure", actualSecure);
+ }
+
+ @Test
+ public void testInvokeXforwardedHostAndPort() throws Exception {
+ // PREPARE
+ FilterDef filterDef = new FilterDef();
+ filterDef.addInitParameter("hostHeader", "x-forwarded-host");
+ filterDef.addInitParameter("portHeader", "x-forwarded-port");
+ filterDef.addInitParameter("protocolHeader", "x-forwarded-proto");
+
+ MockHttpServletRequest request = new MockHttpServletRequest();
+ // client ip
+ request.setRemoteAddr("192.168.0.10");
+ request.setRemoteHost("192.168.0.10");
+ // protocol
+ request.setSecure(false);
+ request.setServerPort(8080);
+ request.setScheme("http");
+ // host and port
+ request.getCoyoteRequest().serverName().setString("10.0.0.1");
+ request.setHeader("x-forwarded-host", "example.com:8443");
+ request.setHeader("x-forwarded-proto", "https");
+
+ // TEST
+ HttpServletRequest actualRequest = testRemoteIpFilter(filterDef, request).getRequest();
+
+ // VERIFY
+ // protocol
+ String actualServerName = actualRequest.getServerName();
+ Assert.assertEquals("postInvoke serverName", "example.com", actualServerName);
+
+ String actualScheme = actualRequest.getScheme();
+ Assert.assertEquals("postInvoke scheme", "https", actualScheme);
+
+ int actualServerPort = actualRequest.getServerPort();
+ Assert.assertEquals("postInvoke serverPort", 443, actualServerPort);
+
+ boolean actualSecure = actualRequest.isSecure();
+ Assert.assertTrue("postInvoke secure", actualSecure);
+ }
+
+ @Test
public void testListToCommaDelimitedString() {
String[] actual = RemoteIpFilter.commaDelimitedListToStringArray("element1, element2, element3");
String[] expected = new String[] { "element1", "element2", "element3" };
diff --git a/test/org/apache/catalina/valves/TestRemoteIpValve.java b/test/org/apache/catalina/valves/TestRemoteIpValve.java
index cb26382..a50ab47 100644
--- a/test/org/apache/catalina/valves/TestRemoteIpValve.java
+++ b/test/org/apache/catalina/valves/TestRemoteIpValve.java
@@ -41,6 +41,7 @@ public class TestRemoteIpValve {
private String remoteHost;
private String scheme;
private boolean secure;
+ private String serverName;
private int serverPort;
private String forwardedFor;
private String forwardedBy;
@@ -57,6 +58,10 @@ public class TestRemoteIpValve {
return scheme;
}
+ public String getServerName() {
+ return serverName;
+ }
+
public int getServerPort() {
return serverPort;
}
@@ -79,6 +84,7 @@ public class TestRemoteIpValve {
this.remoteAddr = request.getRemoteAddr();
this.scheme = request.getScheme();
this.secure = request.isSecure();
+ this.serverName = request.getServerName();
this.serverPort = request.getServerPort();
this.forwardedFor = request.getHeader("x-forwarded-for");
this.forwardedBy = request.getHeader("x-forwarded-by");
@@ -846,6 +852,118 @@ public class TestRemoteIpValve {
}
@Test
+ public void testInvokeXforwardedHost() throws Exception {
+
+ // PREPARE
+ RemoteIpValve remoteIpValve = new RemoteIpValve();
+ remoteIpValve.setHostHeader("x-forwarded-host");
+ remoteIpValve.setPortHeader("x-forwarded-port");
+ remoteIpValve.setProtocolHeader("x-forwarded-proto");
+ RemoteAddrAndHostTrackerValve remoteAddrAndHostTrackerValve = new RemoteAddrAndHostTrackerValve();
+ remoteIpValve.setNext(remoteAddrAndHostTrackerValve);
+
+ Request request = new MockRequest();
+ request.setCoyoteRequest(new org.apache.coyote.Request());
+ // client ip
+ request.setRemoteAddr("192.168.0.10");
+ request.setRemoteHost("192.168.0.10");
+ // protocol
+ request.setSecure(false);
+ request.setServerPort(8080);
+ request.getCoyoteRequest().scheme().setString("http");
+ // host and port
+ request.getCoyoteRequest().serverName().setString("10.0.0.1");
+ request.getCoyoteRequest().getMimeHeaders().addValue("x-forwarded-host").setString("example.com:8443");
+ request.getCoyoteRequest().getMimeHeaders().addValue("x-forwarded-port").setString("8443");
+ request.getCoyoteRequest().getMimeHeaders().addValue("x-forwarded-proto").setString("https");
+
+ // TEST
+ remoteIpValve.invoke(request, null);
+
+ // VERIFY
+ // protocol
+ String actualServerName = remoteAddrAndHostTrackerValve.getServerName();
+ Assert.assertEquals("tracked serverName", "example.com", actualServerName);
+
+ String actualScheme = remoteAddrAndHostTrackerValve.getScheme();
+ Assert.assertEquals("tracked scheme", "https", actualScheme);
+
+ int actualServerPort = remoteAddrAndHostTrackerValve.getServerPort();
+ Assert.assertEquals("tracked serverPort", 8443, actualServerPort);
+
+ boolean actualSecure = remoteAddrAndHostTrackerValve.isSecure();
+ Assert.assertTrue("tracked secure", actualSecure);
+
+ String actualPostInvokeServerName = request.getServerName();
+ Assert.assertEquals("postInvoke serverName", "10.0.0.1", actualPostInvokeServerName);
+
+ boolean actualPostInvokeSecure = request.isSecure();
+ Assert.assertFalse("postInvoke secure", actualPostInvokeSecure);
+
+ int actualPostInvokeServerPort = request.getServerPort();
+ Assert.assertEquals("postInvoke serverPort", 8080, actualPostInvokeServerPort);
+
+ String actualPostInvokeScheme = request.getScheme();
+ Assert.assertEquals("postInvoke scheme", "http", actualPostInvokeScheme);
+ }
+
+ @Test
+ public void testInvokeXforwardedHostAndPort() throws Exception {
+
+ // PREPARE
+ RemoteIpValve remoteIpValve = new RemoteIpValve();
+ remoteIpValve.setHostHeader("x-forwarded-host");
+ remoteIpValve.setPortHeader("x-forwarded-port");
+ remoteIpValve.setProtocolHeader("x-forwarded-proto");
+ RemoteAddrAndHostTrackerValve remoteAddrAndHostTrackerValve = new RemoteAddrAndHostTrackerValve();
+ remoteIpValve.setNext(remoteAddrAndHostTrackerValve);
+
+ Request request = new MockRequest();
+ request.setCoyoteRequest(new org.apache.coyote.Request());
+ // client ip
+ request.setRemoteAddr("192.168.0.10");
+ request.setRemoteHost("192.168.0.10");
+ // protocol
+ request.setSecure(false);
+ request.setServerPort(8080);
+ request.getCoyoteRequest().scheme().setString("http");
+ // host and port
+ request.getCoyoteRequest().serverName().setString("10.0.0.1");
+ request.getCoyoteRequest().getMimeHeaders().addValue("x-forwarded-host").setString("example.com");
+ request.getCoyoteRequest().getMimeHeaders().addValue("x-forwarded-port").setString("8443");
+ request.getCoyoteRequest().getMimeHeaders().addValue("x-forwarded-proto").setString("https");
+
+ // TEST
+ remoteIpValve.invoke(request, null);
+
+ // VERIFY
+ // protocol
+ String actualServerName = remoteAddrAndHostTrackerValve.getServerName();
+ Assert.assertEquals("tracked serverName", "example.com", actualServerName);
+
+ String actualScheme = remoteAddrAndHostTrackerValve.getScheme();
+ Assert.assertEquals("tracked scheme", "https", actualScheme);
+
+ int actualServerPort = remoteAddrAndHostTrackerValve.getServerPort();
+ Assert.assertEquals("tracked serverPort", 8443, actualServerPort);
+
+ boolean actualSecure = remoteAddrAndHostTrackerValve.isSecure();
+ Assert.assertTrue("tracked secure", actualSecure);
+
+ String actualPostInvokeServerName = request.getServerName();
+ Assert.assertEquals("postInvoke serverName", "10.0.0.1", actualPostInvokeServerName);
+
+ boolean actualPostInvokeSecure = request.isSecure();
+ Assert.assertFalse("postInvoke secure", actualPostInvokeSecure);
+
+ int actualPostInvokeServerPort = request.getServerPort();
+ Assert.assertEquals("postInvoke serverPort", 8080, actualPostInvokeServerPort);
+
+ String actualPostInvokeScheme = request.getScheme();
+ Assert.assertEquals("postInvoke scheme", "http", actualPostInvokeScheme);
+ }
+
+ @Test
public void testInvokeNotAllowedRemoteAddr() throws Exception {
// PREPARE
RemoteIpValve remoteIpValve = new RemoteIpValve();
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index dbca095..23e137d 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -47,6 +47,11 @@
<section name="Tomcat 8.5.44 (markt)" rtext="in development">
<subsection name="Catalina">
<changelog>
+ <add>
+ <bug>57665</bug>: Add support for the <code>X-Forwarded-Host</code>
+ header to the <code>RemoteIpFilter</code> and <code>RemotepValve</code>.
+ (markt)
+ </add>
<fix>
<bug>63550</bug>: Only try the <code>alternateURL</code> in the
<code>JNDIRealm</code> if one has been specified. (markt)
diff --git a/webapps/docs/config/filter.xml b/webapps/docs/config/filter.xml
index 9e5b5fb..72cbaf4 100644
--- a/webapps/docs/config/filter.xml
+++ b/webapps/docs/config/filter.xml
@@ -1597,6 +1597,12 @@ FINE: Request "/docs/config/manager.html" with response status "200"
default of <code>X-Forwarded-Proto</code> is used.</p>
</attribute>
+ <attribute name="hostHeader" required="false">
+ <p>Name of the HTTP Header read by this valve that holds the host
+ used by the client to connect to the proxy. If not specified, the
+ default of <code>null</code> is used.</p>
+ </attribute>
+
<attribute name="portHeader" required="false">
<p>Name of the HTTP Header read by this valve that holds the port
used by the client to connect to the proxy. If not specified, the
@@ -1623,6 +1629,13 @@ FINE: Request "/docs/config/manager.html" with response status "200"
specified, the default of <code>443</code> is used.</p>
</attribute>
+ <attribute name="changeLocalName" required="false">
+ <p>If <code>true</code>, the value returned by
+ <code>ServletRequest.getLocalName()</code> and
+ <code>ServletRequest.getServerName()</code> is modified by the this
+ filter. If not specified, the default of <code>false</code> is used.</p>
+ </attribute>
+
<attribute name="changeLocalPort" required="false">
<p>If <code>true</code>, the value returned by
<code>ServletRequest.getLocalPort()</code> and
diff --git a/webapps/docs/config/valve.xml b/webapps/docs/config/valve.xml
index 5aae5e8..048c77f 100644
--- a/webapps/docs/config/valve.xml
+++ b/webapps/docs/config/valve.xml
@@ -993,6 +993,12 @@
default of <code>X-Forwarded-Proto</code> is used.</p>
</attribute>
+ <attribute name="hostHeader" required="false">
+ <p>Name of the HTTP Header read by this valve that holds the host
+ used by the client to connect to the proxy. If not specified, the
+ default of <code>null</code> is used.</p>
+ </attribute>
+
<attribute name="portHeader" required="false">
<p>Name of the HTTP Header read by this valve that holds the port
used by the client to connect to the proxy. If not specified, the
@@ -1019,6 +1025,13 @@
specified, the default of <code>443</code> is used.</p>
</attribute>
+ <attribute name="changeLocalHost" required="false">
+ <p>If <code>true</code>, the value returned by
+ <code>ServletRequest.getLocalHost()</code> and
+ <code>ServletRequest.getServerHost()</code> is modified by the this
+ valve. If not specified, the default of <code>false</code> is used.</p>
+ </attribute>
+
<attribute name="changeLocalPort" required="false">
<p>If <code>true</code>, the value returned by
<code>ServletRequest.getLocalPort()</code> and
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org