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