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