288194 Add blacklist/whitelist to ProxyServlet and ProxyHandler

git-svn-id: svn+ssh://dev.eclipse.org/svnroot/rt/org.eclipse.jetty/jetty/trunk@2020 7e9141cc-0065-0410-87d8-b60c137991c4
This commit is contained in:
Michael Gorovoy 2010-06-16 22:55:43 +00:00
parent 51c6be9c42
commit 6bbaa8feaf
5 changed files with 382 additions and 13 deletions

View File

@ -1,4 +1,5 @@
jetty-7.1.5-SNAPSHOT
+ 288194 Add blacklist/whitelist to ProxyServlet and ProxyHandler
+ 311550 The WebAppProvider should allow setTempDirectory
+ 316449 Websocket disconnect fix
+ 316584 Exception on startup if temp path has spaces and extractWAR=false

View File

@ -13,10 +13,12 @@
package org.eclipse.jetty.embedded;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.bio.SocketConnector;
import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.server.handler.HandlerCollection;
import org.eclipse.jetty.server.handler.ProxyHandler;
import org.eclipse.jetty.server.nio.SelectChannelConnector;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.servlets.ProxyServlet;
public class ProxyServer
@ -24,18 +26,25 @@ public class ProxyServer
public static void main(String[] args) throws Exception
{
Server server = new Server();
Connector connector = new SocketConnector();
SelectChannelConnector connector = new SelectChannelConnector();
connector.setPort(8080);
server.setConnectors(new Connector[]
{ connector });
server.addConnector(connector);
ServletHandler handler = new ServletHandler();
server.setHandler(handler);
HandlerCollection handlers = new HandlerCollection();
server.setHandler(handlers);
handler.addServletWithMapping(ProxyServlet.class,"/");
ServletContextHandler context = new ServletContextHandler(handlers, "/", ServletContextHandler.SESSIONS);
ServletHolder proxyServlet = new ServletHolder(ProxyServlet.class);
proxyServlet.setInitParameter("whiteList", "google.com, www.eclipse.org");
proxyServlet.setInitParameter("blackList", "google.com/calendar/*, www.eclipse.org/committers/");
context.addServlet(proxyServlet, "/*");
ProxyHandler proxy = new ProxyHandler();
proxy.setWhite(new String[]{"mail.google.com"});
proxy.addWhite("www.google.com");
handlers.addHandler(proxy);
server.start();
server.join();
}
}

View File

@ -9,6 +9,7 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@ -26,6 +27,7 @@ import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.HttpConnection;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.HostMap;
import org.eclipse.jetty.util.component.LifeCycle;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@ -48,17 +50,31 @@ public class ProxyHandler extends HandlerWrapper
private volatile int _writeTimeout = 30000;
private volatile ThreadPool _threadPool;
private volatile boolean _privateThreadPool;
private HostMap<String> _white = new HostMap<String>();
private HostMap<String> _black = new HostMap<String>();
public ProxyHandler()
{
this(null);
}
public ProxyHandler(String[] white, String[] black)
{
this(null, white, black);
}
public ProxyHandler(Handler handler)
{
setHandler(handler);
}
public ProxyHandler(Handler handler, String[] white, String[] black)
{
setHandler(handler);
set(white, _white);
set(black, _black);
}
/**
* @return the timeout, in milliseconds, to connect to the remote server
*/
@ -208,6 +224,14 @@ public class ProxyHandler extends HandlerWrapper
host = serverAddress.substring(0, colon);
port = Integer.parseInt(serverAddress.substring(colon + 1));
}
if (!validateDestination(host))
{
Log.info("ProxyHandler: Forbidden destination "+host);
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
baseRequest.setHandled(true);
return;
}
SocketChannel channel = connectToServer(request, host, port);
@ -705,4 +729,119 @@ public class ProxyHandler extends HandlerWrapper
}
}
}
/* ------------------------------------------------------------ */
/**
* Add a whitelist entry to an existing handler configuration
*
* @param entry new whitelist entry
*/
public void addWhite(String entry)
{
add(entry, _white);
}
/* ------------------------------------------------------------ */
/**
* Add a blacklist entry to an existing handler configuration
*
* @param entry new blacklist entry
*/
public void addBlack(String entry)
{
add(entry, _black);
}
/* ------------------------------------------------------------ */
/**
* Re-initialize the whitelist of existing handler object
*
* @param entries array of whitelist entries
*/
public void setWhite(String[] entries)
{
set(entries, _white);
}
/* ------------------------------------------------------------ */
/**
* Re-initialize the blacklist of existing handler object
*
* @param entries array of blacklist entries
*/
public void setBlack(String[] entries)
{
set(entries, _black);
}
/* ------------------------------------------------------------ */
/**
* Helper method to process a list of new entries and replace
* the content of the specified host map
*
* @param entries new entries
* @param patternMap target host map
*/
protected void set(String[] entries, HostMap<String> hostMap)
{
hostMap.clear();
if (entries != null && entries.length > 0)
{
for (String addrPath:entries)
{
add(addrPath, hostMap);
}
}
}
/* ------------------------------------------------------------ */
/**
* Helper method to process the new entry and add it to
* the specified host map.
*
* @param entry new entry
* @param patternMap target host map
*/
private void add(String entry, HostMap<String> hostMap)
{
if (entry != null && entry.length() > 0)
{
entry = entry.trim();
if (hostMap.get(entry) == null)
{
hostMap.put(entry,entry);
}
}
}
/* ------------------------------------------------------------ */
/**
* Check the request hostname against white- and blacklist.
*
* @param host hostname to check
* @return true if hostname is allowed to be proxied
*/
public boolean validateDestination(String host)
{
if (_white.size()>0)
{
Object whiteObj = _white.getLazyMatches(host);
if (whiteObj == null)
{
return false;
}
}
if (_black.size() > 0)
{
Object blackObj = _black.getLazyMatches(host);
if (blackObj != null)
{
return false;
}
}
return true;
}
}

