소스 검색

HUE-392. Proxy to support a blacklist

bc Wong 15 년 전
부모
커밋
c418cae6c3
3개의 변경된 파일57개의 추가작업 그리고 1개의 파일을 삭제
  1. 7 1
      apps/proxy/src/proxy/conf.py
  2. 24 0
      apps/proxy/src/proxy/proxy_test.py
  3. 26 0
      apps/proxy/src/proxy/views.py

+ 7 - 1
apps/proxy/src/proxy/conf.py

@@ -29,5 +29,11 @@ def list_of_compiled_res(list_of_strings):
 WHITELIST = Config(
   key="whitelist",
   default="(localhost|127\.0\.0\.1):(50030|50070|50060|50075)",
-  help="Comma-separated list of regular expressions, which match 'host:ip' of requested proxy target.",
+  help="Comma-separated list of regular expressions, which match 'host:port' of requested proxy target.",
+  type=list_of_compiled_res)
+
+BLACKLIST = Config(
+  key="blacklist",
+  default=(),
+  help="Comma-separated list of regular expressions, which match any prefix of 'host:port/path' of requested proxy target. This does not support matching GET parameters.",
   type=list_of_compiled_res)

+ 24 - 0
apps/proxy/src/proxy/proxy_test.py

@@ -23,6 +23,7 @@ import StringIO
 
 from nose.tools import assert_true, assert_false
 from django.test.client import Client
+from desktop.lib.django_test_util import make_logged_in_client
 
 from proxy.views import _rewrite_links
 import proxy.conf
@@ -115,6 +116,29 @@ def test_proxy_post():
   finally:
     finish()
 
+def test_blacklist():
+  client = make_logged_in_client('test')
+  finish_confs = [
+    proxy.conf.WHITELIST.set_for_testing(r"localhost:\d*"),
+    proxy.conf.BLACKLIST.set_for_testing(r"localhost:\d*/(foo|bar)/fred/"),
+  ]
+  try:
+    # Request 1: Hit the blacklist
+    resp = client.get('/proxy/localhost/1234//foo//fred/')
+    assert_true("is blocked" in resp.content)
+
+    # Request 2: This is not a match
+    httpd, finish = run_test_server()
+    try:
+      resp = client.get('/proxy/localhost/%s//foo//fred_ok' % (httpd.server_port,))
+      assert_true("Hello there" in resp.content)
+    finally:
+      finish()
+  finally:
+    for fin in finish_confs:
+      fin()
+
+
 class UrlLibFileWrapper(StringIO.StringIO):
   """
   urllib2.urlopen returns a file-like object; we fake it here.

+ 26 - 0
apps/proxy/src/proxy/views.py

@@ -49,6 +49,29 @@ def check_host_port(host, port):
 
   return False
 
+def check_blacklist(host, port, path):
+  """
+  Return true if this host:port path combo is allowed to be proxied.
+  """
+  blacklist = conf.BLACKLIST.get()
+  if not blacklist:
+    return True
+
+  # Make a canonical path, since "/forbidden//path" (string) does not match
+  # "/forbidden/path" (regex).
+  has_trailing_slash = path.endswith('/')
+  path_elems = path.split('/')
+  path_elems = [ p for p in path_elems if p ]
+  canon_url = "%s:%s/%s" % (host, port, '/'.join(path_elems))
+  if has_trailing_slash:
+    canon_url += '/'
+
+  for regexp in blacklist:
+    if regexp.match(canon_url):
+      return False
+  return True
+
+
 def proxy(request, host, port, path):
   """
   Proxies an HTTP request by fetching the data
@@ -60,6 +83,9 @@ def proxy(request, host, port, path):
     raise MessageException(
       ("%s:%d is not whitelisted for reverse proxying, nor a daemon that Cluster Health " +
        "is aware of.  Contact your administrator.") % (host, port))
+  if not check_blacklist(host, port, path):
+    raise MessageException(
+      "Access to %s:%s%s is blocked. Contact your administrator." % (host, port, path))
 
   # The tuple here is: (scheme, netloc, path, params, query, fragment).
   # We don't support params or fragment.