View | Details | Raw Unified | Return to bug 51953
Collapse All | Expand All

(-)a/java/org/apache/catalina/filters/RemoteCIDRFilter.java (+241 lines)
Line 0 Link Here
1
package org.apache.catalina.filters;
2
3
import org.apache.catalina.comet.CometEvent;
4
import org.apache.catalina.comet.CometFilter;
5
import org.apache.catalina.comet.CometFilterChain;
6
import org.apache.catalina.util.NetMask;
7
import org.apache.juli.logging.Log;
8
import org.apache.juli.logging.LogFactory;
9
10
import javax.servlet.FilterChain;
11
import javax.servlet.ServletException;
12
import javax.servlet.ServletRequest;
13
import javax.servlet.ServletResponse;
14
import javax.servlet.http.HttpServletResponse;
15
import java.io.IOException;
16
import java.io.PrintWriter;
17
import java.net.InetAddress;
18
import java.net.UnknownHostException;
19
import java.util.ArrayList;
20
import java.util.Collections;
21
import java.util.LinkedList;
22
import java.util.List;
23
24
public final class RemoteCIDRFilter
25
    extends FilterBase
26
    implements CometFilter {
27
28
    /**
29
     * text/plain MIME type: this is the MIME type we return when a
30
     * {@link ServletResponse} is not an {@link HttpServletResponse}
31
     */
32
33
    private static final String PLAIN_TEXT_MIME_TYPE = "text/plain";
34
35
    /**
36
     * Our logger
37
     */
38
39
    private static final Log log = LogFactory.getLog(RemoteCIDRFilter.class);
40
41
    /**
42
     * The list of allowed {@link NetMask}s
43
     */
44
45
    private final List<NetMask> allow = new ArrayList<NetMask>();
46
47
    /**
48
     * The list of denied {@link NetMask}s
49
     */
50
51
    private final List<NetMask> deny = new ArrayList<NetMask>();
52
53
    /**
54
     * Return a string representation of the {@link NetMask} list in #allow.
55
     *
56
     * @return the #allow list as a string, without the leading '[' and
57
     * trailing ']'
58
     */
59
60
    public String getAllow() {
61
        return allow.toString().replace("[", "").replace("]", "");
62
    }
63
64
65
    /**
66
     * Fill the #allow list with the list of netmasks provided as an argument,
67
     * if any. Calls #fillFromInput.
68
     *
69
     * @param input The list of netmasks, as a comma separated string
70
     * @throws IllegalArgumentException One or more netmasks are invalid
71
     */
72
73
    public void setAllow(final String input) {
74
        final List<String> messages = fillFromInput(input, allow);
75
76
        if (messages.isEmpty())
77
            return;
78
79
        for (final String message:messages)
80
            log.error(message);
81
82
        throw new IllegalArgumentException("Filter error, see messages above");
83
    }
84
85
86
    /**
87
     * Return a string representation of the {@link NetMask} list in #deny.
88
     *
89
     * @return the #deny list as string, without the leading '[' and trailing
90
     * ']'
91
     */
92
93
    public String getDeny() {
94
        return deny.toString().replace("[", "").replace("]", "");
95
    }
96
97
98
    /**
99
     * Fill the #deny list with the list of netmasks provided as an argument,
100
     * if any. Calls #fillFromInput.
101
     *
102
     * @param input The list of netmasks, as a comma separated string
103
     * @throws IllegalArgumentException One or more netmasks are invalid
104
     */
105
106
    public void setDeny(final String input) {
107
        final List<String> messages = fillFromInput(input, deny);
108
109
        if (messages.isEmpty())
110
            return;
111
112
        for (final String message: messages)
113
            log.error(message);
114
115
        throw new IllegalArgumentException("Filter error: illegal netmask(s) " +
116
            "in allow, see messages above");
117
    }
118
119
    @Override
120
    public void doFilterEvent(CometEvent event, CometFilterChain chain)
121
        throws IOException, ServletException {
122
        processCometEvent(event.getHttpServletRequest().getRemoteHost(),
123
            event, chain);
124
    }
125
126
    @Override
127
    public void doFilter(final ServletRequest request,
128
        final ServletResponse response, final FilterChain chain)
129
        throws IOException, ServletException {
130
        process(request.getRemoteAddr(), request, response, chain);
131
    }
132
133
    public void processCometEvent(final String property, final CometEvent event,
134
        final CometFilterChain chain)
135
        throws IOException, ServletException {
136
        HttpServletResponse response = event.getHttpServletResponse();
137
138
        if (isAllowed(property)) {
139
            chain.doFilterEvent(event);
140
            return;
141
        }
142
143
        response.sendError(HttpServletResponse.SC_FORBIDDEN);
144
        event.close();
145
    }
146
147
    public void process(final String property, final ServletRequest request,
148
        final ServletResponse response, final FilterChain chain)
149
        throws IOException, ServletException {
150
151
        if (isAllowed(property)) {
152
            chain.doFilter(request, response);
153
            return;
154
        }
155
156
        if (!(response instanceof HttpServletResponse)) {
157
            sendErrorWhenNotHttp(response);
158
            return;
159
        }
160
161
        ((HttpServletResponse) response)
162
            .sendError(HttpServletResponse.SC_FORBIDDEN);
163
    }
164
165
    @Override
166
    public Log getLogger() {
167
        return log;
168
    }
169
170
    /**
171
     * Test if a remote's IP address is allowed to proceed.
172
     *
173
     * @param property The remote's IP address, as a string
174
     * @return true if allowed
175
     */
176
177
    private boolean isAllowed(final String property) {
178
        final InetAddress addr;
179
180
        try {
181
            addr = InetAddress.getByName(property);
182
        } catch (UnknownHostException e) {
183
            //Eh?
184
            log.error("Eh? Our remote doesn't even have a valid IP address? ",
185
                e);
186
            return false;
187
        }
188
189
        for (final NetMask nm: deny)
190
            if (nm.matches(addr))
191
                return false;
192
193
        for (final NetMask nm: allow)
194
            if (nm.matches(addr))
195
                return true;
196
197
        // Allow if deny is specified but allow isn't
198
        if (!deny.isEmpty() && allow.isEmpty())
199
            return true;
200
201
        // Deny this request
202
        return false;
203
    }
204
205
    private void sendErrorWhenNotHttp(ServletResponse response)
206
        throws IOException {
207
        final PrintWriter writer = response.getWriter();
208
        response.setContentType(PLAIN_TEXT_MIME_TYPE);
209
        writer.write(sm.getString("http.403"));
210
        writer.flush();
211
    }
212
213
    /**
214
     * Fill a {@link NetMask} list from a string input containing a
215
     * comma-separated list of (hopefully valid) {@link NetMask}s.
216
     *
217
     * @param input The input string
218
     * @param victim The list to fill
219
     * @return a string list of processing errors (empty when no errors)
220
     */
221
222
    private List<String> fillFromInput(final String input,
223
        final List<NetMask> victim) {
224
        victim.clear();
225
        if (input == null || input.isEmpty())
226
            return Collections.emptyList();
227
228
        final List<String> messages = new LinkedList<String>();
229
        NetMask nm;
230
231
        for (final String s: input.split("\\s*,\\s*"))
232
            try {
233
                nm = new NetMask(s);
234
                victim.add(nm);
235
            } catch (IllegalArgumentException e) {
236
                messages.add(s + ": " + e.getMessage());
237
            }
238
239
        return Collections.unmodifiableList(messages);
240
    }
241
}
(-)a/java/org/apache/catalina/util/NetMask.java (+213 lines)
Line 0 Link Here
1
package org.apache.catalina.util;
2
3
import java.net.InetAddress;
4
import java.net.UnknownHostException;
5
6
/**
7
 * A class representing a CIDR netmask.
8
 *
9
 * <p>The constructor takes a string as an argument which represents a
10
 * netmask, as per the CIDR notation -- whether this netmask be IPv4 or
11
 * IPv6. It then extracts the network address (before the /) and the CIDR
12
 * prefix (after the /), and tells through the #matches() method whether a
13
 * candidate {@link InetAddress} object fits in the recorded range.</p>
14
 *
15
 * <p>As byte arrays as returned by <code>InetAddress.getByName()</code> are
16
 * always in network byte order, finding a match is therefore as simple as
17
 * testing whether the n first bits (where n is the CIDR) are the same in both
18
 * byte arrays (the one of the network address and the one of the candidate
19
 * address). We do that by first doing byte comparisons, then testing the last
20
 * bits if any (that is, if the remainder of the integer division of the CIDR
21
 * by 8 is not 0).</p>
22
 *
23
 * <p>As a bonus, if no / is found in the input, it is assumed that an exact
24
 * address match is required.</p>
25
 */
