You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by rj...@apache.org on 2020/12/04 13:01:14 UTC

[tomcat] branch master updated: Let the RemoteCIDRValve inherit from RequestFilterValve and support all of its features. Especially add support for connector specific configuration using "addConnectorPort".

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

rjung pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/tomcat.git


The following commit(s) were added to refs/heads/master by this push:
     new 74f4740  Let the RemoteCIDRValve inherit from RequestFilterValve and support all of its features. Especially add support for connector specific configuration using "addConnectorPort".
74f4740 is described below

commit 74f474045c8a28747675509c0bae031e939f23f4
Author: Rainer Jung <ra...@kippdata.de>
AuthorDate: Fri Dec 4 13:59:43 2020 +0100

    Let the RemoteCIDRValve inherit from RequestFilterValve
    and support all of its features. Especially add support
    for connector specific configuration using "addConnectorPort".
---
 .../apache/catalina/util/LocalStrings.properties   |   1 +
 java/org/apache/catalina/util/NetMask.java         |  74 ++++++++++++++-
 .../apache/catalina/valves/LocalStrings.properties |   4 +-
 .../apache/catalina/valves/RemoteCIDRValve.java    |  81 +++++++++++++---
 .../apache/catalina/valves/mbeans-descriptors.xml  |  63 +++++++++++++
 test/org/apache/catalina/util/TestNetMask.java     |  36 +++++++-
 .../catalina/valves/TestRequestFilterValve.java    |  46 +++++++++-
 webapps/docs/changelog.xml                         |   6 ++
 webapps/docs/config/valve.xml                      | 102 ++++++++++++++++++---
 9 files changed, 378 insertions(+), 35 deletions(-)

diff --git a/java/org/apache/catalina/util/LocalStrings.properties b/java/org/apache/catalina/util/LocalStrings.properties
index 7c2fe5b..9dcb2ff 100644
--- a/java/org/apache/catalina/util/LocalStrings.properties
+++ b/java/org/apache/catalina/util/LocalStrings.properties
@@ -42,6 +42,7 @@ netmask.cidrNegative=The CIDR [{0}] is negative
 netmask.cidrNotNumeric=The CIDR [{0}] is not numeric
 netmask.cidrTooBig=The CIDR [{0}] is greater than the address length [{1}]
 netmask.invalidAddress=The address [{0}] is not valid
+netmask.invalidPort=The port part in the pattern [{0}] is not valid
 
 parameterMap.locked=No modifications are allowed to a locked ParameterMap
 
diff --git a/java/org/apache/catalina/util/NetMask.java b/java/org/apache/catalina/util/NetMask.java
index fc8e442..fa34065 100644
--- a/java/org/apache/catalina/util/NetMask.java
+++ b/java/org/apache/catalina/util/NetMask.java
@@ -18,6 +18,8 @@ package org.apache.catalina.util;
 
 import java.net.InetAddress;
 import java.net.UnknownHostException;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
 
 import org.apache.tomcat.util.res.StringManager;
 
