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