This is an automated email from the ASF dual-hosted git repository.

markt pushed a commit to branch 11.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git


The following commit(s) were added to refs/heads/11.0.x by this push:
     new 07862cabc9 Add CIDR support for the configuration of internal and 
trusted proxies
07862cabc9 is described below

commit 07862cabc9d6ae34a7202a8035719b71f2a550c0
Author: Mark Thomas <[email protected]>
AuthorDate: Wed Oct 1 15:30:21 2025 +0100

    Add CIDR support for the configuration of internal and trusted proxies
---
 .../apache/catalina/filters/RemoteIpFilter.java    | 175 ++++++++++++--------
 java/org/apache/catalina/valves/RemoteIpValve.java | 179 +++++++++++++--------
 .../apache/catalina/valves/mbeans-descriptors.xml  |   4 +-
 .../catalina/filters/TestRemoteIpFilter.java       | 103 +++++++++++-
 .../apache/catalina/valves/TestRemoteIpValve.java  |  77 +++++++++
 webapps/docs/changelog.xml                         |   9 ++
 webapps/docs/config/filter.xml                     |  32 ++--
 webapps/docs/config/valve.xml                      |  24 +--
 8 files changed, 438 insertions(+), 165 deletions(-)

diff --git a/java/org/apache/catalina/filters/RemoteIpFilter.java 
b/java/org/apache/catalina/filters/RemoteIpFilter.java
index 22c02cd9d5..984892d7b7 100644
--- a/java/org/apache/catalina/filters/RemoteIpFilter.java
+++ b/java/org/apache/catalina/filters/RemoteIpFilter.java
@@ -42,6 +42,7 @@ import jakarta.servlet.http.HttpServletResponse;
 
 import org.apache.catalina.AccessLog;
 import org.apache.catalina.Globals;
+import org.apache.catalina.util.NetMaskSet;
 import org.apache.catalina.util.RequestUtil;
 import org.apache.juli.logging.Log;
 import org.apache.juli.logging.LogFactory;
@@ -93,7 +94,7 @@ import org.apache.tomcat.util.res.StringManager;
  * <table border="1">
  * <caption>Configuration parameters</caption>
  * <tr>
- * <th>XForwardedFilter property</th>
+ * <th>RemoteIpFilter property</th>
  * <th>Description</th>
  * <th>Equivalent mod_remoteip directive</th>
  * <th>Format</th>
@@ -109,18 +110,12 @@ import org.apache.tomcat.util.res.StringManager;
  * </tr>
  * <tr>
  * <td>internalProxies</td>
- * <td>Regular expression that matches the IP addresses of internal proxies. 
If they appear in the
- * <code>remoteIpHeader</code> value, they will be trusted and will not appear 
in the <code>proxiesHeader</code>
- * value</td>
+ * <td>Either a comma separated list of CIDR blocks or a single regular 
expression that matches the IP addresses of
+ * internal proxies. If they appear in the <code>remoteIpHeader</code> value, 
they will be trusted and will not appear
+ * in the <code>proxiesHeader</code> value</td>
  * <td>RemoteIPInternalProxy</td>
- * <td>Regular expression (in the syntax supported by {@link 
java.util.regex.Pattern java.util.regex})</td>
- * <td>10\.\d{1,3}\.\d{1,3}\.\d{1,3}|192\.168\.\d{1,3}\.\d{1,3}|
- * 169\.254\.\d{1,3}\.\d{1,3}|127\.\d{1,3}\.\d{1,3}\.\d{1,3}|
- * 100\.6[4-9]{1}\.\d{1,3}\.\d{1,3}|100\.[7-9]{1}\d{1}\.\d{1,3}\.\d{1,3}|
- * 100\.1[0-1]{1}\d{1}\.\d{1,3}\.\d{1,3}|100\.12[0-7]{1}\.\d{1,3}\.\d{1,3}|
- * 172\.1[6-9]{1}\.\d{1,3}\.\d{1,3}|172\.2[0-9]{1}\.\d{1,3}\.\d{1,3}| 
172\.3[0-1]{1}\.\d{1,3}\.\d{1,3}|
- * 0:0:0:0:0:0:0:1|::1 <br>
- * By default, 10/8, 192.168/16, 169.254/16, 127/8, 100.64/10, 172.16/12, and 
0:0:0:0:0:0:0:1 are allowed.</td>
+ * <td>Comma separated list of CIDR blocks or a single regular expression 
{@link Pattern}</td>
+ * 
<td>10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,169.254.0.0/16,100.64.0.0/10,127.0.0.0/8,::1/128,fe80::/10,fc00::/7</td>
  * </tr>
  * <tr>
  * <td>proxiesHeader</td>
@@ -132,10 +127,11 @@ import org.apache.tomcat.util.res.StringManager;
  * </tr>
  * <tr>
  * <td>trustedProxies</td>
- * <td>Regular expression that matches the IP addresses of trusted proxies. If 
they appear in the
- * <code>remoteIpHeader</code> value, they will be trusted and will appear in 
the <code>proxiesHeader</code> value</td>
+ * <td>Either a comma separated list of CIDR blocks or a single regular 
expression that matches the IP addresses of
+ * internal proxies. If they appear in the <code>remoteIpHeader</code> value, 
they will be trusted and will appear in
+ * the <code>proxiesHeader</code> value</td>
  * <td>RemoteIPTrustedProxy</td>
- * <td>Regular expression (in the syntax supported by {@link 
java.util.regex.Pattern java.util.regex})</td>
+ * <td>Comma separated list of CIDR blocks or a single regular expression 
{@link Pattern}</td>
  * <td>&nbsp;</td>
  * </tr>
  * <tr>
@@ -177,19 +173,12 @@ import org.apache.tomcat.util.res.StringManager;
  * <td>false</td>
  * </tr>
  * </table>
- * <p>
- * <strong>Regular expression vs. IP address blocks:</strong> 
<code>mod_remoteip</code> allows to use address blocks
- * (e.g. <code>192.168/16</code>) to configure 
<code>RemoteIPInternalProxy</code> and <code>RemoteIPTrustedProxy</code>
- * ; as the JVM doesn't have a library similar to <a href=
- * 
"https://apr.apache.org/docs/apr/1.3/group__apr__network__io.html#gb74d21b8898b7c40bf7fd07ad3eb993d";>apr_ipsubnet_test</a>,
- * we rely on regular expressions.
- * </p>
  * <hr>
  * <p>
  * <strong>Sample with internal proxies</strong>
  * </p>
  * <p>
- * XForwardedFilter configuration:
+ * RemoteIpFilter configuration:
  * </p>
  *
  * <pre>
@@ -198,7 +187,7 @@ import org.apache.tomcat.util.res.StringManager;
  *    
&lt;filter-class&gt;org.apache.catalina.filters.RemoteIpFilter&lt;/filter-class&gt;
  *    &lt;init-param&gt;
  *       &lt;param-name&gt;internalProxies&lt;/param-name&gt;
- *       &lt;param-value&gt;192\.168\.0\.10|192\.168\.0\.11&lt;/param-value&gt;
+ *       &lt;param-value&gt;192.168.0.10/31&lt;/param-value&gt;
  *    &lt;/init-param&gt;
  *    &lt;init-param&gt;
  *       &lt;param-name&gt;remoteIpHeader&lt;/param-name&gt;
@@ -279,7 +268,7 @@ import org.apache.tomcat.util.res.StringManager;
  *    
&lt;filter-class&gt;org.apache.catalina.filters.RemoteIpFilter&lt;/filter-class&gt;
  *    &lt;init-param&gt;
  *       &lt;param-name&gt;internalProxies&lt;/param-name&gt;