View File

@ -22,8 +22,13 @@ import java.net.MalformedURLException;
import java.net.Socket;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
@ -42,8 +47,10 @@ import org.eclipse.jetty.http.HttpHeaderValues;
import org.eclipse.jetty.http.HttpHeaders;
import org.eclipse.jetty.http.HttpSchemes;
import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.http.PathMap;
import org.eclipse.jetty.io.Buffer;
import org.eclipse.jetty.io.EofException;
import org.eclipse.jetty.util.HostMap;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@ -67,13 +74,15 @@ import org.eclipse.jetty.util.thread.QueuedThreadPool;
* <li>maxThreads - maximum threads
* <li>maxConnections - maximum connections per destination
* <li>HostHeader - Force the host header to a particular value
* <li>whiteList - comma-separated list of allowed proxy destinations
* <li>blackList - comma-separated list of forbidden proxy destinations
* </ul>
*/
public class ProxyServlet implements Servlet
{
protected Logger _log;
HttpClient _client;
String _hostHeader;
protected HttpClient _client;
protected String _hostHeader;
protected HashSet<String> _DontProxyHeaders = new HashSet<String>();
{
@ -90,6 +99,8 @@ public class ProxyServlet implements Servlet
protected ServletConfig _config;
protected ServletContext _context;
protected HostMap<PathMap> _white = new HostMap<PathMap>();
protected HostMap<PathMap> _black = new HostMap<PathMap>();
/* ------------------------------------------------------------ */
/* (non-Javadoc)
@ -129,6 +140,17 @@ public class ProxyServlet implements Servlet
_context.setAttribute(config.getServletName()+".ThreadPool",_client.getThreadPool());
_context.setAttribute(config.getServletName()+".HttpClient",_client);
}
String white = config.getInitParameter("whiteList");
if (white != null)
{
parseList(white, _white);
}
String black = config.getInitParameter("blackList");
if (black != null)
{
parseList(black, _black);
}
}
catch (Exception e)
{
@ -136,6 +158,95 @@ public class ProxyServlet implements Servlet
}
}
/* ------------------------------------------------------------ */
/**
* Helper function to process a parameter value containing a list
* of new entries and initialize the specified host map.
*
* @param list comma-separated list of new entries
* @param hostMap target host map
*/
private void parseList(String list, HostMap<PathMap> hostMap)
{
if (list != null && list.length() > 0)
{
int idx;
String entry;
StringTokenizer entries = new StringTokenizer(list, ",");
while(entries.hasMoreTokens())
{
entry = entries.nextToken();
idx = entry.indexOf('/');
String host = idx > 0 ? entry.substring(0,idx) : entry;
String path = idx > 0 ? entry.substring(idx) : "/*";
host = host.trim();
PathMap pathMap = hostMap.get(host);
if (pathMap == null)
{
pathMap = new PathMap(true);
hostMap.put(host,pathMap);
}
if (path != null)
{
pathMap.put(path,path);
}
}
}
}
/* ------------------------------------------------------------ */
/**
* Check the request hostname and path against white- and blacklist.
*
* @param host hostname to check
* @param path path to check
* @return true if request is allowed to be proxied
*/
public boolean validateDestination(String host, String path)
{
if (_white.size()>0)
{
boolean match = false;
Object whiteObj = _white.getLazyMatches(host);
if (whiteObj != null)
{
List whiteList = (whiteObj instanceof List) ? (List)whiteObj : Collections.singletonList(whiteObj);
for (Object entry: whiteList)
{
PathMap pathMap = ((Map.Entry<String, PathMap>)entry).getValue();
if (match = (pathMap!=null && (pathMap.size()==0 || pathMap.match(path)!=null)))
break;
}
}
if (!match)
return false;
}
if (_black.size() > 0)
{
Object blackObj = _black.getLazyMatches(host);
if (blackObj != null)
{
List blackList = (blackObj instanceof List) ? (List)blackObj : Collections.singletonList(blackObj);
for (Object entry: blackList)
{
PathMap pathMap = ((Map.Entry<String, PathMap>)entry).getValue();
if (pathMap!=null && (pathMap.size()==0 || pathMap.match(path)!=null))
return false;
}
}
}
return true;
}
/* ------------------------------------------------------------ */
/* (non-Javadoc)
* @see javax.servlet.Servlet#getServletConfig()
@ -423,6 +534,9 @@ public class ProxyServlet implements Servlet
protected HttpURI proxyHttpURI(String scheme, String serverName, int serverPort, String uri)
throws MalformedURLException
{
if (!validateDestination(serverName, uri))
return null;
return new HttpURI(scheme+"://"+serverName+":"+serverPort+uri);
}
@ -517,8 +631,13 @@ public class ProxyServlet implements Servlet
{
if (!uri.startsWith(_prefix))
return null;
URI dstUri = new URI(_proxyTo + uri.substring(_prefix.length())).normalize();
if (!validateDestination(dstUri.getHost(),dstUri.getPath()))
return null;
return new HttpURI(new URI(_proxyTo + uri.substring(_prefix.length())).normalize().toString());
return new HttpURI(dstUri.toString());
}
catch (URISyntaxException ex)
{

View File

@ -0,0 +1,101 @@
// ========================================================================
// Copyright (c) 2010 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
package org.eclipse.jetty.util;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
/* ------------------------------------------------------------ */
/**
*/
public class HostMap<TYPE> extends HashMap<String, TYPE>
{
/* --------------------------------------------------------------- */
/** Construct empty HostMap.
*/
public HostMap()
{
super(11);
}
/* --------------------------------------------------------------- */
/** Construct empty HostMap.
*
* @param capacity initial capacity
*/
public HostMap(int capacity)
{
super (capacity);
}
/* ------------------------------------------------------------ */
/**
* @see java.util.HashMap#put(java.lang.Object, java.lang.Object)
*/
@Override
public TYPE put(String host, TYPE object)
throws IllegalArgumentException
{
return super.put(host, object);
}
/* ------------------------------------------------------------ */
/**
* @see java.util.HashMap#get(java.lang.Object)
*/
@Override
public TYPE get(Object key)
{
return super.get(key);
}
/* ------------------------------------------------------------ */
/**
* Retrieve a lazy list of map entries associated with specified
* hostname by taking into account the domain suffix matches.
*
* @param host hostname
* @return lazy list of map entries
*/
public Object getLazyMatches(String host)
{
if (host == null)
return LazyList.getList(super.entrySet());
int idx = 0;
String domain = host.trim();
HashSet<String> domains = new HashSet<String>();
do {
domains.add(domain);
if ((idx = domain.indexOf('.')) > 0)
{
domain = domain.substring(idx+1);
}
} while (idx > 0);
Object entries = null;
for(Map.Entry<String, TYPE> entry: super.entrySet())
{
if (domains.contains(entry.getKey()))
{
entries = LazyList.add(entries,entry);
}
}
return entries;
}
}