View Javadoc
1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.server.handler;
20  
21  import java.io.IOException;
22  import java.net.InetSocketAddress;
23  import java.util.Map;
24  
25  import javax.servlet.ServletException;
26  import javax.servlet.http.HttpServletRequest;
27  import javax.servlet.http.HttpServletResponse;
28  
29  import org.eclipse.jetty.http.HttpStatus;
30  import org.eclipse.jetty.http.PathMap;
31  import org.eclipse.jetty.io.EndPoint;
32  import org.eclipse.jetty.server.HttpChannel;
33  import org.eclipse.jetty.server.Request;
34  import org.eclipse.jetty.util.IPAddressMap;
35  import org.eclipse.jetty.util.log.Log;
36  import org.eclipse.jetty.util.log.Logger;
37  
38  
39  /**
40   * IP Access Handler
41   * <p>
42   * Controls access to the wrapped handler by the real remote IP. Control is provided
43   * by white/black lists that include both internet addresses and URIs. This handler
44   * uses the real internet address of the connection, not one reported in the forwarded
45   * for headers, as this cannot be as easily forged.
46   * <p>
47   * Typically, the black/white lists will be used in one of three modes:
48   * <ul>
49   * <li>Blocking a few specific IPs/URLs by specifying several black list entries.
50   * <li>Allowing only some specific IPs/URLs by specifying several white lists entries.
51   * <li>Allowing a general range of IPs/URLs by specifying several general white list
52   * entries, that are then further refined by several specific black list exceptions
53   * </ul>
54   * <p>
55   * By default an empty white list is treated as match all. If there is at least one entry in
56   * the white list, then a request must match a white list entry. Black list entries
57   * are always applied, so that even if an entry matches the white list, a black list
58   * entry will override it.
59   * <p>
60   * You can change white list policy setting whiteListByPath to true. In this mode a request will be white listed
61   * IF it has a matching URL in the white list, otherwise the black list applies, e.g. in default mode when
62   * whiteListByPath = false and wl = "127.0.0.1|/foo", /bar request from 127.0.0.1 will be blacklisted,
63   * if whiteListByPath=true then not.
64   * <p>
65   * Internet addresses may be specified as absolute address or as a combination of
66   * four octet wildcard specifications (a.b.c.d) that are defined as follows.
67   * </p>
68   * <pre>
69   * nnn - an absolute value (0-255)
70   * mmm-nnn - an inclusive range of absolute values,
71   *           with following shorthand notations:
72   *           nnn- =&gt; nnn-255
73   *           -nnn =&gt; 0-nnn
74   *           -    =&gt; 0-255
75   * a,b,... - a list of wildcard specifications
76   * </pre>
77   * <p>
78   * Internet address specification is separated from the URI pattern using the "|" (pipe)
79   * character. URI patterns follow the servlet specification for simple * prefix and
80   * suffix wild cards (e.g. /, /foo, /foo/bar, /foo/bar/*, *.baz).
81   * <p>
82   * Earlier versions of the handler used internet address prefix wildcard specification
83   * to define a range of the internet addresses (e.g. 127., 10.10., 172.16.1.).
84   * They also used the first "/" character of the URI pattern to separate it from the
85   * internet address. Both of these features have been deprecated in the current version.
86   * <p>
87   * Examples of the entry specifications are:
88   * <ul>
89   * <li>10.10.1.2 - all requests from IP 10.10.1.2
90   * <li>10.10.1.2|/foo/bar - all requests from IP 10.10.1.2 to URI /foo/bar
91   * <li>10.10.1.2|/foo/* - all requests from IP 10.10.1.2 to URIs starting with /foo/
92   * <li>10.10.1.2|*.html - all requests from IP 10.10.1.2 to URIs ending with .html
93   * <li>10.10.0-255.0-255 - all requests from IPs within 10.10.0.0/16 subnet
94   * <li>10.10.0-.-255|/foo/bar - all requests from IPs within 10.10.0.0/16 subnet to URI /foo/bar
95   * <li>10.10.0-3,1,3,7,15|/foo/* - all requests from IPs addresses with last octet equal
96   *                                  to 1,3,7,15 in subnet 10.10.0.0/22 to URIs starting with /foo/
97   * </ul>
98   * <p>
99   * Earlier versions of the handler used internet address prefix wildcard specification
100  * to define a range of the internet addresses (e.g. 127., 10.10., 172.16.1.).
101  * They also used the first "/" character of the URI pattern to separate it from the
102  * internet address. Both of these features have been deprecated in the current version.
103  * @deprecated
104  * @see InetAccessHandler
105  */
106 public class IPAccessHandler extends HandlerWrapper
107 {
108     private static final Logger LOG = Log.getLogger(IPAccessHandler.class);
109     // true means nodefault match
110     PathMap<IPAddressMap<Boolean>> _white = new PathMap<IPAddressMap<Boolean>>(true);
111     PathMap<IPAddressMap<Boolean>> _black = new PathMap<IPAddressMap<Boolean>>(true);
112     boolean _whiteListByPath = false;
113 
114     /* ------------------------------------------------------------ */
115     /**
116      * Creates new handler object
117      */
118     public IPAccessHandler()
119     {
120         super();
121     }
122 
123     /* ------------------------------------------------------------ */
124     /**
125      * Creates new handler object and initializes white- and black-list
126      *
127      * @param white array of whitelist entries
128      * @param black array of blacklist entries
129      */
130     public IPAccessHandler(String[] white, String []black)
131     {
132         super();
133 
134         if (white != null && white.length > 0)
135             setWhite(white);
136         if (black != null && black.length > 0)
137             setBlack(black);
138     }
139 
140     /* ------------------------------------------------------------ */
141     /**
142      * Add a whitelist entry to an existing handler configuration
143      *
144      * @param entry new whitelist entry
145      */
146     public void addWhite(String entry)
147     {
148         add(entry, _white);
149     }
150 
151     /* ------------------------------------------------------------ */
152     /**
153      * Add a blacklist entry to an existing handler configuration
154      *
155      * @param entry new blacklist entry
156      */
157     public void addBlack(String entry)
158     {
159         add(entry, _black);
160     }
161 
162     /* ------------------------------------------------------------ */
163     /**
164      * Re-initialize the whitelist of existing handler object
165      *
166      * @param entries array of whitelist entries
167      */
168     public void setWhite(String[] entries)
169     {
170         set(entries, _white);
171     }
172 
173     /* ------------------------------------------------------------ */
174     /**
175      * Re-initialize the blacklist of existing handler object
176      *
177      * @param entries array of blacklist entries
178      */
179     public void setBlack(String[] entries)
180     {
181         set(entries, _black);
182     }
183 
184     /* ------------------------------------------------------------ */
185     /**
186      * Re-initialize the mode of path matching
187      *
188      * @param whiteListByPath matching mode
189      */
190     public void setWhiteListByPath(boolean whiteListByPath)
191     {
192         this._whiteListByPath = whiteListByPath;
193     }
194 
195     /* ------------------------------------------------------------ */
196     /**
197      * Checks the incoming request against the whitelist and blacklist
198      *
199      * @see org.eclipse.jetty.server.handler.HandlerWrapper#handle(java.lang.String, org.eclipse.jetty.server.Request, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
200      */
201     @Override
202     public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
203     {
204         // Get the real remote IP (not the one set by the forwarded headers (which may be forged))
205         HttpChannel channel = baseRequest.getHttpChannel();
206         if (channel!=null)
207         {
208             EndPoint endp=channel.getEndPoint();
209             if (endp!=null)
210             {
211                 InetSocketAddress address = endp.getRemoteAddress();
212                 if (address!=null && !isAddrUriAllowed(address.getHostString(),baseRequest.getPathInfo()))
213                 {
214                     response.sendError(HttpStatus.FORBIDDEN_403);
215                     baseRequest.setHandled(true);
216                     return;
217                 }
218             }
219         }
220 
221         getHandler().handle(target,baseRequest, request, response);
222     }
223 
224 
225     /* ------------------------------------------------------------ */
226     /**
227      * Helper method to parse the new entry and add it to
228      * the specified address pattern map.
229      *
230      * @param entry new entry
231      * @param patternMap target address pattern map
232      */
233     protected void add(String entry, PathMap<IPAddressMap<Boolean>> patternMap)
234     {
235         if (entry != null && entry.length() > 0)
236         {
237             boolean deprecated = false;
238             int idx;
239             if (entry.indexOf('|') > 0 )
240             {
241                 idx = entry.indexOf('|');
242             }
243             else
244             {
245                 idx = entry.indexOf('/');
246                 deprecated = (idx >= 0);
247             }
248 
249             String addr = idx > 0 ? entry.substring(0,idx) : entry;
250             String path = idx > 0 ? entry.substring(idx) : "/*";
251 
252             if (addr.endsWith("."))
253                 deprecated = true;
254             if (path!=null && (path.startsWith("|") || path.startsWith("/*.")))
255                 path=path.substring(1);
256 
257             IPAddressMap<Boolean> addrMap = patternMap.get(path);
258             if (addrMap == null)
259             {
260                 addrMap = new IPAddressMap<Boolean>();
261                 patternMap.put(path,addrMap);
262             }
263             if (addr != null && !"".equals(addr))
264                 // MUST NOT BE null
265                 addrMap.put(addr, true);
266 
267             if (deprecated)
268                 LOG.debug(toString() +" - deprecated specification syntax: "+entry);
269         }
270     }
271 
272     /* ------------------------------------------------------------ */
273     /**
274      * Helper method to process a list of new entries and replace
275      * the content of the specified address pattern map
276      *
277      * @param entries new entries
278      * @param patternMap target address pattern map
279      */
280     protected void set(String[] entries,  PathMap<IPAddressMap<Boolean>> patternMap)
281     {
282         patternMap.clear();
283 
284         if (entries != null && entries.length > 0)
285         {
286             for (String addrPath:entries)
287             {
288                 add(addrPath, patternMap);
289             }
290         }
291     }
292 
293     /* ------------------------------------------------------------ */
294     /**
295      * Check if specified request is allowed by current IPAccess rules.
296      *
297      * @param addr internet address
298      * @param path context path
299      * @return true if request is allowed
300      *
301      */
302     protected boolean isAddrUriAllowed(String addr, String path)
303     {
304         if (_white.size()>0)
305         {
306             boolean match = false;
307             boolean matchedByPath = false;
308 
309             for (Map.Entry<String,IPAddressMap<Boolean>> entry : _white.getMatches(path))
310             {
311                 matchedByPath=true;
312                 IPAddressMap<Boolean> addrMap = entry.getValue();
313                 if ((addrMap!=null && (addrMap.size()==0 || addrMap.match(addr)!=null)))
314                 {
315                     match=true;
316                     break;
317                 }
318             }
319             
320             if (_whiteListByPath)
321             {
322                 if (matchedByPath && !match)
323                     return false;
324             }
325             else
326             {
327                 if (!match)
328                     return false;
329             }
330         }
331 
332         if (_black.size() > 0)
333         {
334             for (Map.Entry<String,IPAddressMap<Boolean>> entry : _black.getMatches(path))
335             {
336                 IPAddressMap<Boolean> addrMap = entry.getValue();
337                 if (addrMap!=null && (addrMap.size()==0 || addrMap.match(addr)!=null))
338                     return false;
339             }
340             
341         }
342 
343         return true;
344     }
345 
346     /* ------------------------------------------------------------ */
347     /**
348      * Dump the handler configuration
349      */
350     @Override
351     public String dump()
352     {
353         StringBuilder buf = new StringBuilder();
354 
355         buf.append(toString());
356         buf.append(" WHITELIST:\n");
357         dump(buf, _white);
358         buf.append(toString());
359         buf.append(" BLACKLIST:\n");
360         dump(buf, _black);
361 
362         return buf.toString();
363     }
364 
365     /* ------------------------------------------------------------ */
366     /**
367      * Dump a pattern map into a StringBuilder buffer
368      *
369      * @param buf buffer
370      * @param patternMap pattern map to dump
371      */
372     protected void dump(StringBuilder buf, PathMap<IPAddressMap<Boolean>> patternMap)
373     {
374         for (String path: patternMap.keySet())
375         {
376             for (String addr: patternMap.get(path).keySet())
377             {
378                 buf.append("# ");
379                 buf.append(addr);
380                 buf.append("|");
381                 buf.append(path);
382                 buf.append("\n");
383             }
384         }
385     }
386  }