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 2010/02/02 14:28:57 UTC

svn commit: r905627 - in /tomcat/trunk: java/org/apache/catalina/valves/RemoteIpValve.java java/org/apache/catalina/valves/mbeans-descriptors.xml test/org/apache/catalina/valves/TestRemoteIpValve.java webapps/docs/config/valve.xml

Author: markt
Date: Tue Feb  2 13:28:55 2010
New Revision: 905627

URL: http://svn.apache.org/viewvc?rev=905627&view=rev
Log:
Fix https://issues.apache.org/bugzilla/show_bug.cgi?id=48653
RemoteIpValve : request.secure and request.scheme are not forced to "false" and "http" if X-Forwarded-Proto=http
Patch provided by Cyrille Le Clerc

Modified:
    tomcat/trunk/java/org/apache/catalina/valves/RemoteIpValve.java
    tomcat/trunk/java/org/apache/catalina/valves/mbeans-descriptors.xml
    tomcat/trunk/test/org/apache/catalina/valves/TestRemoteIpValve.java
    tomcat/trunk/webapps/docs/config/valve.xml

Modified: tomcat/trunk/java/org/apache/catalina/valves/RemoteIpValve.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/valves/RemoteIpValve.java?rev=905627&r1=905626&r2=905627&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/valves/RemoteIpValve.java (original)
+++ tomcat/trunk/java/org/apache/catalina/valves/RemoteIpValve.java Tue Feb  2 13:28:55 2010
@@ -26,6 +26,7 @@
 import java.util.regex.PatternSyntaxException;
 
 import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
 
 import org.apache.tomcat.util.res.StringManager;
 import org.apache.catalina.connector.Request;
@@ -126,6 +127,19 @@
  * <td><code>https</code></td>
  * </tr>
  * <tr>
+ * <td>httpServerPort</td>
+ * <td>Value returned by {@link ServletRequest#getServerPort()} when the <code>protocolHeader</code> indicates <code>http</code> protocol</td>
+ * <td>N/A</td>
+ * <td>integer</td>
+ * <td>80</td>
+ * </tr>
+ * <tr>
+ * <td>httpsServerPort</td>
+ * <td>Value returned by {@link ServletRequest#getServerPort()} when the <code>protocolHeader</code> indicates <code>https</code> protocol</td>
+ * <td>N/A</td>
+ * <td>integer</td>
+ * <td>443</td>
+ * </tr>
  * </table>
  * </p>
  * <p>
@@ -415,6 +429,11 @@
     }
     
     /**
+     * @see #setHttpServerPort(int)
+     */
+    private int httpServerPort = 80;
+    
+    /**
      * @see #setHttpsServerPort(int)
      */
     private int httpsServerPort = 443;
@@ -456,6 +475,10 @@
         return httpsServerPort;
     }
     
+    public int getHttpServerPort() {
+        return httpServerPort;
+    }
+    
     /**
      * Return descriptive information about this Valve implementation.
      */
@@ -507,7 +530,7 @@
     public String getRemoteIpHeader() {
         return remoteIpHeader;
     }