26
27
public final class NetMask {
28
    /**
29
     * The argument to the constructor, used for .toString()
30
     */
31
    private final String expression;
32
33
    /**
34
     * The byte array representing the address extracted from the expression
35
     */
36
    private final byte[] netaddr;
37
38
    /**
39
     * The number of bytes to test for equality (CIDR / 8)
40
     */
41
    private final int nrBytes;
42
43
    /**
44
     * The right shift to apply to the last byte if CIDR % 8 is not 0; if it is
45
     * 0, this variable is set to 0
46
     */
47
    private final int lastByteShift;
48
49
    /**
50
     * Constructor
51
     *
52
     * @param input the CIDR netmask
53
     * @throws IllegalArgumentException if the netmask is not correct
54
     * (invalid address specification, malformed CIDR prefix, etc)
55
     */
56
57
    public NetMask(final String input) {
58
59
        expression = input;
60
61
        final int idx = input.indexOf("/");
62
63
        /*
64
         * Handle the "IP only" case first
65
         */
66
        if (idx == -1) {
67
            try {
68
                netaddr = InetAddress.getByName(input).getAddress();
69
            } catch (UnknownHostException e) {
70
                throw new IllegalArgumentException("invalid address "
71
                    + "specification");
72
            }
73
            nrBytes = netaddr.length;
74
            lastByteShift = 0;
75
            return;
76
        }
77
78
        /*
79
         * OK, we do have a netmask specified, so let's extract both the
80
         * address and the CIDR.
81
         */
82
83
        final String addressPart = input.substring(0, idx),
84
            cidrPart = input.substring(idx + 1);
85
86
        try {
87
            /*
88
             * The address first...
89
             */
90
            netaddr = InetAddress.getByName(addressPart).getAddress();
91
        } catch (UnknownHostException e) {
92
            throw new IllegalArgumentException("invalid address "
93
                + "specification");
94
        }
95
96
        final int addrlen = netaddr.length * 8;
97
        final int cidr;
98
99
        try {
100
            /*
101
             * And then the CIDR.
102
             */
103
            cidr = Integer.parseInt(cidrPart);
104
        } catch (NumberFormatException e) {
105
            throw new IllegalArgumentException("CIDR is not a number");
106
        }
107
108
        /*
109
        * We don't want a negative CIDR, nor do we want a CIDR which is
110
        * greater than the address length (consider 0.0.0.0/33, or ::/129)
111
        */
112
        if (cidr < 0)
113
            throw new IllegalArgumentException("CIDR is negative");
114
        if (cidr > addrlen)
115
            throw new IllegalArgumentException("CIDR is greater than address "
116
                + "length");
117
118
        nrBytes = cidr / 8;
119
120
        /*
121
         * These last two lines could be shortened to:
122
         *
123
         * lastByteShift = (8 - (cidr % 8)) & 7;
124
         *
125
         * But... It's not worth it. In fact, explaining why it could work
126
         * would be too long to be worth the trouble, so let's do it the simple
127
         * way...
128
         */
129
130
        final int remainder = cidr % 8;
131
132
        lastByteShift = (remainder == 0) ? 0 : 8 - remainder;
133
    }
134
135
    /**
136
     * Test if a given address matches this netmask
137
     *
138
     * @param addr The {@link java.net.InetAddress} to test
139
     * @return true on match, false otherwise
140
     */
141
142
    public boolean matches (final InetAddress addr) {
143
        final byte[] candidate = addr.getAddress();
144
145
        /*
146
         * OK, remember that a CIDR prefix tells the number of BITS which
147
         * should be equal between this NetMask's recorded address (netaddr)
148
         * and the candidate address. One byte is 8 bits, no matter what,
149
         * and IP addresses, whether they be IPv4 or IPv6, are big endian,
150
         * aka MSB, Most Significant Byte (first).
151
         *
152
         * We therefore need to get the byte array of the candidate address,
153
         * compare as many bytes of the candidate address with the recorded
154
         * address as the CIDR prefix tells us to (that is, CIDR / 8),
155
         * and then deal with the remaining bits -- if any.
156
         *
157
         * But prior to that, a simple test can be done: we deal with IP
158
         * addresses here, which means IPv4 and IPv6. IPv4 addresses are
159
         * encoded on 4 bytes, IPv6 addresses are encoded on 16 bytes. If the
160
         * candidate address length is different than this NetMask's
161
         * address, we don't have a match.
162
         */
163
        if (candidate.length != netaddr.length)
164
            return false;
165
166
        int i;
167
168
        /*
169
         * Now do the byte-compare. The constructor has recorded the number
170
         * of bytes to compare in nrBytes, use that. If any of the byte we have
171
         * to compare is different than what we expect, we don't have a match.
172
         *
173
         * If, on the opposite, after this loop, all bytes have been deemed
174
         * equal, then the loop variable i will point to the byte right after
175
          * that -- which we will need...
176
         */
177
        for (i = 0; i < nrBytes; i++)
178
            if (netaddr[i] != candidate[i])
179
                return false;
180
181
        /*
182
         * ... if there are bits left to test. There aren't any if
183
         * lastByteShift is set to 0.
184
         */
185
        if (lastByteShift == 0)
186
            return true;
187
188
        /*
189
         * If it is not 0, however, we must test for the relevant bits in the
190
         * next byte (whatever is in the bytes after that doesn't matter). We
191
         * do it this way (remember that lastByteShift contains the amount of
192
         * bits we should _right_ shift the last byte):
193
         *
194
         * - grab both bytes at index i, both from the netmask address and
195
         *   the candidate address;
196
         * - xor them both.
197
         *
198
         * After the xor, it means that all the remaining bits of the CIDR
199
         * should be set to 0...
200
         */
201
        final int lastByte = netaddr[i] ^ candidate[i];
202
203
        /*
204
         * ... Which means that right shifting by lastByteShift should be 0.
205
         */
206
        return (lastByte >> lastByteShift == 0);
207
    }
208
209
    @Override
210
    public String toString() {
211
        return expression;
212
    }
213
}
(-)a/java/org/apache/catalina/valves/RemoteCIDRValve.java (+184 lines)
Line 0 Link Here
1
package org.apache.catalina.valves;
2
3
import org.apache.catalina.connector.Request;
4
import org.apache.catalina.connector.Response;
5
import org.apache.catalina.util.NetMask;
6
import org.apache.juli.logging.Log;
7
import org.apache.juli.logging.LogFactory;
8
9
import javax.servlet.ServletException;
10
import javax.servlet.http.HttpServletResponse;
11
import java.io.IOException;
12
import java.net.InetAddress;
13
import java.util.ArrayList;
14
import java.util.Collections;
15
import java.util.LinkedList;
16
import java.util.List;
17
18
public final class RemoteCIDRValve
19
    extends ValveBase {
20
21
    /**
22
     * What this Valve is to the administrator
23
     */
24
25
    private static final String info
26
        = "org.apache.catalina.valves.RemoteCIDRValve/1.0";
27
28
    /**
29
     * Our logger
30
     */
31
32
    private static final Log log = LogFactory.getLog(RemoteCIDRValve.class);
33
34
    /**
35
     * The list of allowed {@link NetMask}s
36
     */
37
38
    private final List<NetMask> allow = new ArrayList<NetMask>();
39
40
    /**
41
     * The list of denied {@link NetMask}s
42
     */
43
44
    private final List<NetMask> deny = new ArrayList<NetMask>();
45
46
    public RemoteCIDRValve() {
47
        super(true);
48
    }
49
50
    /**
51
     * Return a string representation of the {@link NetMask} list in #allow.
52
     *
53
     * @return the #allow list as a string, without the leading '[' and
54
     * trailing ']'
55
     */
56
57
    public String getAllow() {
58
        return allow.toString().replace("[", "").replace("]", "");
59
    }
60
61
    /**
62
     * Fill the #allow list with the list of netmasks provided as an argument,
63
     * if any. Calls #fillFromInput.
64
     *
65
     * @param input The list of netmasks, as a comma separated string
66
     * @throws IllegalArgumentException One or more netmasks are invalid
67
     */
68
69
    public void setAllow(final String input) {
70
        final List<String> messages = fillFromInput(input, allow);
71
72
        if (messages.isEmpty())
73
            return;
74
75
        for (final String message: messages)
76
            log.error(message);
77
78
        throw new IllegalArgumentException("Valve error: illegal netmask(s) " +
79
            "in allow, see messages above");
80
    }
81
82
    /**
83
     * Return a string representation of the {@link NetMask} list in #deny.
84
     *
85
     * @return the #deny list as a string, without the leading '[' and
86
     * trailing ']'
87
     */
88
89
    public String getDeny() {
90
        return deny.toString().replace("[", "").replace("]", "");
91
    }
92
93
    /**
94
     * Fill the #deny list with the list of netmasks provided as an argument,
95
     * if any. Calls #fillFromInput.
96
     *
97
     * @param input The list of netmasks, as a comma separated string
98
     * @throws IllegalArgumentException One or more netmasks are invalid
99
     */
100
101
    public void setDeny(final String input) {
102
        final List<String> messages = fillFromInput(input, deny);
103
104
        if (messages.isEmpty())
105
            return;
106
107
        for (final String message: messages)
108
            log.error(message);
109
110
        throw new IllegalArgumentException("Valve error: illegal netmask(s) " +
111
            "in deny, see messages above");
112
    }
113
114
115
    /**
116
     * Return descriptive information about this Valve implementation.
117
     */
118
119
    @Override
120
    public String getInfo() {
121
        return info;
122
    }
123
124
    @Override
125
    public void invoke(final Request request, final Response response)
126
        throws IOException, ServletException {
127
        process(request.getRequest().getRemoteAddr(), request, response);
128
    }
129
130
    private void process(final String property, final Request request,
131
        final Response response)
132
        throws IOException, ServletException {
133
134
        final InetAddress addr = InetAddress.getByName(property);
135
136
        for (final NetMask nm : deny)
137
            if (nm.matches(addr)) {
138
                response.sendError(HttpServletResponse.SC_FORBIDDEN);
139
                return;
140
            }
141
142
        if (allow.isEmpty()) {
143
            getNext().invoke(request, response);
144
            return;
145
        }
146
147
        for (final NetMask nm : allow)
148
            if (nm.matches(addr)) {
149
                getNext().invoke(request, response);
150
                return;
151
            }
152
153
        response.sendError(HttpServletResponse.SC_FORBIDDEN);
154
    }
155
156
    /**
157
     * Fill a {@link NetMask} list from a string input containing a
158
     * comma-separated list of (hopefully valid) {@link NetMask}s.
159
     *
160
     * @param input The input string
161
     * @param victim The list to fill
162
     * @return a string list of processing errors (empty when no errors)
163
     */
164
165
    private List<String> fillFromInput(final String input,
166
        final List<NetMask> victim) {
167
        victim.clear();
168
        if (input == null || input.isEmpty())
169
            return Collections.emptyList();
170
171
        final List<String> messages = new LinkedList<String>();
172
        NetMask nm;
173
174
        for (final String s: input.split("\\s*,\\s*"))
175
            try {
176
                nm = new NetMask(s);
177
                victim.add(nm);
178
            } catch (IllegalArgumentException e) {
179
                messages.add(s + ": " + e.getMessage());
180
            }
181
182
        return Collections.unmodifiableList(messages);
183
    }
184
}
(-)a/test/org/apache/catalina/util/TestNetMask.java (+72 lines)
Line 0 Link Here
1
package org.apache.catalina.util;
2
3
import org.junit.Test;
4
5
import static org.junit.Assert.assertEquals;
6
import static org.junit.Assert.fail;
7
8
public final class TestNetMask
9
{
10
11
    @Test
12
    public void testIPV4InitErrors() {
13
        try {
14
            new NetMask("260.1.1.1");
15
            fail("NetMask succeeded with an invalid address!");
16
        } catch (IllegalArgumentException e) {
17
            assertEquals(e.getMessage(), "invalid address specification");
18
        }
19
20
        try {
21
            new NetMask("1.2.3.4/foo");
22
            fail("NetMask succeeded with a non numeric CIDR!");
23
        } catch (IllegalArgumentException e) {
24
            assertEquals(e.getMessage(), "CIDR is not a number");
25
        }
26
27
        try {
28
            new NetMask("1.2.3.4/-1");
29
            fail("NetMask succeeded with a negative CIDR!");
30
        } catch (IllegalArgumentException e) {
31
            assertEquals(e.getMessage(), "CIDR is negative");
32
        }
33
34
        try {
35
            new NetMask("1.2.3.4/33");
36
            fail("NetMask succeeded with CIDR greater than address length!");
37
        } catch (IllegalArgumentException e) {
38
            assertEquals(e.getMessage(), "CIDR is greater than address length");
39
        }
40
    }
41
42
    @Test
43
    public void testIPV6InitErrors() {
44
        try {
45
            new NetMask("fffff::/71");
46
            fail("NetMask succeeded with an invalid address!");
47
        } catch (IllegalArgumentException e) {
48
            assertEquals(e.getMessage(), "invalid address specification");
49
        }
50
51
        try {
52
            new NetMask("ae31::27:ef2:1/foo");
53
            fail("NetMask succeeded with a non numeric CIDR!");
54
        } catch (IllegalArgumentException e) {
55
            assertEquals(e.getMessage(), "CIDR is not a number");
56
        }
57
58
        try {
59
            new NetMask("ae31::27:ef2:1/-1");
60
            fail("NetMask succeeded with a negative CIDR!");
61
        } catch (IllegalArgumentException e) {
62
            assertEquals(e.getMessage(), "CIDR is negative");
63
        }
64
65
        try {
66
            new NetMask("ae31::27:ef2:1/129");
67
            fail("NetMask succeeded with CIDR greater than address length!");
68
        } catch (IllegalArgumentException e) {
69
            assertEquals(e.getMessage(), "CIDR is greater than address length");
70
        }
71
    }
72
}
(-)a/webapps/docs/config/filter.xml (+103 lines)
Lines 654-659 FINE: Request "/docs/config/manager.html" with response status "200" content-typ Link Here
654
654
655
</section>
655
</section>
656
656
657
<section name="Remote CIDR Filter">
658
659
  <subsection name="Introduction">
