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:53:17 UTC

[tomcat] 02/02: 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 7.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit 31e0a994ba6b69980f6beecd14324f94d8e35f7a
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 ++++++++++++++++++--
 .../org/apache/catalina/valves/AccessLogValve.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 5e95840..56759f9 100644
--- a/java/org/apache/catalina/filters/RemoteIpFilter.java
+++ b/java/org/apache/catalina/filters/RemoteIpFilter.java
@@ -41,6 +41,7 @@ import org.apache.catalina.Globals;
 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;
 
 /**
@@ -441,6 +442,8 @@ public class RemoteIpFilter implements Filter {
 
         protected Map<String, List<String>> headers;
 
+        protected String localName;
+
         protected int localPort;
 
         protected String remoteAddr;
@@ -451,15 +454,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<String, List<String>>();
@@ -524,6 +531,11 @@ public class RemoteIpFilter implements Filter {
         }
 
         @Override
+        public String getLocalName() {
+            return localName;
+        }
+
+        @Override
         public int getLocalPort() {
             return localPort;
         }
@@ -544,6 +556,11 @@ public class RemoteIpFilter implements Filter {
         }
 
         @Override
+        public String getServerName() {
+            return serverName;
+        }
+
+        @Override
         public int getServerPort() {
             return serverPort;
         }
@@ -571,6 +588,10 @@ public class RemoteIpFilter implements Filter {
 
         }
 
+        public void setLocalName(String localName) {
+            this.localName = localName;
+        }
+
         public void setLocalPort(int localPort) {
             this.localPort = localPort;
         }
@@ -591,6 +612,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;
         }
@@ -638,8 +663,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";
@@ -709,6 +738,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;
@@ -820,17 +853,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,
@@ -841,6 +894,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()));
             }
@@ -907,6 +962,10 @@ public class RemoteIpFilter implements Filter {
         }
     }
 
+    public boolean isChangeLocalName() {
+        return changeLocalName;
+    }
+
     public boolean isChangeLocalPort() {
         return changeLocalPort;
     }
@@ -966,6 +1025,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));
         }
@@ -974,6 +1037,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));
         }
@@ -1006,6 +1073,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()}.
@@ -1065,6 +1148,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/AccessLogValve.java b/java/org/apache/catalina/valves/AccessLogValve.java
index 8d133e6..d33f622 100644
--- a/java/org/apache/catalina/valves/AccessLogValve.java
+++ b/java/org/apache/catalina/valves/AccessLogValve.java
@@ -1926,11 +1926,25 @@ public class AccessLogValve extends ValveBase implements AccessLog {
     /**
      * write local server name - %v
      */
-    protected static class LocalServerNameElement implements AccessLogElement {
+    protected class LocalServerNameElement implements AccessLogElement {
         @Override
         public void addElement(StringBuilder 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 477c8ab..087a6d5 100644
--- a/java/org/apache/catalina/valves/LocalStrings.properties
+++ b/java/org/apache/catalina/valves/LocalStrings.properties
@@ -125,6 +125,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}]
 remoteIpValve.syntax=Invalid regular expressions [{0}] provided.
 
diff --git a/java/org/apache/catalina/valves/RemoteIpValve.java b/java/org/apache/catalina/valves/RemoteIpValve.java
index f36cd72..3f97be9 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>
@@ -398,6 +399,10 @@ public class RemoteIpValve extends ValveBase {
         return result.toString();
     }
 
+    private String hostHeader = null;
+
+    private boolean changeLocalName = false;
+
     /**
      * @see #setHttpServerPort(int)
      */
@@ -408,6 +413,8 @@ public class RemoteIpValve extends ValveBase {
      */
     private int httpsServerPort = 443;
 
+    private String portHeader = null;
+
     private boolean changeLocalPort = false;
 
     /**
@@ -430,8 +437,6 @@ public class RemoteIpValve extends ValveBase {
      */
     private String protocolHeaderHttpsValue = "https";
 
-    private String portHeader = null;
-
     /**
      * @see #setProxiesHeader(String)
      */
@@ -462,21 +467,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;
     }
 
     /**
@@ -501,6 +527,14 @@ public class RemoteIpValve extends ValveBase {
         this.portHeader = portHeader;
     }
 
+    public boolean isChangeLocalPort() {
+        return changeLocalPort;
+    }
+
+    public void setChangeLocalPort(boolean changeLocalPort) {
+        this.changeLocalPort = changeLocalPort;
+    }
+
     /**
      * Return descriptive information about this Valve implementation.
      */
@@ -581,7 +615,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 &&
@@ -662,13 +699,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()) {
@@ -685,6 +747,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()));
         }
@@ -695,7 +759,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 3db2d24..254dde7 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 e62db95..033fa4e 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");
@@ -767,6 +773,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 ebe9f2a..17ce73e 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -62,6 +62,11 @@
 <section name="Tomcat 7.0.97 (violetagg)">
   <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 6d3334f..30e525b 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 702a464..e5d50c9 100644
--- a/webapps/docs/config/valve.xml
+++ b/webapps/docs/config/valve.xml
@@ -904,6 +904,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
@@ -930,6 +936,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