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> </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;
*
<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>
@@ -279,7 +268,7 @@ import org.apache.tomcat.util.res.StringManager;
*
<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>
@@ -343,7 +332,7 @@ import org.apache.tomcat.util.res.StringManager;
*
<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>
@@ -408,7 +397,7 @@ import org.apache.tomcat.util.res.StringManager;
*
<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>
@@ -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> </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>
* <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>
* <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>
* <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>
* <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'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'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'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'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'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'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'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'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]