660
661
    <p>The <strong>Remote CIDR Filter</strong> allows you to compare the
662
      IP address of the client that submitted this request against one or more
663
      netmasks following the CIDR notation, and either allow the request to
664
      continue or refuse to process the request from this client. IPv4 and
665
      IPv6 are both fully supported.
666
    </p>
667
668
    <p>This filter mimicks Apache's <code>Order</code>,
669
      <code>Allow from</code> and <code>Deny from</code> directives,
670
      with the following limitations:
671
    </p>
672
673
    <ul>
674
      <li><code>Order</code> will always be <code>allow, deny</code>;</li>
675
      <li>dotted quad notations for netmasks are not supported (that is, you
676
        cannot write <code>192.168.1.0/255.255.255.0</code>, you must write
677
        <code>192.168.1.0/24</code>;
678
      </li>
679
      <li>shortcuts, like <code>10.10.</code>, which is equivalent to
680
        <code>10.10.0.0/16</code>, are not supported;
681
      </li>
682
      <li>as the filter name says, this is a CIDR only filter,
683
        therefore subdomain notations like <code>.mydomain.com</code> are not
684
        supported either.
685
      </li>
686
    </ul>
687
688
    <p>Some more features of this filter are:
689
    </p>
690
691
    <ul>
692
      <li>if you omit the CIDR prefix, this filter becomes a single IP
693
      filter;</li>
694
      <li>unlike the <a href="#Remote_Host_Filter">Remote Host Filter</a>,
695
      it will do the correct thing with IPv6 in every case, which means you can
696
      write IPv6 addresses in condensed form (<code>::1</code>,
697
      <code>fe80::/71</code>, etc).</li>
698
    </ul>
699
700
  </subsection>
701
702
  <subsection name="Filter Class Name">
703
704
    <p>The filter class name for the Remote Address Filter is
705
      <strong><code>org.apache.catalina.filters.RemoteCIDRFilter</code>
706
      </strong>.</p>
707
708
  </subsection>
709
710
  <subsection name="Initialisation parameters">
711
712
    <p>The <strong>Remote CIDR Filter</strong> supports the following
713
      initialisation parameters:</p>
714
715
    <attributes>
716
717
      <attribute name="allow" required="false">
718
        <p>A comma-separated list of IPv4 or IPv6 netmasks or addresses
719
          that the remote client&apos;s IP address is matched against.
720
          If this attribute is specified, the remote address MUST match
721
          for this request to be accepted. If this attribute is not specified,
722
          all requests will be accepted UNLESS the remote IP is matched by a
723
          netmask in the <code>deny</code> attribute.
724
        </p>
725
      </attribute>
726
727
      <attribute name="deny" required="false">
728
        <p>A comma-separated list of IPv4 or IPv6 netmasks or addresses
729
          that the remote client&apos;s IP address is matched against.
730
          If this attribute is specified, the remote address MUST NOT match
731
          for this request to be accepted. If this attribute is not specified,
732
          request acceptance is governed solely by the <code>accept</code>
733
          attribute.
734
        </p>
735
      </attribute>
736
737
    </attributes>
738
739
  </subsection>
740
741
  <subsection name="Example">
742
    <p>To allow access only for the clients connecting from localhost:</p>
743
    <pre>
744
      &lt;filter>
745
      &lt;filter-name>Remote CIDR Filter&lt;/filter-name>
746
      &lt;filter-class>org.apache.catalina.filters.RemoteCIDRFilter&lt;/filter-class>
747
      &lt;init-param>
748
      &lt;param-name>allow&lt;/param-name>
749
      &lt;param-value>127.0.0.0/8, ::1&lt;/param-value>
750
      &lt;/init-param>
751
      &lt;/filter>
752
      &lt;filter-mapping>
753
      &lt;filter-name>Remote CIDR Filter&lt;/filter-name>
754
      &lt;url-pattern>/*&lt;/url-pattern>
755
      &lt;/filter-mapping>
756
    </pre>
757
  </subsection>
758
759
</section>
657
760
658
<section name="Remote IP Filter">
761
<section name="Remote IP Filter">
659
762
(-)a/webapps/docs/config/valve.xml (+94 lines)
Lines 621-626 Link Here
621
621
622
</section>
622
</section>
623
623
624
<section name="Remote CIDR Filter">
625
626
  <subsection name="Introduction">
627
628
    <p>The <strong>Remote CIDR Filter</strong> allows you to compare the
629
      IP address of the client that submitted this request against one or more
630
      netmasks following the CIDR notation, and either allow the request to
631
      continue or refuse to process the request from this client. IPv4 and
632
      IPv6 are both fully supported. A Remote CIDR Filter can be associated
633
      with any Catalina container (<a href="engine.html">Engine</a>,
634
      <a href="host.html">Host</a>, or <a href="context.html">Context</a>), and
635
      must accept any request presented to this container for processing before
636
      it will be passed on.
637
    </p>
638
639
    <p>This filter mimicks Apache's <code>Order</code>,
640
      <code>Allow from</code> and <code>Deny from</code> directives,
641
      with the following limitations:
642
    </p>
643
644
    <ul>
645
      <li><code>Order</code> will always be <code>allow, deny</code>;</li>
646
      <li>dotted quad notations for netmasks are not supported (that is, you
647
        cannot write <code>192.168.1.0/255.255.255.0</code>, you must write
648
        <code>192.168.1.0/24</code>;
649
      </li>
650
      <li>shortcuts, like <code>10.10.</code>, which is equivalent to
651
        <code>10.10.0.0/16</code>, are not supported;
652
      </li>
653
      <li>as the filter name says, this is a CIDR only filter,
654
        therefore subdomain notations like <code>.mydomain.com</code> are not
655
        supported either.
656
      </li>
657
    </ul>
658
659
    <p>Some more features of this filter are:
660
    </p>
661
662
    <ul>
663
      <li>if you omit the CIDR prefix, this filter becomes a single IP
664
        filter;</li>
665
      <li>unlike the <a href="#Remote_Host_Filter">Remote Host Filter</a>,
666
        it will do the correct thing with IPv6 in every case, which means you can
667
        write IPv6 addresses in condensed form (<code>::1</code>,
668
        <code>fe80::/71</code>, etc).</li>
669
    </ul>
670
671
  </subsection>
672
673
  <subsection name="Attributes">
674
675
    <p>The <strong>Remote CIDR Filter</strong> supports the following
676
      configuration attributes:</p>
677
678
    <attributes>
679
680
      <attribute name="className" required="true">
681
        <p>Java class name of the implementation to use.  This MUST be set to
682
          <strong>org.apache.catalina.valves.RemoteCIDRValve</strong>.</p>
683
      </attribute>
684
685
      <attribute name="allow" required="false">
686
        <p>A comma-separated list of IPv4 or IPv6 netmasks or addresses
687
          that the remote client&apos;s IP address is matched against.
688
          If this attribute is specified, the remote address MUST match
689
          for this request to be accepted. If this attribute is not specified,
690
          all requests will be accepted UNLESS the remote IP is matched by a
691
          netmask in the <code>deny</code> attribute.
692
        </p>
693
      </attribute>
694
695
      <attribute name="deny" required="false">
696
        <p>A comma-separated list of IPv4 or IPv6 netmasks or addresses
697
          that the remote client&apos;s IP address is matched against.
698
          If this attribute is specified, the remote address MUST NOT match
699
          for this request to be accepted. If this attribute is not specified,
700
          request acceptance is governed solely by the <code>accept</code>
701
          attribute.
702
        </p>
703
      </attribute>
704
705
    </attributes>
706
707
  </subsection>
708
709
  <subsection name="Example">
710
    <p>To allow access only for the clients connecting from localhost:</p>
711
    <pre>
712
      &lt;Valve className="org.apache.catalina.valves.RemoteCIDRValve"
713
      allow="127.0.0.1, ::1"/&gt;
714
    </pre>
715
  </subsection>
716
717
</section>
624
718
625
<section name="Single Sign On Valve">
719
<section name="Single Sign On Valve">
626
720

Return to bug 51953