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- => nnn-255 73 * -nnn => 0-nnn 74 * - => 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 }