@@ -72,6 +74,16 @@ public final class NetMask {
      */
     private final int lastByteShift;
 
+    /**
+     * Should we use the port pattern when matching
+     */
+    private final boolean foundPort;
+
+    /**
+     * The regular expression used to test for the server port (optional).
+     */
+    private final Pattern portPattern;
+
 
     /**
      * Constructor
@@ -84,16 +96,36 @@ public final class NetMask {
 
         expression = input;
 
-        final int idx = input.indexOf("/");
+        final int portIdx = input.indexOf(";");
+        final String nonPortPart;
+
+        if (portIdx == -1) {
+            foundPort = false;
+            nonPortPart = input;
+            portPattern = null;
+        } else {
+            foundPort = true;
+            nonPortPart = input.substring(0, portIdx);
+            try {
+                portPattern = Pattern.compile(input.substring(portIdx + 1));
+            } catch (PatternSyntaxException e) {
+                /*
+                 * In case of error never match any non-empty port given
+                 */
+                throw new IllegalArgumentException(sm.getString("netmask.invalidPort", input), e);
+            }
+        }
+
+        final int idx = nonPortPart.indexOf("/");
 
         /*
          * Handle the "IP only" case first
          */
         if (idx == -1) {
             try {
-                netaddr = InetAddress.getByName(input).getAddress();
+                netaddr = InetAddress.getByName(nonPortPart).getAddress();
             } catch (UnknownHostException e) {
-                throw new IllegalArgumentException(sm.getString("netmask.invalidAddress", input));
+                throw new IllegalArgumentException(sm.getString("netmask.invalidAddress", nonPortPart));
             }
             nrBytes = netaddr.length;
             lastByteShift = 0;
@@ -105,7 +137,7 @@ public final class NetMask {
          * and the CIDR.
          */
 
-        final String addressPart = input.substring(0, idx), cidrPart = input.substring(idx + 1);
+        final String addressPart = nonPortPart.substring(0, idx), cidrPart = nonPortPart.substring(idx + 1);
 
         try {
             /*
@@ -158,12 +190,46 @@ public final class NetMask {
 
 
     /**
+     * Test if a given address and port matches this netmask.
+     *
+     * @param addr The {@link java.net.InetAddress} to test
+     * @param port The port to test
+     * @return true on match, false otherwise
+     */
+    public boolean matches(final InetAddress addr, int port) {
+        if (!foundPort) {
+            return false;
+        }
+        final String portString = Integer.toString(port);
+        if (!portPattern.matcher(portString).matches()) {
+            return false;
+        }
+        return matches(addr, true);
+    }
+
+
+    /**
      * Test if a given address matches this netmask.
      *
      * @param addr The {@link java.net.InetAddress} to test
      * @return true on match, false otherwise
      */
     public boolean matches(final InetAddress addr) {
+        return matches(addr, false);
+    }
+
+
+    /**
+     * Test if a given address matches this netmask.
+     *
+     * @param addr The {@link java.net.InetAddress} to test
+     * @param checkedPort Indicates, whether we already checked the port
+     * @return true on match, false otherwise
+     */
+    public boolean matches(final InetAddress addr, boolean checkedPort) {
+        if (!checkedPort && foundPort) {
+            return false;
+        }
         final byte[] candidate = addr.getAddress();
 
         /*
diff --git a/java/org/apache/catalina/valves/LocalStrings.properties b/java/org/apache/catalina/valves/LocalStrings.properties
index 25f8332..111dd4d 100644
--- a/java/org/apache/catalina/valves/LocalStrings.properties
+++ b/java/org/apache/catalina/valves/LocalStrings.properties
@@ -130,7 +130,9 @@ jdbcAccessLogValve.close=Failed to close database
 jdbcAccessLogValve.exception=Exception performing insert access entry
 
 remoteCidrValve.invalid=Invalid configuration provided for [{0}]. See previous messages for details.
+remoteCidrValve.noPort=Request does not contain a valid server port. Request denied.
 remoteCidrValve.noRemoteIp=Client does not have an IP address. Request denied.
+remoteCidrValve.unexpectedPort=Request contains server port, although connector configuration attribute addConnectorPort is false. Request denied.
 
 remoteIpValve.invalidHostHeader=Invalid value [{0}] found for Host in HTTP header [{1}]
 remoteIpValve.invalidHostWithPort=Host value [{0}] in HTTP header [{1}] included a port number which will be ignored
@@ -147,4 +149,4 @@ stuckThreadDetectionValve.notifyStuckThreadCompleted=Thread [{0}] (id=[{3}]) was
 stuckThreadDetectionValve.notifyStuckThreadDetected=Thread [{0}] (id=[{6}]) has been active for [{1}] milliseconds (since [{2}]) to serve the same request for [{4}] and may be stuck (configured threshold for this StuckThreadDetectionValve is [{5}] seconds). There is/are [{3}] thread(s) in total that are monitored by this Valve and may be stuck.
 stuckThreadDetectionValve.notifyStuckThreadInterrupted=Thread [{0}] (id=[{5}]) has been interrupted because it was active for [{1}] milliseconds (since [{2}]) to serve the same request for [{3}] and was probably stuck (configured interruption threshold for this StuckThreadDetectionValve is [{4}] seconds).
 
-persistentValve.filter.failure=Unable to compile filter=[{0}]
\ No newline at end of file
+persistentValve.filter.failure=Unable to compile filter=[{0}]
diff --git a/java/org/apache/catalina/valves/RemoteCIDRValve.java b/java/org/apache/catalina/valves/RemoteCIDRValve.java
index 20a1b74..4565c71 100644
--- a/java/org/apache/catalina/valves/RemoteCIDRValve.java
+++ b/java/org/apache/catalina/valves/RemoteCIDRValve.java
@@ -25,7 +25,6 @@ import java.util.LinkedList;
 import java.util.List;
 
 import jakarta.servlet.ServletException;
-import jakarta.servlet.http.HttpServletResponse;
 
 import org.apache.catalina.connector.Request;
 import org.apache.catalina.connector.Response;
@@ -33,7 +32,7 @@ import org.apache.catalina.util.NetMask;
 import org.apache.juli.logging.Log;
 import org.apache.juli.logging.LogFactory;
 
-public final class RemoteCIDRValve extends ValveBase {
+public final class RemoteCIDRValve extends RequestFilterValve {
 
     /**
      * Our logger
@@ -52,7 +51,6 @@ public final class RemoteCIDRValve extends ValveBase {
 
 
     public RemoteCIDRValve() {
-        super(true);
     }
 
 
@@ -62,6 +60,7 @@ public final class RemoteCIDRValve extends ValveBase {
      * @return the #allow list as a string, without the leading '[' and trailing
      *         ']'
      */
+    @Override
     public String getAllow() {
         return allow.toString().replace("[", "").replace("]", "");
     }
@@ -74,6 +73,7 @@ public final class RemoteCIDRValve extends ValveBase {
      * @param input The list of netmasks, as a comma separated string
      * @throws IllegalArgumentException One or more netmasks are invalid
      */
+    @Override
     public void setAllow(final String input) {
         final List<String> messages = fillFromInput(input, allow);
 
@@ -81,6 +81,7 @@ public final class RemoteCIDRValve extends ValveBase {
             return;
         }
 
+        allowValid = false;
         for (final String message : messages) {
             log.error(message);
         }
@@ -95,6 +96,7 @@ public final class RemoteCIDRValve extends ValveBase {
      * @return the #deny list as a string, without the leading '[' and trailing
      *         ']'
      */
+    @Override
     public String getDeny() {
         return deny.toString().replace("[", "").replace("]", "");
     }
@@ -107,7 +109,7 @@ public final class RemoteCIDRValve extends ValveBase {
      * @param input The list of netmasks, as a comma separated string
      * @throws IllegalArgumentException One or more netmasks are invalid
      */
-
+    @Override
     public void setDeny(final String input) {
         final List<String> messages = fillFromInput(input, deny);
 
@@ -115,6 +117,7 @@ public final class RemoteCIDRValve extends ValveBase {
             return;
         }
 
+        denyValid = false;
         for (final String message : messages) {
             log.error(message);
         }
@@ -125,19 +128,49 @@ public final class RemoteCIDRValve extends ValveBase {
 
     @Override
     public void invoke(final Request request, final Response response) throws IOException, ServletException {
-
-        if (isAllowed(request.getRequest().getRemoteAddr())) {
-            getNext().invoke(request, response);
+        String property;
+        if (getAddConnectorPort()) {
+            property = request.getRequest().getRemoteAddr() + ";" +
+                    request.getConnector().getPortWithOffset();
         } else {
-            response.sendError(HttpServletResponse.SC_FORBIDDEN);
+            property = request.getRequest().getRemoteAddr();
         }
+        process(property, request, response);
     }
 
-    private boolean isAllowed(final String property) {
-        final InetAddress addr;
+    @Override
+    public boolean isAllowed(final String property) {
+
+        final int portIdx = property.indexOf(";");
+        final int port;
+        final String nonPortPart;
 
+        if (portIdx == -1) {
+            if (getAddConnectorPort()) {
+                log.error(sm.getString("remoteCidrValve.noPort"));
+                return false;
+            }
+            port = -1;
+            nonPortPart = property;
+        } else {
+            if (!getAddConnectorPort()) {
+                log.error(sm.getString("remoteCidrValve.unexpectedPort"));
+                return false;
+            }
+            nonPortPart = property.substring(0, portIdx);
+            try {
+                port = Integer.parseInt(property.substring(portIdx + 1));
+            } catch (NumberFormatException e) {
+                // This should be in the 'could never happen' category but handle it
+                // to be safe.
+                log.error(sm.getString("remoteCidrValve.noPort"), e);
+                return false;
+            }
+        }
+
+        final InetAddress addr;
         try {
-            addr = InetAddress.getByName(property);
+            addr = InetAddress.getByName(nonPortPart);
         } catch (UnknownHostException e) {
             // This should be in the 'could never happen' category but handle it
             // to be safe.
@@ -146,14 +179,26 @@ public final class RemoteCIDRValve extends ValveBase {
         }
 
         for (final NetMask nm : deny) {
-            if (nm.matches(addr)) {
-                return false;
+            if (getAddConnectorPort()) {
+                if (nm.matches(addr, port)) {
+                    return false;
+                }
+            } else {
+                if (nm.matches(addr)) {
+                    return false;
+                }
             }
         }
 
         for (final NetMask nm : allow) {
-            if (nm.matches(addr)) {
-                return true;
+            if (getAddConnectorPort()) {
+                if (nm.matches(addr, port)) {
+                    return true;
+                }
+            } else {
+                if (nm.matches(addr)) {
+                    return true;
+                }
             }
         }
 
@@ -167,6 +212,12 @@ public final class RemoteCIDRValve extends ValveBase {
     }
 
 
+    @Override
+    protected Log getLog() {
+        return log;
+    }
+
+
     /**
      * Fill a {@link NetMask} list from a string input containing a
      * comma-separated list of (hopefully valid) {@link NetMask}s.
diff --git a/java/org/apache/catalina/valves/mbeans-descriptors.xml b/java/org/apache/catalina/valves/mbeans-descriptors.xml
index 41d1178..1fd880e 100644
--- a/java/org/apache/catalina/valves/mbeans-descriptors.xml
+++ b/java/org/apache/catalina/valves/mbeans-descriptors.xml
@@ -457,6 +457,69 @@
     </operation>
   </mbean>
 
+  <mbean name="RemoteCIDRValve"
+         description="Concrete implementation of RequestFilterValve that filters based on the string representation of the remote client's network address in CIDR format"
+         domain="Catalina"
+         group="Valve"
+         type="org.apache.catalina.valves.RemoteCIDRValve">
+
+    <attribute name="addConnectorPort"
+               description="Append the server connector port to the client network CIDR separated by a semicolon"
+               type="boolean"/>
+
+    <attribute name="allow"
+               description="The allow expression"
+               type="java.lang.String"/>
+
+    <attribute name="allowValid"
+               description="Becomes false if assigned value of allow expression is not syntactically correct"
+               is="true"
+               type="boolean"
+               writeable="false"/>
+
+    <attribute name="asyncSupported"
+               description="Does this valve support async reporting."
+               is="true"
+               type="boolean"/>
+
+    <attribute name="className"
+               description="Fully qualified class name of the managed object"
+               type="java.lang.String"
+               writeable="false"/>
+
+    <attribute name="deny"
+               description="The deny expression"
+               type="java.lang.String"/>
+
+    <attribute name="denyStatus"
+               description="HTTP response status code that is used when rejecting denied request"
+               type="int"/>
+
+    <attribute name="denyValid"
+               description="Becomes false if assigned value of deny expression is not syntactically correct"
+               is="true"
+               type="boolean"
+               writeable="false"/>
+
+    <attribute name="invalidAuthenticationWhenDeny"
+               description="Send an invalid authentication header instead of deny"
+               type="boolean"/>
+
+    <attribute name="stateName"
+               description="The name of the LifecycleState that this component is currently in"
+               type="java.lang.String"
+               writeable="false"/>
+
+    <operation name="isAllowed"
+               description="Tests whether a client with this host name is allowed access by the current valve configuration"
+               impact="INFO"
+               returnType="boolean">
+      <parameter name="hostName"
+          description="host name to be tested"
+                 type="java.lang.String"/>
+    </operation>
+  </mbean>
+
   <mbean name="RemoteIpValve"
          description="Valve that sets client information (eg IP address) based on data from a trusted proxy"
          domain="Catalina"
diff --git a/test/org/apache/catalina/util/TestNetMask.java b/test/org/apache/catalina/util/TestNetMask.java
index 2260a24..b67ae54 100644
--- a/test/org/apache/catalina/util/TestNetMask.java
+++ b/test/org/apache/catalina/util/TestNetMask.java
@@ -61,6 +61,9 @@ public final class TestNetMask {
         result.add(new Object[] { "ae31::27:ef2:1/-1", null, Boolean.FALSE, null });
         result.add(new Object[] { "ae31::27:ef2:1/129", null, Boolean.FALSE, null });
 
+        // Invalid port regex suffix after ";"
+        result.add(new Object[] { "1.2.3.4;[", null, Boolean.FALSE, null });
+
         // IPv4
         result.add(new Object[] { "1.2.3.4", "1.2.3.4", Boolean.TRUE, Boolean.TRUE });
 
@@ -98,6 +101,16 @@ public final class TestNetMask {
         // Mixed
         result.add(new Object[] { "10.0.0.0/22", "::1", Boolean.TRUE, Boolean.FALSE });
 
+        // port
+        result.add(new Object[] { "1.2.3.4;8080", "1.2.3.4", Boolean.TRUE, Boolean.FALSE });
+        result.add(new Object[] { "1.2.3.4", "1.2.3.4;8080", Boolean.TRUE, Boolean.FALSE });
+        result.add(new Object[] { "1.2.3.4;", "1.2.3.4;8080", Boolean.TRUE, Boolean.FALSE });
+        result.add(new Object[] { "1.2.3.4;8080", "1.2.3.4;8080", Boolean.TRUE, Boolean.TRUE });
+        result.add(new Object[] { "1.2.3.4;8080", "1.2.3.4;8009", Boolean.TRUE, Boolean.FALSE });
+        result.add(new Object[] { "1.2.3.4;.*", "1.2.3.4;8080", Boolean.TRUE, Boolean.TRUE });
+        result.add(new Object[] { "1.2.3.4;8\\d+", "1.2.3.4;8080", Boolean.TRUE, Boolean.TRUE });
+        result.add(new Object[] { "1.2.3.4;8\\d+", "1.2.3.4;9090", Boolean.TRUE, Boolean.FALSE });
+
         return result;
     }
 
@@ -109,7 +122,7 @@ public final class TestNetMask {
         try {
             netMask = new NetMask(mask);
         } catch (Exception e) {
-            exception =e;
+            exception = e;
         }
 
         if (valid.booleanValue()) {
@@ -122,15 +135,32 @@ public final class TestNetMask {
             return;
         }
 
+        final int portIdx = input.indexOf(";");
+        final boolean usePort = portIdx >= 0 || mask.indexOf(";") >= 0;
+        final int port;
+        final String nonPortPart;
+
+        if (portIdx == -1) {
+            port = -1;
+            nonPortPart = input;
+        } else {
+            port = Integer.parseInt(input.substring(portIdx + 1));
+            nonPortPart = input.substring(0, portIdx);
+        }
+
         InetAddress inetAddress = null;
         try {
-            inetAddress = InetAddress.getByName(input);
+            inetAddress = InetAddress.getByName(nonPortPart);
         } catch (UnknownHostException e) {
             e.printStackTrace();
             Assert.fail();
         }
 
-        Assert.assertEquals(matches, Boolean.valueOf(netMask.matches(inetAddress)));
+        if (usePort) {
+            Assert.assertEquals(matches, Boolean.valueOf(netMask.matches(inetAddress, port)));
+        } else {
+            Assert.assertEquals(matches, Boolean.valueOf(netMask.matches(inetAddress)));
+        }
 
         Assert.assertEquals(mask, netMask.toString());
     }
diff --git a/test/org/apache/catalina/valves/TestRequestFilterValve.java b/test/org/apache/catalina/valves/TestRequestFilterValve.java
index eeae0dc..22703f9 100644
--- a/test/org/apache/catalina/valves/TestRequestFilterValve.java
+++ b/test/org/apache/catalina/valves/TestRequestFilterValve.java
@@ -52,6 +52,20 @@ public class TestRequestFilterValve {
     private static final String HOST_ALLOW_AND_DENY   = "www.example.org";
     private static final String HOST_NO_ALLOW_NO_DENY = "host.example.com";
 
+    private static final String CIDR_ALLOW_PROP       = "127.0.0.0/16";
+    private static final String CIDR_DENY_PROP        = "192.168.0.0/24,127.0.0.0/24";
+    private static final String CIDR_ONLY_ALLOW       = "127.0.1.1";
+    private static final String CIDR_ONLY_DENY        = "192.168.0.1";
+    private static final String CIDR_ALLOW_AND_DENY   = "127.0.0.1";
+    private static final String CIDR_NO_ALLOW_NO_DENY = "192.168.1.1";
+
+    private static final String CIDR6_ALLOW_PROP       = "::/96";
+    private static final String CIDR6_DENY_PROP        = "::f:0:0/112,::/112";
+    private static final String CIDR6_ONLY_ALLOW       = "0:0:0:0:0:0:148f:1";
+    private static final String CIDR6_ONLY_DENY        = "0:0:0:0:0:F:0:a";
+    private static final String CIDR6_ALLOW_AND_DENY   = "0:0:0:0:0:0:0:fA8";
+    private static final String CIDR6_NO_ALLOW_NO_DENY = "1:0:0:0:0:0:0:1";
+
     private static final int PORT = 8080;
     private static final String PORT_MATCH_PATTERN    = ";\\d*";
     private static final String PORT_NO_MATCH_PATTERN = ";8081";
@@ -103,6 +117,10 @@ public class TestRequestFilterValve {
                 valve = new RemoteHostValve();
                 request.setRemoteHost(property);
                 msg.append(" host='" + property + "'");
+            } else if (type.equals("CIDR")) {
+                valve = new RemoteCIDRValve();
+                request.setRemoteAddr(property);
+                msg.append(" ip='" + property + "'");
             }
         }
         Assert.assertNotNull("Invalid test type" + type, valve);
@@ -128,8 +146,10 @@ public class TestRequestFilterValve {
                 ((RemoteAddrValve)valve).setAddConnectorPort(true);
             } else if (valve instanceof RemoteHostValve) {
                 ((RemoteHostValve)valve).setAddConnectorPort(true);
+            } else if (valve instanceof RemoteCIDRValve) {
+                ((RemoteCIDRValve)valve).setAddConnectorPort(true);
             } else {
-                Assert.fail("Can only set 'addConnectorPort' for RemoteAddrValve and RemoteHostValve");
+                Assert.fail("Can only set 'addConnectorPort' for RemoteAddrValve, RemoteHostValve and RemoteCIDRValve");
             }
             msg.append(" addConnectorPort='true'");
         }
@@ -340,4 +360,28 @@ public class TestRequestFilterValve {
                       HOST_ALLOW_AND_DENY, HOST_NO_ALLOW_NO_DENY,
                       true, "Host");
     }
+
+    @Test
+    public void testRemoteCIDRValve() {
+        standardTests(CIDR_ALLOW_PROP, CIDR_DENY_PROP,
+                      CIDR_ONLY_ALLOW, CIDR_ONLY_DENY,
+                      CIDR_ALLOW_AND_DENY, CIDR_NO_ALLOW_NO_DENY,
+                      false, "CIDR");
+        standardTests(CIDR_ALLOW_PROP, CIDR_DENY_PROP,
+                      CIDR_ONLY_ALLOW, CIDR_ONLY_DENY,
+                      CIDR_ALLOW_AND_DENY, CIDR_NO_ALLOW_NO_DENY,
+                      true, "CIDR");
+    }
+
+    @Test
+    public void testRemoteCIDR6Valve() {
+        standardTests(CIDR6_ALLOW_PROP, CIDR6_DENY_PROP,
+                      CIDR6_ONLY_ALLOW, CIDR6_ONLY_DENY,
+                      CIDR6_ALLOW_AND_DENY, CIDR6_NO_ALLOW_NO_DENY,
+                      false, "CIDR");
+        standardTests(CIDR6_ALLOW_PROP, CIDR6_DENY_PROP,
+                      CIDR6_ONLY_ALLOW, CIDR6_ONLY_DENY,
+                      CIDR6_ALLOW_AND_DENY, CIDR6_NO_ALLOW_NO_DENY,
+                      true, "CIDR");
+    }
 }
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 0f89120..2fe02eb 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -111,6 +111,12 @@
         attributes that provide details of the protocols and ciphers requested
         by a client in the initial TLS handshake. (markt)
       </add>
+      <add>
+        Let the <code>RemoteCIDRValve</code> inherit from
+        <code>RequestFilterValve</code> and support all of its features.
+        Especially add support for connector specific configuration
+        using <code>addConnectorPort</code>. (rjung)
+      </add>
     </changelog>
   </subsection>
 </section>
diff --git a/webapps/docs/config/valve.xml b/webapps/docs/config/valve.xml
index f84a952..58ccd2c 100644
--- a/webapps/docs/config/valve.xml
+++ b/webapps/docs/config/valve.xml
@@ -514,10 +514,16 @@
     package. Please consult the Java documentation for details of the
     expressions supported.</p>
 
-    <p>Optionally one can append the server connector port separated with a
+    <p>After setting the attribute <code>addConnectorPort</code> to
+    <code>true</code>, one can append the server connector port separated with a
     semicolon (";") to allow different expressions for each connector.</p>
 
-    <p>The behavior when a request is refused can be changed
+    <p>A refused request will be answered a response with status code
+    <code>403</code>. This status code can be overwritten using the attribute
+    <code>denyStatus</code>.</p>
+
+    <p>By setting the attribute <code>invalidAuthenticationWhenDeny</code> to
+    <code>true</code>, the behavior when a request is refused can be changed
     to not deny but instead set an invalid <code>authentication</code>
     header. This is useful in combination with the context attribute
     <code>preemptiveAuthentication="true"</code>.</p>
@@ -531,7 +537,9 @@
     <code>::1</code>. Consult your access logs for the actual value.</p>
 
     <p>See also: <a href="#Remote_Host_Valve">Remote Host Valve</a>,
-    <a href="#Remote_IP_Valve">Remote IP Valve</a>.</p>
+    <a href="#Remote_CIDR_Valve">Remote CIDR Valve</a>,
+    <a href="#Remote_IP_Valve">Remote IP Valve</a>,
+    <a href="http.html">HTTP Connector</a> configuration.</p>
   </subsection>
 
   <subsection name="Attributes">
@@ -643,10 +651,16 @@
     package. Please consult the Java documentation for details of the
     expressions supported.</p>
 
-    <p>Optionally one can append the server connector port separated with a
+    <p>After setting the attribute <code>addConnectorPort</code> to
+    <code>true</code>, one can append the server connector port separated with a
     semicolon (";") to allow different expressions for each connector.</p>
 
-    <p>The behavior when a request is refused can be changed
+    <p>A refused request will be answered a response with status code
+    <code>403</code>. This status code can be overwritten using the attribute
+    <code>denyStatus</code>.</p>
+
+    <p>By setting the attribute <code>invalidAuthenticationWhenDeny</code> to
+    <code>true</code>, the behavior when a request is refused can be changed
     to not deny but instead set an invalid <code>authentication</code>
     header. This is useful in combination with the context attribute
     <code>preemptiveAuthentication="true"</code>.</p>
@@ -657,6 +671,8 @@
     a <strong>Connector</strong>.</p>
 
     <p>See also: <a href="#Remote_Address_Valve">Remote Address Valve</a>,
+    <a href="#Remote_CIDR_Valve">Remote CIDR Valve</a>,
+    <a href="#Remote_IP_Valve">Remote IP Valve</a>,
     <a href="http.html">HTTP Connector</a> configuration.</p>
   </subsection>
 
@@ -757,6 +773,20 @@
       </li>
     </ul>
 
+    <p>After setting the attribute <code>addConnectorPort</code> to
+    <code>true</code>, one can append the server connector port separated with a
+    semicolon (";") to allow different expressions for each connector.</p>
+
+    <p>A refused request will be answered a response with status code
+    <code>403</code>. This status code can be overwritten using the attribute
+    <code>denyStatus</code>.</p>
+
+    <p>By setting the attribute <code>invalidAuthenticationWhenDeny</code> to
+    <code>true</code>, the behavior when a request is refused can be changed
+    to not deny but instead set an invalid <code>authentication</code>
+    header. This is useful in combination with the context attribute
+    <code>preemptiveAuthentication="true"</code>.</p>
+
     <p>Some more features of this valve are:
     </p>
 
@@ -768,6 +798,10 @@
       <code>fe80::/71</code>, etc).</li>
     </ul>
 
+    <p>See also: <a href="#Remote_Address_Valve">Remote Address Valve</a>,
+    <a href="#Remote_Host_Valve">Remote Host Valve</a>,
+    <a href="#Remote_IP_Valve">Remote IP Valve</a>,
+    <a href="http.html">HTTP Connector</a> configuration.</p>
   </subsection>
 
   <subsection name="Attributes">
@@ -802,20 +836,66 @@
         </p>
       </attribute>
 
+      <attribute name="denyStatus" required="false">
+        <p>HTTP response status code that is used when rejecting denied
+        request. The default value is <code>403</code>. For example,
+        it can be set to the value <code>404</code>.</p>
+      </attribute>
+
+      <attribute name="addConnectorPort" required="false">
+        <p>Append the server connector port to the client IP address separated
+        with a semicolon (";"). If this is set to <code>true</code>, the
+        expressions configured with <code>allow</code> and
+        <code>deny</code> is compared against <code>ADDRESS;PORT</code>
+        where <code>ADDRESS</code> is the client IP address and
+        <code>PORT</code> is the Tomcat connector port which received the
+        request. The default value is <code>false</code>.</p>
+      </attribute>
+
+      <attribute name="invalidAuthenticationWhenDeny" required="false">
+        <p>When a request should be denied, do not deny but instead
+        set an invalid <code>authentication</code> header. This only works
+        if the context has the attribute <code>preemptiveAuthentication="true"</code>
+        set. An already existing <code>authentication</code> header will not be
+        overwritten. In effect this will trigger authentication instead of deny
+        even if the application does not have a security constraint configured.</p>
+        <p>This can be combined with <code>addConnectorPort</code> to trigger authentication
+        depending on the client and the connector that is used to access an application.</p>
+      </attribute>
+
     </attributes>
 
   </subsection>
 
-  <subsection name="Example">
+  <subsection name="Example 1" anchor="Remote_CIDR_Valve/Example_localhost">
     <p>To allow access only for the clients connecting from localhost:</p>
-    <pre>
-      &lt;Valve className="org.apache.catalina.valves.RemoteCIDRValve"
-      allow="127.0.0.1, ::1"/&gt;
-    </pre>
+    <source><![CDATA[<Valve className="org.apache.catalina.valves.RemoteCIDRValve"
+   allow="127.0.0.1, ::1"/>]]></source>
   </subsection>
 
-</subsection>
+  <subsection name="Example 2" anchor="Remote_CIDR_Valve/Example_localhost_port">
+    <p>To allow unrestricted access for the clients connecting from the local network
+    but for all clients in network 10. only to port 8443:</p>
+    <source><![CDATA[<Valve className="org.apache.catalina.valves.RemoteCIDRValve"
+   addConnectorPort="true"
+   allow="127.0.0.1;\d*|::1;\d*|10.0.0.0/8;8443"/>]]></source>
+  </subsection>
 
+  <subsection name="Example 3" anchor="Remote_CIDR_Valve/Example_port_auth">
+    <p>To allow access to port 8009 from network 10., but trigger basic
+    authentication if the application is accessed on another port:</p>
+<source><![CDATA[<Context>
+  ...
+  <Valve className="org.apache.catalina.valves.RemoteCIDRValve"
+         addConnectorPort="true"
+         invalidAuthenticationWhenDeny="true"
+         allow="10.0.0.0/8;8009"/>
+  <Valve className="org.apache.catalina.authenticator.BasicAuthenticator" />
+  ...
+</Context>]]></source>
+  </subsection>
+
+</subsection>
 
 </section>
 


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