-    
+
     /**
      * @see #setTrustedProxies(String)
      * @return comma delimited list of trusted proxies
@@ -580,12 +603,21 @@
             
             if (protocolHeader != null) {
                 String protocolHeaderValue = request.getHeader(protocolHeader);
-                if (protocolHeaderValue != null && protocolHeaderHttpsValue.equalsIgnoreCase(protocolHeaderValue)) {
+                if (protocolHeaderValue == null) {
+                    // don't modify the secure,scheme and serverPort attributes
+                    // of the request
+                } else if (protocolHeaderHttpsValue.equalsIgnoreCase(protocolHeaderValue)) {
                     request.setSecure(true);
                     // use request.coyoteRequest.scheme instead of request.setScheme() because request.setScheme() is no-op in Tomcat 6.0
                     request.getCoyoteRequest().scheme().setString("https");
                     
                     request.setServerPort(httpsServerPort);
+                } else {
+                    request.setSecure(false);
+                    // use request.coyoteRequest.scheme instead of request.setScheme() because request.setScheme() is no-op in Tomcat 6.0
+                    request.getCoyoteRequest().scheme().setString("http");
+                    
+                    request.setServerPort(httpServerPort);
                 }
             }
             
@@ -613,6 +645,18 @@
     
     /**
      * <p>
+     * Server Port value if the {@link #protocolHeader} is not <code>null</code> and does not indicate HTTP
+     * </p>
+     * <p>
+     * Default value : 80
+     * </p>
+     */
+    public void setHttpServerPort(int httpServerPort) {
+        this.httpServerPort = httpServerPort;
+    }
+    
+    /**
+     * <p>
      * Server Port value if the {@link #protocolHeader} indicates HTTPS
      * </p>
      * <p>

Modified: tomcat/trunk/java/org/apache/catalina/valves/mbeans-descriptors.xml
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/valves/mbeans-descriptors.xml?rev=905627&r1=905626&r2=905627&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/valves/mbeans-descriptors.xml (original)
+++ tomcat/trunk/java/org/apache/catalina/valves/mbeans-descriptors.xml Tue Feb  2 13:28:55 2010
@@ -365,7 +365,17 @@
                description="Comma delimited list of internal proxies"
                type="java.lang.String"
                writeable="false" />
-               
+
+    <attribute name="httpServerPort"
+               description="Value returned by ServletRequest.getServerPort() when the protocolHeader indicates http protocol"
+               type="java.lang.String"
+               writeable="false" />
+    
+    <attribute name="httpsServerPort"
+               description="Value returned by ServletRequest.getServerPort() when the protocolHeader indicates https protocol"
+               type="java.lang.String"
+               writeable="false" />
+    
     <attribute name="protocolHeader"
                description="The protocol header (e.g. &quot;X-Forwarded-Proto&quot;)"
                type="java.lang.String"
@@ -381,7 +391,7 @@
                type="java.lang.String"
                writeable="false" />
                
-    <attribute name="remoteIpHedaer"
+    <attribute name="remoteIpHeader"
                description="The remote IP header name (e.g. &quot;X-Forwarded-For&quot;)"
                type="java.lang.String"
                writeable="false" />

Modified: tomcat/trunk/test/org/apache/catalina/valves/TestRemoteIpValve.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/catalina/valves/TestRemoteIpValve.java?rev=905627&r1=905626&r2=905627&view=diff
==============================================================================
--- tomcat/trunk/test/org/apache/catalina/valves/TestRemoteIpValve.java (original)
+++ tomcat/trunk/test/org/apache/catalina/valves/TestRemoteIpValve.java Tue Feb  2 13:28:55 2010
@@ -38,6 +38,9 @@
     static class RemoteAddrAndHostTrackerValve extends ValveBase {
         private String remoteAddr;
         private String remoteHost;
+        private String scheme;
+        private boolean secure;
+        private int serverPort;
         
         public String getRemoteAddr() {
             return remoteAddr;
@@ -47,10 +50,25 @@
             return remoteHost;
         }
         
+        public String getScheme() {
+            return scheme;
+        }
+
+        public int getServerPort() {
+            return serverPort;
+        }
+
+        public boolean isSecure() {
+            return secure;
+        }
+        
         @Override
         public void invoke(Request request, Response response) throws IOException, ServletException {
             this.remoteHost = request.getRemoteHost();
             this.remoteAddr = request.getRemoteAddr();
+            this.scheme = request.getScheme();
+            this.secure = request.isSecure();
+            this.serverPort = request.getServerPort();
         }
     }
     
@@ -271,6 +289,262 @@
         assertEquals("postInvoke remoteAddr", "remote-host-original-value", actualPostInvokeRemoteHost);
     }
     
+    public void testInvokeXforwardedProtoSaysHttpsForIncomingHttpRequest() throws Exception {
+        
+        // PREPARE
+        RemoteIpValve remoteIpValve = new RemoteIpValve();
+        remoteIpValve.setRemoteIpHeader("x-forwarded-for");
+        remoteIpValve.setProtocolHeader("x-forwarded-proto");
+        RemoteAddrAndHostTrackerValve remoteAddrAndHostTrackerValve = new RemoteAddrAndHostTrackerValve();
+        remoteIpValve.setNext(remoteAddrAndHostTrackerValve);
+        
+        Request request = new Request();
+        request.setCoyoteRequest(new org.apache.coyote.Request());
+        // client ip
+        request.setRemoteAddr("192.168.0.10");
+        request.setRemoteHost("192.168.0.10");
+        request.getCoyoteRequest().getMimeHeaders().addValue("x-forwarded-for").setString("140.211.11.130");
+        // protocol
+        request.getCoyoteRequest().getMimeHeaders().addValue("x-forwarded-proto").setString("https");
+        request.setSecure(false);
+        request.setServerPort(8080);
+        request.getCoyoteRequest().scheme().setString("http");
+        
+        // TEST
+        remoteIpValve.invoke(request, null);
+        
+        // VERIFY
+        // client ip
+        String actualXForwardedFor = request.getHeader("x-forwarded-for");
+        assertNull("no intermediate non-trusted proxy, x-forwarded-for must be null", actualXForwardedFor);
+        
+        String actualXForwardedBy = request.getHeader("x-forwarded-by");
+        assertNull("no intermediate trusted proxy", actualXForwardedBy);
+        
+        String actualRemoteAddr = remoteAddrAndHostTrackerValve.getRemoteAddr();
+        assertEquals("remoteAddr", "140.211.11.130", actualRemoteAddr);
+        
+        String actualRemoteHost = remoteAddrAndHostTrackerValve.getRemoteHost();
+        assertEquals("remoteHost", "140.211.11.130", actualRemoteHost);
+        
+        String actualPostInvokeRemoteAddr = request.getRemoteAddr();
+        assertEquals("postInvoke remoteAddr", "192.168.0.10", actualPostInvokeRemoteAddr);
+        
+        String actualPostInvokeRemoteHost = request.getRemoteHost();
+        assertEquals("postInvoke remoteAddr", "192.168.0.10", actualPostInvokeRemoteHost);
+        
+        // protocol
+        String actualScheme = remoteAddrAndHostTrackerValve.getScheme();
+        assertEquals("x-forwarded-proto says https", "https", actualScheme);
+        
+        int actualServerPort = remoteAddrAndHostTrackerValve.getServerPort();
+        assertEquals("x-forwarded-proto says https", 443, actualServerPort);
+        
+        boolean actualSecure = remoteAddrAndHostTrackerValve.isSecure();
+        assertEquals("x-forwarded-proto says https", true, actualSecure);
+
+        boolean actualPostInvokeSecure = request.isSecure();
+        assertEquals("postInvoke secure", false, actualPostInvokeSecure);
+
+        int actualPostInvokeServerPort = request.getServerPort();
+        assertEquals("postInvoke serverPort", 8080, actualPostInvokeServerPort);
+
+        String actualPostInvokeScheme = request.getScheme();
+        assertEquals("postInvoke scheme", "http", actualPostInvokeScheme);
+    }
+    
+    public void testInvokeXforwardedProtoIsNullForIncomingHttpRequest() throws Exception {
+        
+        // PREPARE
+        RemoteIpValve remoteIpValve = new RemoteIpValve();
+        remoteIpValve.setRemoteIpHeader("x-forwarded-for");
+        remoteIpValve.setProtocolHeader("x-forwarded-proto");
+        RemoteAddrAndHostTrackerValve remoteAddrAndHostTrackerValve = new RemoteAddrAndHostTrackerValve();
+        remoteIpValve.setNext(remoteAddrAndHostTrackerValve);
+        
+        Request request = new Request();
+        request.setCoyoteRequest(new org.apache.coyote.Request());
+        // client ip
+        request.setRemoteAddr("192.168.0.10");
+        request.setRemoteHost("192.168.0.10");
+        request.getCoyoteRequest().getMimeHeaders().addValue("x-forwarded-for").setString("140.211.11.130");
+        // protocol
+        // null "x-forwarded-proto"
+        request.setSecure(false);
+        request.setServerPort(8080);
+        request.getCoyoteRequest().scheme().setString("http");
+        
+        // TEST
+        remoteIpValve.invoke(request, null);
+        
+        // VERIFY
+        // client ip
+        String actualXForwardedFor = request.getHeader("x-forwarded-for");
+        assertNull("no intermediate non-trusted proxy, x-forwarded-for must be null", actualXForwardedFor);
+        
+        String actualXForwardedBy = request.getHeader("x-forwarded-by");
+        assertNull("no intermediate trusted proxy", actualXForwardedBy);
+        
+        String actualRemoteAddr = remoteAddrAndHostTrackerValve.getRemoteAddr();
+        assertEquals("remoteAddr", "140.211.11.130", actualRemoteAddr);
+        
+        String actualRemoteHost = remoteAddrAndHostTrackerValve.getRemoteHost();
+        assertEquals("remoteHost", "140.211.11.130", actualRemoteHost);
+        
+        String actualPostInvokeRemoteAddr = request.getRemoteAddr();
+        assertEquals("postInvoke remoteAddr", "192.168.0.10", actualPostInvokeRemoteAddr);
+        
+        String actualPostInvokeRemoteHost = request.getRemoteHost();
+        assertEquals("postInvoke remoteAddr", "192.168.0.10", actualPostInvokeRemoteHost);
+        
+        // protocol
+        String actualScheme = remoteAddrAndHostTrackerValve.getScheme();
+        assertEquals("x-forwarded-proto is null", "http", actualScheme);
+        
+        int actualServerPort = remoteAddrAndHostTrackerValve.getServerPort();
+        assertEquals("x-forwarded-proto is null", 8080, actualServerPort);
+        
+        boolean actualSecure = remoteAddrAndHostTrackerValve.isSecure();
+        assertEquals("x-forwarded-proto is null", false, actualSecure);
+
+        boolean actualPostInvokeSecure = request.isSecure();
+        assertEquals("postInvoke secure", false, actualPostInvokeSecure);
+
+        int actualPostInvokeServerPort = request.getServerPort();
+        assertEquals("postInvoke serverPort", 8080, actualPostInvokeServerPort);
+
+        String actualPostInvokeScheme = request.getScheme();
+        assertEquals("postInvoke scheme", "http", actualPostInvokeScheme);
+    }
+    
+    public void testInvokeXforwardedProtoSaysHttpForIncomingHttpsRequest() throws Exception {
+        
+        // PREPARE
+        RemoteIpValve remoteIpValve = new RemoteIpValve();
+        remoteIpValve.setRemoteIpHeader("x-forwarded-for");
+        remoteIpValve.setProtocolHeader("x-forwarded-proto");
+        RemoteAddrAndHostTrackerValve remoteAddrAndHostTrackerValve = new RemoteAddrAndHostTrackerValve();
+        remoteIpValve.setNext(remoteAddrAndHostTrackerValve);
+        
+        Request request = new Request();
+        request.setCoyoteRequest(new org.apache.coyote.Request());
+        // client ip
+        request.setRemoteAddr("192.168.0.10");
+        request.setRemoteHost("192.168.0.10");
+        request.getCoyoteRequest().getMimeHeaders().addValue("x-forwarded-for").setString("140.211.11.130");
+        // protocol
+        request.getCoyoteRequest().getMimeHeaders().addValue("x-forwarded-proto").setString("http");
+        request.setSecure(true);
+        request.setServerPort(8443);
+        request.getCoyoteRequest().scheme().setString("https");
+        
+        // TEST
+        remoteIpValve.invoke(request, null);
+        
+        // VERIFY
+        // client ip
+        String actualXForwardedFor = request.getHeader("x-forwarded-for");
+        assertNull("no intermediate non-trusted proxy, x-forwarded-for must be null", actualXForwardedFor);
+        
+        String actualXForwardedBy = request.getHeader("x-forwarded-by");
+        assertNull("no intermediate trusted proxy", actualXForwardedBy);
+        
+        String actualRemoteAddr = remoteAddrAndHostTrackerValve.getRemoteAddr();
+        assertEquals("remoteAddr", "140.211.11.130", actualRemoteAddr);
+        
+        String actualRemoteHost = remoteAddrAndHostTrackerValve.getRemoteHost();
+        assertEquals("remoteHost", "140.211.11.130", actualRemoteHost);
+        
+        String actualPostInvokeRemoteAddr = request.getRemoteAddr();
+        assertEquals("postInvoke remoteAddr", "192.168.0.10", actualPostInvokeRemoteAddr);
+        
+        String actualPostInvokeRemoteHost = request.getRemoteHost();
+        assertEquals("postInvoke remoteAddr", "192.168.0.10", actualPostInvokeRemoteHost);
+        
+        // protocol
+        String actualScheme = remoteAddrAndHostTrackerValve.getScheme();
+        assertEquals("x-forwarded-proto says http", "http", actualScheme);
+        
+        int actualServerPort = remoteAddrAndHostTrackerValve.getServerPort();
+        assertEquals("x-forwarded-proto says http", 80, actualServerPort);
+        
+        boolean actualSecure = remoteAddrAndHostTrackerValve.isSecure();
+        assertEquals("x-forwarded-proto says http", false, actualSecure);
+
+        boolean actualPostInvokeSecure = request.isSecure();
+        assertEquals("postInvoke secure", true, actualPostInvokeSecure);
+
+        int actualPostInvokeServerPort = request.getServerPort();
+        assertEquals("postInvoke serverPort", 8443, actualPostInvokeServerPort);
+
+        String actualPostInvokeScheme = request.getScheme();
+        assertEquals("postInvoke scheme", "https", actualPostInvokeScheme);
+    }
+    
+    public void testInvokeXforwardedProtoIsNullForIncomingHttpsRequest() throws Exception {
+        
+        // PREPARE
+        RemoteIpValve remoteIpValve = new RemoteIpValve();
+        remoteIpValve.setRemoteIpHeader("x-forwarded-for");
+        remoteIpValve.setProtocolHeader("x-forwarded-proto");
+        RemoteAddrAndHostTrackerValve remoteAddrAndHostTrackerValve = new RemoteAddrAndHostTrackerValve();
+        remoteIpValve.setNext(remoteAddrAndHostTrackerValve);
+        
+        Request request = new Request();
+        request.setCoyoteRequest(new org.apache.coyote.Request());
+        // client ip
+        request.setRemoteAddr("192.168.0.10");
+        request.setRemoteHost("192.168.0.10");
+        request.getCoyoteRequest().getMimeHeaders().addValue("x-forwarded-for").setString("140.211.11.130");
+        // protocol
+        // Don't declare "x-forwarded-proto"
+        request.setSecure(true);
+        request.setServerPort(8443);
+        request.getCoyoteRequest().scheme().setString("https");
+        
+        // TEST
+        remoteIpValve.invoke(request, null);
+        
+        // VERIFY
+        // client ip
+        String actualXForwardedFor = request.getHeader("x-forwarded-for");
+        assertNull("no intermediate non-trusted proxy, x-forwarded-for must be null", actualXForwardedFor);
+        
+        String actualXForwardedBy = request.getHeader("x-forwarded-by");
+        assertNull("no intermediate trusted proxy", actualXForwardedBy);
+        
+        String actualRemoteAddr = remoteAddrAndHostTrackerValve.getRemoteAddr();
+        assertEquals("remoteAddr", "140.211.11.130", actualRemoteAddr);
+        
+        String actualRemoteHost = remoteAddrAndHostTrackerValve.getRemoteHost();
+        assertEquals("remoteHost", "140.211.11.130", actualRemoteHost);
+        
+        String actualPostInvokeRemoteAddr = request.getRemoteAddr();
+        assertEquals("postInvoke remoteAddr", "192.168.0.10", actualPostInvokeRemoteAddr);
+        
+        String actualPostInvokeRemoteHost = request.getRemoteHost();
+        assertEquals("postInvoke remoteAddr", "192.168.0.10", actualPostInvokeRemoteHost);
+        
+        // protocol
+        String actualScheme = remoteAddrAndHostTrackerValve.getScheme();
+        assertEquals("x-forwarded-proto is null", "https", actualScheme);
+        
+        int actualServerPort = remoteAddrAndHostTrackerValve.getServerPort();
+        assertEquals("x-forwarded-proto is null", 8443, actualServerPort);
+        
+        boolean actualSecure = remoteAddrAndHostTrackerValve.isSecure();
+        assertEquals("x-forwarded-proto is null", true, actualSecure);
+
+        boolean actualPostInvokeSecure = request.isSecure();
+        assertEquals("postInvoke secure", true, actualPostInvokeSecure);
+
+        int actualPostInvokeServerPort = request.getServerPort();
+        assertEquals("postInvoke serverPort", 8443, actualPostInvokeServerPort);
+
+        String actualPostInvokeScheme = request.getScheme();
+        assertEquals("postInvoke scheme", "https", actualPostInvokeScheme);
+    }
+    
     public void testInvokeNotAllowedRemoteAddr() throws Exception {
         // PREPARE
         RemoteIpValve remoteIpValve = new RemoteIpValve();

Modified: tomcat/trunk/webapps/docs/config/valve.xml
URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/config/valve.xml?rev=905627&r1=905626&r2=905627&view=diff
==============================================================================
--- tomcat/trunk/webapps/docs/config/valve.xml (original)
+++ tomcat/trunk/webapps/docs/config/valve.xml Tue Feb  2 13:28:55 2010
@@ -622,9 +622,10 @@
     via a request headers (e.g. &quot;X-Forwarded-For&quot;).</p>
 
     <p>Another feature of this valve is to replace the apparent scheme
-    (http/https) and server port with the scheme presented by a proxy or a load
-    balancer via a request header (e.g. &quot;X-Forwarded-Proto&quot;).</p>
- 
+    (http/https), server port and <code>request.secure</code> with the scheme presented 
+    by a proxy or a load balancer via a request header 
+    (e.g. &quot;X-Forwarded-Proto&quot;).</p>
+    
     <p>This Valve may be used at the <code>Engine</code>, <code>Host</code> or
     <code>Context</code> level as required. Normally, this Valve would be used
     at the <code>Engine</code> level.</p>
@@ -690,6 +691,20 @@
         used.</p>
       </attribute>
 
+      <attribute name="httpServerPort" required="false">
+         <p>Value returned by <code>ServletRequest.getServerPort()</code> 
+         when the <strong>protocolHeader</strong> indicates <code>http</code> 
+         protocol. If not specified, the default of <code>80</code> is
+        used.</p>
+      </attribute>
+
+      <attribute name="httpsServerPort" required="false">
+         <p>Value returned by <code>ServletRequest.getServerPort()</code> 
+         when the <strong>protocolHeader</strong> indicates <code>https</code> 
+         protocol. If not specified, the default of <code>443</code> is
+        used.</p>
+      </attribute>
+      
     </attributes>
 
   </subsection>



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org