- *       &lt;param-value&gt;192\.168\.0\.10|192\.168\.0\.11&lt;/param-value&gt;
+ *       &lt;param-value&gt;192.168.0.10/31&lt;/param-value&gt;
  *    &lt;/init-param&gt;
  *    &lt;init-param&gt;
  *       &lt;param-name&gt;remoteIpHeader&lt;/param-name&gt;
@@ -343,7 +332,7 @@ import org.apache.tomcat.util.res.StringManager;
  *    
&lt;filter-class&gt;org.apache.catalina.filters.RemoteIpFilter&lt;/filter-class&gt;
  *    &lt;init-param&gt;
  *       &lt;param-name&gt;internalProxies&lt;/param-name&gt;
- *       &lt;param-value&gt;192\.168\.0\.10|192\.168\.0\.11&lt;/param-value&gt;
+ *       &lt;param-value&gt;192.168.0.10/31&lt;/param-value&gt;
  *    &lt;/init-param&gt;
  *    &lt;init-param&gt;
  *       &lt;param-name&gt;remoteIpHeader&lt;/param-name&gt;
@@ -408,7 +397,7 @@ import org.apache.tomcat.util.res.StringManager;
  *    
&lt;filter-class&gt;org.apache.catalina.filters.RemoteIpFilter&lt;/filter-class&gt;
  *    &lt;init-param&gt;
  *       &lt;param-name&gt;internalProxies&lt;/param-name&gt;
- *       &lt;param-value&gt;192\.168\.0\.10|192\.168\.0\.11&lt;/param-value&gt;
+ *       &lt;param-value&gt;192.168.0.10/31&lt;/param-value&gt;
  *    &lt;/init-param&gt;
  *    &lt;init-param&gt;
  *       &lt;param-name&gt;remoteIpHeader&lt;/param-name&gt;
@@ -692,15 +681,16 @@ public class RemoteIpFilter extends GenericFilter {
     private int httpsServerPort = 443;
 
     /**
-     * @see #setInternalProxies(String)
+     * Regular expression pattern for internal proxies.
+     */
+    private Pattern internalProxiesRegex = null;
+
+    /**
+     * CIDR notation for internal proxies.
      */
-    private Pattern internalProxies = 
Pattern.compile("10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|" +
-            "192\\.168\\.\\d{1,3}\\.\\d{1,3}|" + 
"169\\.254\\.\\d{1,3}\\.\\d{1,3}|" +
-            "127\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|" + 
"100\\.6[4-9]{1}\\.\\d{1,3}\\.\\d{1,3}|" +
-            "100\\.[7-9]{1}\\d{1}\\.\\d{1,3}\\.\\d{1,3}|" + 
"100\\.1[0-1]{1}\\d{1}\\.\\d{1,3}\\.\\d{1,3}|" +
-            "100\\.12[0-7]{1}\\.\\d{1,3}\\.\\d{1,3}|" + 
"172\\.1[6-9]{1}\\.\\d{1,3}\\.\\d{1,3}|" +
-            "172\\.2[0-9]{1}\\.\\d{1,3}\\.\\d{1,3}|" + 
"172\\.3[0-1]{1}\\.\\d{1,3}\\.\\d{1,3}|" +
-            "0:0:0:0:0:0:0:1|::1|" + "fe[89ab]\\p{XDigit}:.*|" + 
"f[cd]\\p{XDigit}{2}+:.*");
+    private NetMaskSet internalProxiesCidr =
+            
NetMaskSet.parse("10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,169.254.0.0/16,100.64.0.0/10,127.0.0.0/8,"
 +
+                    "::1/128,fe80::/10,fc00::/7");
 
     /**
      * @see #setProtocolHeader(String)
@@ -733,18 +723,24 @@ public class RemoteIpFilter extends GenericFilter {
     private boolean requestAttributesEnabled = true;
 
     /**
-     * @see #setTrustedProxies(String)
+     * Regular expression notation for trusted proxies.
      */
-    private Pattern trustedProxies = null;
+    private Pattern trustedProxiesRegex = null;
+
+    /**
+     * CIDR notation for trusted proxies.
+     */
+    private NetMaskSet trustedProxiesCidr = null;
+
 
     private boolean enableLookups;
 
     public void doFilter(HttpServletRequest request, HttpServletResponse 
response, FilterChain chain)
             throws IOException, ServletException {
 
-        boolean isInternal = internalProxies != null && 
internalProxies.matcher(request.getRemoteAddr()).matches();
+        boolean isInternal = isInternalProxy(request.getRemoteAddr());
 
-        if (isInternal || (trustedProxies != null && 
trustedProxies.matcher(request.getRemoteAddr()).matches())) {
+        if (isInternal || isTrustedProxy(request.getRemoteAddr())) {
             String remoteIp = null;
             Deque<String> proxiesHeaderValue = new ArrayDeque<>();
             StringBuilder concatRemoteIpHeaderValue = new StringBuilder();
@@ -766,9 +762,10 @@ public class RemoteIpFilter extends GenericFilter {
             for (idx = remoteIpHeaderValue.length - 1; idx >= 0; idx--) {
                 String currentRemoteIp = remoteIpHeaderValue[idx];
                 remoteIp = currentRemoteIp;
-                if (internalProxies != null && 
internalProxies.matcher(currentRemoteIp).matches()) {
+
+                if (isInternalProxy(currentRemoteIp)) {
                     // do nothing, internalProxies IPs are not appended to the
-                } else if (trustedProxies != null && 
trustedProxies.matcher(currentRemoteIp).matches()) {
+                } else if (isTrustedProxy(currentRemoteIp)) {
                     proxiesHeaderValue.addFirst(currentRemoteIp);
                 } else {
                     idx--; // decrement idx because break statement doesn't do 
it
@@ -884,6 +881,47 @@ public class RemoteIpFilter extends GenericFilter {
 
     }
 
+    /**
+     * Checks if the given IP address is from an internal proxy.
+     *
+     * @param remoteIp The IP address to check
+     *
+     * @return {@code true} if the IP address is from an internal proxy, 
otherwise {@code false}
+     */
+    private boolean isInternalProxy(String remoteIp) {
+        if (internalProxiesRegex != null && 
internalProxiesRegex.matcher(remoteIp).matches()) {
+            return true;
+        }
+        return checkIsCidr(internalProxiesCidr, remoteIp);
+    }
+
+    /**
+     * Checks if the given IP address is from a trusted proxy.
+     *
+     * @param remoteIp The IP address to check
+     *
+     * @return {@code true} if the IP address is from a trusted proxy, 
otherwise {@code false}
+     */
+    private boolean isTrustedProxy(String remoteIp) {
+        if (trustedProxiesRegex != null && 
trustedProxiesRegex.matcher(remoteIp).matches()) {
+            return true;
+        }
+
+        return checkIsCidr(trustedProxiesCidr, remoteIp);
+    }
+
+    private boolean checkIsCidr(NetMaskSet netMaskSet, String remoteIp) {
+        if (netMaskSet == null) {
+            return false;
+        }
+        try {
+            return netMaskSet.contains(remoteIp);
+        } catch (UnknownHostException uhe) {
+            log.debug(sm.getString("remoteIpFilter.invalidRemoteAddress", 
remoteIp), uhe);
+        }
+        return false;
+    }
+
     /*
      * Considers the value to be secure if it exclusively holds forwards for 
{@link #protocolHeaderHttpsValue}.
      */
@@ -958,7 +996,7 @@ public class RemoteIpFilter extends GenericFilter {
      */
     @Deprecated
     public Pattern getInternalProxies() {
-        return internalProxies;
+        return internalProxiesRegex;
     }
 
     /**
@@ -970,10 +1008,13 @@ public class RemoteIpFilter extends GenericFilter {
      */
     @Deprecated
     public String getInternalProxiesAsString() {
-        if (internalProxies == null) {
+        if (internalProxiesCidr != null) {
+            return internalProxiesCidr.toString();
+        } else if (internalProxiesRegex != null) {
+            return internalProxiesRegex.toString();
+        } else {
             return null;
         }
-        return internalProxies.toString();
     }
 
     public String getProtocolHeader() {
@@ -1016,7 +1057,7 @@ public class RemoteIpFilter extends GenericFilter {
      */
     @Deprecated
     public Pattern getTrustedProxies() {
-        return trustedProxies;
+        return trustedProxiesRegex;
     }
 
     /**
@@ -1028,10 +1069,13 @@ public class RemoteIpFilter extends GenericFilter {
      */
     @Deprecated
     public String getTrustedProxiesAsString() {
-        if (trustedProxies == null) {
+        if (trustedProxiesCidr != null) {
+            return trustedProxiesCidr.toString();
+        } else if (trustedProxiesRegex != null) {
+            return trustedProxiesRegex.toString();
+        } else {
             return null;
         }
-        return trustedProxies.toString();
     }
 
     public boolean getEnableLookups() {
@@ -1165,21 +1209,20 @@ public class RemoteIpFilter extends GenericFilter {
     }
 
     /**
-     * <p>
-     * Regular expression that defines the internal proxies.
-     * </p>
-     * <p>
-     * Default value :
-     * 
10\.\d{1,3}\.\d{1,3}\.\d{1,3}|192\.168\.\d{1,3}\.\d{1,3}|169\.254.\d{1,3}.\d{1,3}|127\.\d{1,3}\.\d{1,3}\.\d{1,3}|0:0:0:0:0:0:0:1
-     * </p>
+     * Set the internal proxies either as a comma separated list of CIDR 
blocks or a single regular expression.
      *
-     * @param internalProxies The regexp
+     * @param internalProxies The new internal proxies
      */
     public void setInternalProxies(String internalProxies) {
         if (internalProxies == null || internalProxies.isEmpty()) {
-            this.internalProxies = null;
+            this.internalProxiesRegex = null;
+            this.internalProxiesCidr = null;
+        } else if (internalProxies.indexOf('/') > 0) {
+            this.internalProxiesRegex = null;
+            this.internalProxiesCidr = NetMaskSet.parse(internalProxies);
         } else {
-            this.internalProxies = Pattern.compile(internalProxies);
+            this.internalProxiesRegex = Pattern.compile(internalProxies);
+            this.internalProxiesCidr = null;
         }
     }
 
@@ -1301,20 +1344,20 @@ public class RemoteIpFilter extends GenericFilter {
     }
 
     /**
-     * <p>
-     * Regular expression defining proxies that are trusted when they appear 
in the {@link #remoteIpHeader} header.
-     * </p>
-     * <p>
-     * Default value : empty list, no external proxy is trusted.
-     * </p>
+     * Set the trusted proxies either as a comma separated list of CIDR blocks 
or a single regular expression.
      *
-     * @param trustedProxies The trusted proxies regexp
+     * @param trustedProxies The new trusted proxies
      */
     public void setTrustedProxies(String trustedProxies) {
         if (trustedProxies == null || trustedProxies.isEmpty()) {
-            this.trustedProxies = null;
+            this.trustedProxiesRegex = null;
+            this.trustedProxiesCidr = null;
+        } else if (trustedProxies.indexOf('/') > 0) {
+            this.trustedProxiesRegex = null;
+            this.trustedProxiesCidr = NetMaskSet.parse(trustedProxies);
         } else {
-            this.trustedProxies = Pattern.compile(trustedProxies);
+            this.trustedProxiesCidr = null;
+            this.trustedProxiesRegex = Pattern.compile(trustedProxies);
         }
     }
 
diff --git a/java/org/apache/catalina/valves/RemoteIpValve.java 
b/java/org/apache/catalina/valves/RemoteIpValve.java
index f005a8234a..f54879070d 100644
--- a/java/org/apache/catalina/valves/RemoteIpValve.java
+++ b/java/org/apache/catalina/valves/RemoteIpValve.java
@@ -30,6 +30,7 @@ import org.apache.catalina.AccessLog;
 import org.apache.catalina.Globals;
 import org.apache.catalina.connector.Request;
 import org.apache.catalina.connector.Response;
+import org.apache.catalina.util.NetMaskSet;
 import org.apache.juli.logging.Log;
 import org.apache.juli.logging.LogFactory;
 import org.apache.tomcat.util.buf.StringUtils;
@@ -90,18 +91,12 @@ import org.apache.tomcat.util.http.parser.Host;
  * </tr>
  * <tr>
  * <td>internalProxies</td>
- * <td>Regular expression that matches the IP addresses of internal proxies. 
If they appear in the
- * <code>remoteIpHeader</code> value, they will be trusted and will not appear 
in the <code>proxiesHeader</code>
- * value</td>
+ * <td>Either a comma separated list of CIDR blocks or a single regular 
expression that matches the IP addresses of
+ * internal proxies. If they appear in the <code>remoteIpHeader</code> value, 
they will be trusted and will not appear
+ * in the <code>proxiesHeader</code> value</td>
  * <td>RemoteIPInternalProxy</td>
- * <td>Regular expression (in the syntax supported by {@link 
java.util.regex.Pattern java.util.regex})</td>
- * <td>10\.\d{1,3}\.\d{1,3}\.\d{1,3}|192\.168\.\d{1,3}\.\d{1,3}|
- * 169\.254\.\d{1,3}\.\d{1,3}|127\.\d{1,3}\.\d{1,3}\.\d{1,3}|
- * 100\.6[4-9]{1}\.\d{1,3}\.\d{1,3}|100\.[7-9]{1}\d{1}\.\d{1,3}\.\d{1,3}|
- * 100\.1[0-1]{1}\d{1}\.\d{1,3}\.\d{1,3}|100\.12[0-7]{1}\.\d{1,3}\.\d{1,3}|
- * 172\.1[6-9]{1}\.\d{1,3}\.\d{1,3}|172\.2[0-9]{1}\.\d{1,3}\.\d{1,3}| 
172\.3[0-1]{1}\.\d{1,3}\.\d{1,3}|
- * 0:0:0:0:0:0:0:1|::1 <br>
- * By default, 10/8, 192.168/16, 169.254/16, 127/8, 100.64/10, 172.16/12, and 
::1 are allowed.</td>
+ * <td>Comma separated list of CIDR blocks or a single regular expression 
{@link Pattern}</td>
+ * 
<td>10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,169.254.0.0/16,100.64.0.0/10,127.0.0.0/8,::1/128,fe80::/10,fc00::/7</td>
  * </tr>
  * <tr>
  * <td>proxiesHeader</td>
@@ -113,10 +108,11 @@ import org.apache.tomcat.util.http.parser.Host;
  * </tr>
  * <tr>
  * <td>trustedProxies</td>
- * <td>Regular expression that matches the IP addresses of trusted proxies. If 
they appear in the
- * <code>remoteIpHeader</code> value, they will be trusted and will appear in 
the <code>proxiesHeader</code> value</td>
+ * <td>Either a comma separated list of CIDR blocks or a single regular 
expression that matches the IP addresses of
+ * internal proxies. If they appear in the <code>remoteIpHeader</code> value, 
they will be trusted and will appear in
+ * the <code>proxiesHeader</code> value</td>
  * <td>RemoteIPTrustedProxy</td>
- * <td>Regular expression (in the syntax supported by {@link 
java.util.regex.Pattern java.util.regex})</td>
+ * <td>Comma separated list of CIDR blocks or a single regular expression 
{@link Pattern}</td>
  * <td>&nbsp;</td>
  * </tr>
  * <tr>
@@ -154,14 +150,6 @@ import org.apache.tomcat.util.http.parser.Host;
  * <p>
  * This Valve may be attached to any Container, depending on the granularity 
of the filtering you wish to perform.
  * </p>
- * <p>
- * <strong>Regular expression vs. IP address blocks:</strong> 
<code>mod_remoteip</code> allows to use address blocks
- * (e.g. <code>192.168/16</code>) to configure 
<code>RemoteIPInternalProxy</code> and <code>RemoteIPTrustedProxy</code>
- * ; as Tomcat doesn't have a library similar to <a href=
- * 
"https://apr.apache.org/docs/apr/1.3/group__apr__network__io.html#gb74d21b8898b7c40bf7fd07ad3eb993d";>apr_ipsubnet_test</a>,
- * <code>RemoteIpValve</code> uses regular expression to configure 
<code>internalProxies</code> and
- * <code>trustedProxies</code> in the same fashion as {@link 
RequestFilterValve} does.
- * </p>
  * <hr>
  * <p>
  * <strong>Sample with internal proxies</strong>
@@ -172,7 +160,7 @@ import org.apache.tomcat.util.http.parser.Host;
  * <code>
  * &lt;Valve
  *   className="org.apache.catalina.valves.RemoteIpValve"
- *   internalProxies="192\.168\.0\.10|192\.168\.0\.11"
+ *   internalProxies="192.168.0.10/31"
  *   remoteIpHeader="x-forwarded-for"
  *   proxiesHeader="x-forwarded-by"
  *   protocolHeader="x-forwarded-proto"
@@ -234,7 +222,7 @@ import org.apache.tomcat.util.http.parser.Host;
  * <code>
  * &lt;Valve
  *   className="org.apache.catalina.valves.RemoteIpValve"
- *   internalProxies="192\.168\.0\.10|192\.168\.0\.11"
+ *   internalProxies="192.168.0.10/31"
  *   remoteIpHeader="x-forwarded-for"
  *   proxiesHeader="x-forwarded-by"
  *   trustedProxies="proxy1|proxy2"
@@ -277,7 +265,7 @@ import org.apache.tomcat.util.http.parser.Host;
  * <code>
  * &lt;Valve
  *   className="org.apache.catalina.valves.RemoteIpValve"
- *   internalProxies="192\.168\.0\.10|192\.168\.0\.11"
+ *   internalProxies="192.168.0.10/31"
  *   remoteIpHeader="x-forwarded-for"
  *   proxiesHeader="x-forwarded-by"
  *   trustedProxies="proxy1|proxy2"
@@ -321,7 +309,7 @@ import org.apache.tomcat.util.http.parser.Host;
  * <code>
  * &lt;Valve
  *   className="org.apache.catalina.valves.RemoteIpValve"
- *   internalProxies="192\.168\.0\.10|192\.168\.0\.11"
+ *   internalProxies="192.168.0.10/31"
  *   remoteIpHeader="x-forwarded-for"
  *   proxiesHeader="x-forwarded-by"
  *   trustedProxies="proxy1|proxy2"
@@ -357,9 +345,7 @@ import org.apache.tomcat.util.http.parser.Host;
  * </p>
  */
 public class RemoteIpValve extends ValveBase {
-    /**
-     * Logger
-     */
+
     private static final Log log = LogFactory.getLog(RemoteIpValve.class);
 
     private String hostHeader = null;
@@ -381,15 +367,16 @@ public class RemoteIpValve extends ValveBase {
     private boolean changeLocalPort = false;
 
     /**
-     * @see #setInternalProxies(String)
+     * Regular expression pattern for internal proxies.
      */
-    private Pattern internalProxies = 
Pattern.compile("10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|" +
-            "192\\.168\\.\\d{1,3}\\.\\d{1,3}|" + 
"169\\.254\\.\\d{1,3}\\.\\d{1,3}|" +
-            "127\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|" + 
"100\\.6[4-9]{1}\\.\\d{1,3}\\.\\d{1,3}|" +
-            "100\\.[7-9]{1}\\d{1}\\.\\d{1,3}\\.\\d{1,3}|" + 
"100\\.1[0-1]{1}\\d{1}\\.\\d{1,3}\\.\\d{1,3}|" +
-            "100\\.12[0-7]{1}\\.\\d{1,3}\\.\\d{1,3}|" + 
"172\\.1[6-9]{1}\\.\\d{1,3}\\.\\d{1,3}|" +
-            "172\\.2[0-9]{1}\\.\\d{1,3}\\.\\d{1,3}|" + 
"172\\.3[0-1]{1}\\.\\d{1,3}\\.\\d{1,3}|" +
-            "0:0:0:0:0:0:0:1|::1|" + "fe[89ab]\\p{XDigit}:.*|" + 
"f[cd]\\p{XDigit}{2}+:.*");
+    private Pattern internalProxiesRegex = null;
+
+    /**
+     * CIDR notation for internal proxies.
+     */
+    private NetMaskSet internalProxiesCidr =
+            
NetMaskSet.parse("10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,169.254.0.0/16,100.64.0.0/10,127.0.0.0/8,"
 +
+                    "::1/128,fe80::/10,fc00::/7");
 
     /**
      * @see #setProtocolHeader(String)
@@ -417,9 +404,14 @@ public class RemoteIpValve extends ValveBase {
     private boolean requestAttributesEnabled = true;
 
     /**
-     * @see RemoteIpValve#setTrustedProxies(String)
+     * Regular expression notation for trusted proxies.
+     */
+    private Pattern trustedProxiesRegex = null;
+
+    /**
+     * CIDR notation for trusted proxies.
      */
-    private Pattern trustedProxies = null;
+    private NetMaskSet trustedProxiesCidr = null;
 
 
     /**
@@ -495,15 +487,18 @@ public class RemoteIpValve extends ValveBase {
     }
 
     /**
-     * @see #setInternalProxies(String)
+     * Obtain the currently configured internal proxies.
      *
-     * @return Regular expression that defines the internal proxies
+     * @return The currently configured internal proxies.
      */
     public String getInternalProxies() {
-        if (internalProxies == null) {
+        if (internalProxiesCidr != null) {
+            return internalProxiesCidr.toString();
+        } else if (internalProxiesRegex != null) {
+            return internalProxiesRegex.toString();
+        } else {
             return null;
         }
-        return internalProxies.toString();
     }
 
     /**
@@ -552,15 +547,18 @@ public class RemoteIpValve extends ValveBase {
     }
 
     /**
-     * @see #setTrustedProxies(String)
+     * Obtain the currently configured trusted proxies.
      *
-     * @return Regular expression that defines the trusted proxies
+     * @return The currently configured trusted proxies.
      */
     public String getTrustedProxies() {
-        if (trustedProxies == null) {
+        if (trustedProxiesCidr != null) {
+            return trustedProxiesCidr.toString();
+        } else if (trustedProxiesRegex != null) {
+            return trustedProxiesRegex.toString();
+        } else {
             return null;
         }
-        return trustedProxies.toString();
     }
 
     @Override
@@ -575,9 +573,10 @@ public class RemoteIpValve extends ValveBase {
         final int originalLocalPort = request.getLocalPort();
         final String originalProxiesHeader = request.getHeader(proxiesHeader);
         final String originalRemoteIpHeader = 
request.getHeader(remoteIpHeader);
-        boolean isInternal = internalProxies != null && 
internalProxies.matcher(originalRemoteAddr).matches();
 
-        if (isInternal || (trustedProxies != null && 
trustedProxies.matcher(originalRemoteAddr).matches())) {
+        boolean isInternal = isInternalProxy(originalRemoteAddr);
+
+        if (isInternal || isTrustedProxy(originalRemoteAddr)) {
             String remoteIp = null;
             Deque<String> proxiesHeaderValue = new ArrayDeque<>();
             StringBuilder concatRemoteIpHeaderValue = new StringBuilder();
@@ -599,9 +598,9 @@ public class RemoteIpValve extends ValveBase {
             for (idx = remoteIpHeaderValue.length - 1; idx >= 0; idx--) {
                 String currentRemoteIp = remoteIpHeaderValue[idx];
                 remoteIp = currentRemoteIp;
-                if (internalProxies != null && 
internalProxies.matcher(currentRemoteIp).matches()) {
+                if (isInternalProxy(currentRemoteIp)) {
                     // do nothing, internalProxies IPs are not appended to the
-                } else if (trustedProxies != null && 
trustedProxies.matcher(currentRemoteIp).matches()) {
+                } else if (isTrustedProxy(currentRemoteIp)) {
                     proxiesHeaderValue.addFirst(currentRemoteIp);
                 } else {
                     idx--; // decrement idx because break statement doesn't do 
it
@@ -746,6 +745,47 @@ public class RemoteIpValve extends ValveBase {
         }
     }
 
+    /**
+     * Checks if the given IP address is from an internal proxy.
+     *
+     * @param remoteIp The IP address to check
+     *
+     * @return {@code true} if the IP address is from an internal proxy, 
otherwise {@code false}
+     */
+    private boolean isInternalProxy(String remoteIp) {
+        if (internalProxiesRegex != null && 
internalProxiesRegex.matcher(remoteIp).matches()) {
+            return true;
+        }
+        return checkIsCidr(internalProxiesCidr, remoteIp);
+    }
+
+    /**
+     * Checks if the given IP address is from a trusted proxy.
+     *
+     * @param remoteIp The IP address to check
+     *
+     * @return {@code true} if the IP address is from a trusted proxy, 
otherwise {@code false}
+     */
+    private boolean isTrustedProxy(String remoteIp) {
+        if (trustedProxiesRegex != null && 
trustedProxiesRegex.matcher(remoteIp).matches()) {
+            return true;
+        }
+
+        return checkIsCidr(trustedProxiesCidr, remoteIp);
+    }
+
+    private boolean checkIsCidr(NetMaskSet netMaskSet, String remoteIp) {
+        if (netMaskSet == null) {
+            return false;
+        }
+        try {
+            return netMaskSet.contains(remoteIp);
+        } catch (UnknownHostException uhe) {
+            log.debug(sm.getString("remoteIpFilter.invalidRemoteAddress", 
remoteIp), uhe);
+        }
+        return false;
+    }
+
     /*
      * Considers the value to be secure if it exclusively holds forwards for 
{@link #protocolHeaderHttpsValue}.
      */
@@ -814,21 +854,20 @@ public class RemoteIpValve extends ValveBase {
     }
 
     /**
-     * <p>
-     * Regular expression that defines the internal proxies.
-     * </p>
-     * <p>
-     * Default value :
-     * 
10\.\d{1,3}\.\d{1,3}\.\d{1,3}|192\.168\.\d{1,3}\.\d{1,3}|169\.254.\d{1,3}.\d{1,3}|127\.\d{1,3}\.\d{1,3}\.\d{1,3}|0:0:0:0:0:0:0:1
-     * </p>
+     * Set the internal proxies either as a comma separated list of CIDR 
blocks or a single regular expression.
      *
-     * @param internalProxies The proxy regular expression
+     * @param internalProxies The new internal proxies
      */
     public void setInternalProxies(String internalProxies) {
         if (internalProxies == null || internalProxies.isEmpty()) {
-            this.internalProxies = null;
+            this.internalProxiesRegex = null;
+            this.internalProxiesCidr = null;
+        } else if (internalProxies.indexOf('/') > 0) {
+            this.internalProxiesRegex = null;
+            this.internalProxiesCidr = NetMaskSet.parse(internalProxies);
         } else {
-            this.internalProxies = Pattern.compile(internalProxies);
+            this.internalProxiesRegex = Pattern.compile(internalProxies);
+            this.internalProxiesCidr = null;
         }
     }
 
@@ -921,20 +960,20 @@ public class RemoteIpValve extends ValveBase {
     }
 
     /**
-     * <p>
-     * Regular expression defining proxies that are trusted when they appear 
in the {@link #remoteIpHeader} header.
-     * </p>
-     * <p>
-     * Default value : empty list, no external proxy is trusted.
-     * </p>
+     * Set the trusted proxies either as a comma separated list of CIDR blocks 
or a single regular expression.
      *
-     * @param trustedProxies The regular expression
+     * @param trustedProxies The new trusted proxies
      */
     public void setTrustedProxies(String trustedProxies) {
         if (trustedProxies == null || trustedProxies.isEmpty()) {
-            this.trustedProxies = null;
+            this.trustedProxiesRegex = null;
+            this.trustedProxiesCidr = null;
+        } else if (trustedProxies.indexOf('/') > 0) {
+            this.trustedProxiesRegex = null;
+            this.trustedProxiesCidr = NetMaskSet.parse(trustedProxies);
         } else {
-            this.trustedProxies = Pattern.compile(trustedProxies);
+            this.trustedProxiesCidr = null;
+            this.trustedProxiesRegex = Pattern.compile(trustedProxies);
         }
     }
 }
diff --git a/java/org/apache/catalina/valves/mbeans-descriptors.xml 
b/java/org/apache/catalina/valves/mbeans-descriptors.xml
index 62907785c7..6b389d8752 100644
--- a/java/org/apache/catalina/valves/mbeans-descriptors.xml
+++ b/java/org/apache/catalina/valves/mbeans-descriptors.xml
@@ -554,7 +554,7 @@
                type="java.lang.String"/>
 
     <attribute name="internalProxies"
-               description="Regular expression that matches IP addresses of 
internal proxies"
+               description="Either a comma separated list of CIDR blocks or a 
single regular expression that matches the IP addresses of internal proxies."
                type="java.lang.String"/>
 
     <attribute name="portHeader"
@@ -587,7 +587,7 @@
                writeable="false"/>
 
     <attribute name="trustedProxies"
-               description="Regular expression that matches IP addresses of 
trusted proxies"
+               description="Either a comma separated list of CIDR blocks or a 
single regular expression that matches the IP addresses of trusted proxies."
                type="java.lang.String"/>
 
   </mbean>
diff --git a/test/org/apache/catalina/filters/TestRemoteIpFilter.java 
b/test/org/apache/catalina/filters/TestRemoteIpFilter.java
index 2cfcd1e224..5596722c12 100644
--- a/test/org/apache/catalina/filters/TestRemoteIpFilter.java
+++ b/test/org/apache/catalina/filters/TestRemoteIpFilter.java
@@ -20,6 +20,7 @@ import java.io.IOException;
 import java.io.PrintWriter;
 import java.net.HttpURLConnection;
 import java.net.URI;
+import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -48,6 +49,7 @@ import org.apache.catalina.connector.Connector;
 import org.apache.catalina.connector.Request;
 import org.apache.catalina.startup.Tomcat;
 import org.apache.catalina.startup.TomcatBaseTest;
+import org.apache.catalina.util.NetMaskSet;
 import org.apache.tomcat.unittest.TesterContext;
 import org.apache.tomcat.unittest.TesterResponse;
 import org.apache.tomcat.util.buf.StringUtils;
@@ -842,8 +844,16 @@ public class TestRemoteIpFilter extends TomcatBaseTest {
     }
 
     @Test
-    public void testInternalProxies() throws Exception {
+    public void testInternalProxiesRegex() throws Exception {
         RemoteIpFilter remoteIpFilter = new RemoteIpFilter();
+        // Regex equivalent of default
+        
remoteIpFilter.setInternalProxies("10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|" +
+                "192\\.168\\.\\d{1,3}\\.\\d{1,3}|" + 
"169\\.254\\.\\d{1,3}\\.\\d{1,3}|" +
+                "127\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|" + 
"100\\.6[4-9]{1}\\.\\d{1,3}\\.\\d{1,3}|" +
+                "100\\.[7-9]{1}\\d{1}\\.\\d{1,3}\\.\\d{1,3}|" + 
"100\\.1[0-1]{1}\\d{1}\\.\\d{1,3}\\.\\d{1,3}|" +
+                "100\\.12[0-7]{1}\\.\\d{1,3}\\.\\d{1,3}|" + 
"172\\.1[6-9]{1}\\.\\d{1,3}\\.\\d{1,3}|" +
+                "172\\.2[0-9]{1}\\.\\d{1,3}\\.\\d{1,3}|" + 
"172\\.3[0-1]{1}\\.\\d{1,3}\\.\\d{1,3}|" +
+                "0:0:0:0:0:0:0:1|::1|" + "fe[89ab]\\p{XDigit}:.*|" + 
"f[cd]\\p{XDigit}{2}+:.*");
         @SuppressWarnings("deprecation")
         Pattern internalProxiesPattern = 
Pattern.compile(remoteIpFilter.getInternalProxiesAsString());
 
@@ -888,4 +898,95 @@ public class TestRemoteIpFilter extends TomcatBaseTest {
         boolean match = pattern.matcher(input).matches();
         Assert.assertEquals(input, Boolean.valueOf(expectedMatch), 
Boolean.valueOf(match));
     }
+
+    @Test
+    public void testInvokeAllowedRemoteAddrWithNullRemoteIpHeaderCidr() throws 
Exception {
+        // PREPARE
+        FilterDef filterDef = new FilterDef();
+        filterDef.addInitParameter("internalProxies", "192.168.0.0/24");
+        filterDef.addInitParameter("trustedProxies", "203.0.113.0/24");
+        filterDef.addInitParameter("remoteIpHeader", "x-forwarded-for");
+        filterDef.addInitParameter("proxiesHeader", "x-forwarded-by");
+
+        MockHttpServletRequest request = new MockHttpServletRequest();
+        request.setRemoteAddr("192.168.0.10");
+        request.setRemoteHost("remote-host-original-value");
+
+        // TEST
+        HttpServletRequest actualRequest = testRemoteIpFilter(filterDef, 
request).getRequest();
+
+        // VERIFY
+        String actualXForwardedFor = request.getHeader("x-forwarded-for");
+        Assert.assertNull("x-forwarded-for must be null", actualXForwardedFor);
+
+        String actualXForwardedBy = request.getHeader("x-forwarded-by");
+        Assert.assertNull("x-forwarded-by must be null", actualXForwardedBy);
+
+        String actualRemoteAddr = actualRequest.getRemoteAddr();
+        Assert.assertEquals("remoteAddr", "192.168.0.10", actualRemoteAddr);
+
+        String actualRemoteHost = actualRequest.getRemoteHost();
+        Assert.assertEquals("remoteHost", "remote-host-original-value", 
actualRemoteHost);
+    }
+
+    @Test
+    public void testInternalProxiesCidrWithNull() throws Exception {
+        RemoteIpFilter remoteIpFilter = new RemoteIpFilter();
+        try {
+            // Multiple invalid CIDR formats
+            remoteIpFilter.setInternalProxies("192.168.0.0/33,2001:db8::/129");
+            Assert.fail("Expected IllegalArgumentException was not thrown");
+        } catch (IllegalArgumentException e) {
+            // Test passes - exception was thrown as expected
+            Assert.assertNotNull("Exception message should not be null", 
e.getMessage());
+        }
+    }
+
+    @Test
+    public void testInternalProxiesCidr() throws Exception {
+        RemoteIpFilter remoteIpFilter = new RemoteIpFilter();
+        @SuppressWarnings("deprecation")
+        NetMaskSet netMaskSet = 
NetMaskSet.parse(remoteIpFilter.getInternalProxiesAsString());
+
+        doTestNetMaskSet(netMaskSet, "8.8.8.8", false);
+        doTestNetMaskSet(netMaskSet, "100.62.0.0", false);
+        doTestNetMaskSet(netMaskSet, "100.63.255.255", false);
+        doTestNetMaskSet(netMaskSet, "100.64.0.0", true);
+        doTestNetMaskSet(netMaskSet, "100.65.0.0", true);
+        doTestNetMaskSet(netMaskSet, "100.68.0.0", true);
+        doTestNetMaskSet(netMaskSet, "100.72.0.0", true);
+        doTestNetMaskSet(netMaskSet, "100.88.0.0", true);
+        doTestNetMaskSet(netMaskSet, "100.95.0.0", true);
+        doTestNetMaskSet(netMaskSet, "100.102.0.0", true);
+        doTestNetMaskSet(netMaskSet, "100.110.0.0", true);
+        doTestNetMaskSet(netMaskSet, "100.126.0.0", true);
+        doTestNetMaskSet(netMaskSet, "100.127.255.255", true);
+        doTestNetMaskSet(netMaskSet, "100.128.0.0", false);
+        doTestNetMaskSet(netMaskSet, "100.130.0.0", false);
+        // Bug 69600 - IPv6 RFC 4193 Unique Local IPv6 Unicast Addresses
+        doTestNetMaskSet(netMaskSet, 
"fe79:ffff:ffff:ffff:ffff:ffff:ffff:ffff", false);
+        doTestNetMaskSet(netMaskSet, 
"fe80:0000:0000:0000:0000:0000:0000:0000", true);
+        doTestNetMaskSet(netMaskSet, "fe80::", true);
+        doTestNetMaskSet(netMaskSet, 
"fe80:0000:0000:0000:0000:0000:0000:0001", true);
+        doTestNetMaskSet(netMaskSet, "fe80::1", true);
+        doTestNetMaskSet(netMaskSet, 
"fe80:1234:5678:9abc:def0:1234:5678:9abc", true);
+        doTestNetMaskSet(netMaskSet, 
"febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff", true);
+        doTestNetMaskSet(netMaskSet, 
"fec0:0000:0000:0000:0000:0000:0000:0000", false);
+        doTestNetMaskSet(netMaskSet, "fec0::", false);
+        // Bug 69600 - IPv6 RFC 4291 Link Local IPv6 Unicast Addresses
+        doTestNetMaskSet(netMaskSet, 
"fbff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", false);
+        doTestNetMaskSet(netMaskSet, 
"fc00:0000:0000:0000:0000:0000:0000:0000", true);
+        doTestNetMaskSet(netMaskSet, "fc00::", true);
+        doTestNetMaskSet(netMaskSet, 
"fc00:0000:0000:0000:0000:0000:0000:0001", true);
+        doTestNetMaskSet(netMaskSet, "fc00::1", true);
+        doTestNetMaskSet(netMaskSet, 
"fc00:1234:5678:9abc:def0:1234:5678:9abc", true);
+        doTestNetMaskSet(netMaskSet, 
"fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", true);
+        doTestNetMaskSet(netMaskSet, 
"fe00:0000:0000:0000:0000:0000:0000:0000", false);
+        doTestNetMaskSet(netMaskSet, "fe00::", false);
+    }
+
+    private void doTestNetMaskSet(NetMaskSet netMaskSet, String remoteIp, 
boolean expectedMatch) throws UnknownHostException {
+        boolean match = netMaskSet.contains(remoteIp);
+        Assert.assertEquals(remoteIp, Boolean.valueOf(expectedMatch), 
Boolean.valueOf(match));
+    }
 }
diff --git a/test/org/apache/catalina/valves/TestRemoteIpValve.java 
b/test/org/apache/catalina/valves/TestRemoteIpValve.java
index f394b24ec4..4d35fb55cc 100644
--- a/test/org/apache/catalina/valves/TestRemoteIpValve.java
+++ b/test/org/apache/catalina/valves/TestRemoteIpValve.java
@@ -17,6 +17,7 @@
 package org.apache.catalina.valves;
 
 import java.io.IOException;
+import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -32,6 +33,7 @@ import org.apache.catalina.Globals;
 import org.apache.catalina.connector.Connector;
 import org.apache.catalina.connector.Request;
 import org.apache.catalina.connector.Response;
+import org.apache.catalina.util.NetMaskSet;
 import org.apache.tomcat.util.buf.StringUtils;
 
 /**
@@ -1178,6 +1180,14 @@ public class TestRemoteIpValve {
     @Test
     public void testInternalProxies() throws Exception {
         RemoteIpValve remoteIpValve = new RemoteIpValve();
+        // Regex equivalent of default
+        
remoteIpValve.setInternalProxies("10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|" +
+                "192\\.168\\.\\d{1,3}\\.\\d{1,3}|" + 
"169\\.254\\.\\d{1,3}\\.\\d{1,3}|" +
+                "127\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|" + 
"100\\.6[4-9]{1}\\.\\d{1,3}\\.\\d{1,3}|" +
+                "100\\.[7-9]{1}\\d{1}\\.\\d{1,3}\\.\\d{1,3}|" + 
"100\\.1[0-1]{1}\\d{1}\\.\\d{1,3}\\.\\d{1,3}|" +
+                "100\\.12[0-7]{1}\\.\\d{1,3}\\.\\d{1,3}|" + 
"172\\.1[6-9]{1}\\.\\d{1,3}\\.\\d{1,3}|" +
+                "172\\.2[0-9]{1}\\.\\d{1,3}\\.\\d{1,3}|" + 
"172\\.3[0-1]{1}\\.\\d{1,3}\\.\\d{1,3}|" +
+                "0:0:0:0:0:0:0:1|::1|" + "fe[89ab]\\p{XDigit}:.*|" + 
"f[cd]\\p{XDigit}{2}+:.*");
         Pattern internalProxiesPattern = 
Pattern.compile(remoteIpValve.getInternalProxies());
 
         doTestPattern(internalProxiesPattern, "8.8.8.8", false);
@@ -1221,4 +1231,71 @@ public class TestRemoteIpValve {
         boolean match = pattern.matcher(input).matches();
         Assert.assertEquals(input, Boolean.valueOf(expectedMatch), 
Boolean.valueOf(match));
     }
+
+    @Test
+    public void testInvokeAllowedRemoteAddrWithNullRemoteIpHeaderCidr() throws 
Exception {
+        // PREPARE
+        RemoteIpValve remoteIpValve = new RemoteIpValve();
+        remoteIpValve.setInternalProxies("");
+        remoteIpValve.setTrustedProxies("");
+        remoteIpValve.setInternalProxies("192.168.0.0/24");
+        remoteIpValve.setTrustedProxies("203.0.113.0/24");
+        remoteIpValve.setRemoteIpHeader("x-forwarded-for");
+        remoteIpValve.setProxiesHeader("x-forwarded-by");
+
+        RemoteAddrAndHostTrackerValve remoteAddrAndHostTrackerValve = new 
RemoteAddrAndHostTrackerValve();
+        remoteIpValve.setNext(remoteAddrAndHostTrackerValve);
+
+        Request request = new MockRequest(new org.apache.coyote.Request());
+        request.setRemoteAddr("192.168.0.10");
+        request.setRemoteHost("remote-host-original-value");
+
+        // TEST
+        remoteIpValve.invoke(request, null);
+
+        // VERIFY
+        String actualXForwardedFor = request.getHeader("x-forwarded-for");
+        Assert.assertNull("x-forwarded-for must be null", actualXForwardedFor);
+
+        String actualXForwardedBy = request.getHeader("x-forwarded-by");
+        Assert.assertNull("x-forwarded-by must be null", actualXForwardedBy);
+
+        String actualRemoteAddr = 
remoteAddrAndHostTrackerValve.getRemoteAddr();
+        Assert.assertEquals("remoteAddr", "192.168.0.10", actualRemoteAddr);
+
+        String actualRemoteHost = 
remoteAddrAndHostTrackerValve.getRemoteHost();
+        Assert.assertEquals("remoteHost", "remote-host-original-value", 
actualRemoteHost);
+    }
+
+    @Test
+    public void testInternalProxiesCidr() throws Exception {
+        RemoteIpValve remoteIpValve = new RemoteIpValve();
+        remoteIpValve.setInternalProxies("192.168.0.0/24,223.168.0.0/24");
+        NetMaskSet internalProxiesCidr = 
NetMaskSet.parse(remoteIpValve.getInternalProxies());
+
+        doTestNetMaskSet(internalProxiesCidr, "192.168.0.0", true);
+        doTestNetMaskSet(internalProxiesCidr, "192.168.0.1", true);
+        doTestNetMaskSet(internalProxiesCidr, "193.168.1.0", false);
+        doTestNetMaskSet(internalProxiesCidr, "223.168.0.0", true);
+        doTestNetMaskSet(internalProxiesCidr, "223.168.0.123", true);
+        doTestNetMaskSet(internalProxiesCidr, "223.168.1.123", false);
+    }
+
+    @Test
+    public void testInternalProxiesCidrWithNull() throws Exception {
+        RemoteIpValve remoteIpValve = new RemoteIpValve();
+        try {
+            // Multiple invalid CIDR formats
+            remoteIpValve.setInternalProxies("192.168.0.0/33,2001:db8::/129");
+            Assert.fail("Expected IllegalArgumentException was not thrown");
+        } catch (IllegalArgumentException e) {
+            // Test passes - exception was thrown as expected
+            Assert.assertNotNull("Exception message should not be null", 
e.getMessage());
+        }
+    }
+
+    private void doTestNetMaskSet(NetMaskSet netMaskSet, String remoteIp, 
boolean expectedMatch) throws UnknownHostException {
+        boolean match = netMaskSet.contains(remoteIp);
+        Assert.assertEquals(remoteIp, Boolean.valueOf(expectedMatch), 
Boolean.valueOf(match));
+    }
 }
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 937f7e33f5..3752ad9d69 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -105,6 +105,15 @@
   issues do not "pop up" wrt. others).
 -->
 <section name="Tomcat 11.0.13 (markt)" rtext="in development">
+  <subsection name="Catalina">
+    <changelog>
+      <add>
+        Add CIDR support for the configuration of internal and trusted proxies
+        for the <code>RemoteIpFilter</code> and <code>RemoteIpValve</code>.
+        (markt)
+      </add>
+    </changelog>
+  </subsection>
 </section>
 <section name="Tomcat 11.0.12 (markt)" rtext="release in progress">
   <subsection name="Catalina">
diff --git a/webapps/docs/config/filter.xml b/webapps/docs/config/filter.xml
index c3d1c98bf4..b7f6d27798 100644
--- a/webapps/docs/config/filter.xml
+++ b/webapps/docs/config/filter.xml
@@ -1460,7 +1460,7 @@ FINE: Request "/docs/config/manager.html" with response 
status "200"
        <filter-class>org.apache.catalina.filters.RemoteIpFilter</filter-class>
        <init-param>
          <param-name>internalProxies</param-name>
-         <param-value>192\.168\.0\.10|192\.168\.0\.11</param-value>
+         <param-value>192.168.0.10/31</param-value>
        </init-param>
        <init-param>
          <param-name>remoteIpHeader</param-name>
@@ -1541,7 +1541,7 @@ FINE: Request "/docs/config/manager.html" with response 
status "200"
        <filter-class>org.apache.catalina.filters.RemoteIpFilter</filter-class>
        <init-param>
          <param-name>internalProxies</param-name>
-         <param-value>192\.168\.0\.10|192\.168\.0\.11</param-value>
+         <param-value>192.168.0.10/31</param-value>
        </init-param>
        <init-param>
          <param-name>remoteIpHeader</param-name>
@@ -1601,7 +1601,7 @@ FINE: Request "/docs/config/manager.html" with response 
status "200"
        <filter-class>org.apache.catalina.filters.RemoteIpFilter</filter-class>
        <init-param>
          <param-name>internalProxies</param-name>
-         <param-value>192\.168\.0\.10|192\.168\.0\.11</param-value>
+         <param-value>192.168.0.10/31</param-value>
        </init-param>
        <init-param>
          <param-name>remoteIpHeader</param-name>
@@ -1664,7 +1664,7 @@ FINE: Request "/docs/config/manager.html" with response 
status "200"
        <filter-class>org.apache.catalina.filters.RemoteIpFilter</filter-class>
        <init-param>
          <param-name>internalProxies</param-name>
-         <param-value>192\.168\.0\.10|192\.168\.0\.11</param-value>
+         <param-value>192.168.0.10/31</param-value>
        </init-param>
        <init-param>
          <param-name>remoteIpHeader</param-name>
@@ -1739,12 +1739,13 @@ FINE: Request "/docs/config/manager.html" with response 
status "200"
       </attribute>
 
       <attribute name="internalProxies" required="false">
-        <p>Regular expression (using <code>java.util.regex</code>) that a
-        proxy&apos;s IP address must match to be considered an internal proxy.
-        Internal proxies that appear in the <strong>remoteIpHeader</strong> 
will
-        be trusted and will not appear in the <strong>proxiesHeader</strong>
-        value. If not specified the default value of <code>
-        
10\.\d{1,3}\.\d{1,3}\.\d{1,3}|192\.168\.\d{1,3}\.\d{1,3}|169\.254\.\d{1,3}\.\d{1,3}|127\.\d{1,3}\.\d{1,3}\.\d{1,3}|100\.6[4-9]{1}\.\d{1,3}\.\d{1,3}|100\.[7-9]{1}\d{1}\.\d{1,3}\.\d{1,3}|100\.1[0-1]{1}\d{1}\.\d{1,3}\.\d{1,3}|100\.12[0-7]{1}\.\d{1,3}\.\d{1,3}|172\.1[6-9]{1}\.\d{1,3}\.\d{1,3}|172\.2[0-9]{1}\.\d{1,3}\.\d{1,3}|172\.3[0-1]{1}\.\d{1,3}\.\d{1,3}|0:0:0:0:0:0:0:1|::1|fe[89ab]\p{XDigit}:.*|"f[cd]\p{XDigit}{2}+:.*
+        <p>Either a comma separated list of CIDR blocks or a single regular
+        expression that a proxy&apos;s IP address must match to be considered 
an
+        internal proxy. Internal proxies that appear in the
+        <strong>remoteIpHeader</strong> will be trusted and will not appear in
+        the <strong>proxiesHeader</strong> value. If not specified the default
+        value of <code>10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16,
+        169.254.0.0/16, 100.64.0.0/10, 127.0.0.0/8, ::1/128, fe80::/10, 
fc00::/7
         </code> will be used.</p>
       </attribute>
 
@@ -1765,11 +1766,12 @@ FINE: Request "/docs/config/manager.html" with response 
status "200"
       </attribute>
 
       <attribute name="trustedProxies" required="false">
-        <p>Regular expression (using <code>java.util.regex</code>) that a
-        proxy&apos;s IP address must match to be considered an trusted proxy.
-        Trusted proxies that appear in the <strong>remoteIpHeader</strong> will
-        be trusted and will appear in the <strong>proxiesHeader</strong> value.
-        If not specified, no proxies will be trusted.</p>
+        <p>Either a comma separated list of CIDR blocks or a single regular
+        expression that a proxy&apos;s IP address must match to be considered a
+        trusted proxy. Trusted proxies that appear in the
+        <strong>remoteIpHeader</strong> will be trusted and will appear in the
+        <strong>proxiesHeader</strong> value. If not specified, no proxies will
+        be trusted.</p>
       </attribute>
 
       <attribute name="protocolHeader" required="false">
diff --git a/webapps/docs/config/valve.xml b/webapps/docs/config/valve.xml
index 4e570fc9c0..c78669b195 100644
--- a/webapps/docs/config/valve.xml
+++ b/webapps/docs/config/valve.xml
@@ -1190,12 +1190,13 @@
       </attribute>
 
       <attribute name="internalProxies" required="false">
-        <p>Regular expression (using <code>java.util.regex</code>) that a
-        proxy&apos;s IP address must match to be considered an internal proxy.
-        Internal proxies that appear in the <strong>remoteIpHeader</strong> 
will
-        be trusted and will not appear in the <strong>proxiesHeader</strong>
-        value. If not specified the default value of <code>
-        
10\.\d{1,3}\.\d{1,3}\.\d{1,3}|192\.168\.\d{1,3}\.\d{1,3}|169\.254\.\d{1,3}\.\d{1,3}|127\.\d{1,3}\.\d{1,3}\.\d{1,3}|100\.6[4-9]{1}\.\d{1,3}\.\d{1,3}|100\.[7-9]{1}\d{1}\.\d{1,3}\.\d{1,3}|100\.1[0-1]{1}\d{1}\.\d{1,3}\.\d{1,3}|100\.12[0-7]{1}\.\d{1,3}\.\d{1,3}|172\.1[6-9]{1}\.\d{1,3}\.\d{1,3}|172\.2[0-9]{1}\.\d{1,3}\.\d{1,3}|172\.3[0-1]{1}\.\d{1,3}\.\d{1,3}|0:0:0:0:0:0:0:1|::1|fe[89ab]\p{XDigit}:.*|"f[cd]\p{XDigit}{2}+:.*
+        <p>Either a comma separated list of CIDR blocks or a single regular
+        expression that a proxy&apos;s IP address must match to be considered 
an
+        internal proxy. Internal proxies that appear in the
+        <strong>remoteIpHeader</strong> will be trusted and will not appear in
+        the <strong>proxiesHeader</strong> value. If not specified the default
+        value of <code>10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16,
+        169.254.0.0/16, 100.64.0.0/10, 127.0.0.0/8, ::1/128, fe80::/10, 
fc00::/7
         </code> will be used.</p>
       </attribute>
 
@@ -1216,11 +1217,12 @@
       </attribute>
 
       <attribute name="trustedProxies" required="false">
-        <p>Regular expression (using <code>java.util.regex</code>) that a
-        proxy&apos;s IP address must match to be considered an trusted proxy.
-        Trusted proxies that appear in the <strong>remoteIpHeader</strong> will
-        be trusted and will appear in the <strong>proxiesHeader</strong> value.
-        If not specified, no proxies will be trusted.</p>
+        <p>Either a comma separated list of CIDR blocks or a single regular
+        expression that a proxy&apos;s IP address must match to be considered a
+        trusted proxy. Trusted proxies that appear in the
+        <strong>remoteIpHeader</strong> will be trusted and will appear in the
+        <strong>proxiesHeader</strong> value. If not specified, no proxies will
+        be trusted.</p>
       </attribute>
 
       <attribute name="protocolHeader" required="false">


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to