Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Add ACL thing

  • Loading branch information...
commit 3bd2a37eeff1ea6de055853593614551b8af7612 1 parent 5226bdb
@Jc2k Jc2k authored
View
40 badgerproxy-example
@@ -5,17 +5,26 @@ resolver_cache: /tmp/badgerproxy.resolvercache
services:
- listen: tcp:8084
- methods:
- allowed:
- - HEAD
- - POST
- - GET
- - CONECT
-
- ports:
- allowed:
- - 80
- - 443
+
+ rules:
+ - match:
+ source:
+ - 192.168.0.1/8
+ - 127.0.0.1
+ port: 22
+ method: CONNECT
+
+ action: passthru
+
+ - match:
+ port: 22
+ action: deny
+
+ - match:
+ method: CONNECT
+ action: rewrite-ssl
+
+ - action: rewrite
rewriter:
cls: badgerproxy.proxyclient:RewritingProxyClient
@@ -23,12 +32,3 @@ services:
bannerurl: http://badger/badgerproxy.gif
- - listen: tcp:8085
- methods:
- allowed:
- - CONNECT
-
- ports:
- allowed:
- - 22
-
View
149 badgerproxy/proxy.py
@@ -31,6 +31,7 @@
from .proxyclient import Proxier
from .internalproxy import InternalProxier
from .passthru import PassthruFactory
+from .rules import Rules
def sibpath(asset):
path = os.path.dirname(__file__)
@@ -77,35 +78,66 @@ class ReverseProxy(HTTPChannel):
class TunnelProxyRequest (ProxyRequest):
- #protocol = RewritingProxyClientFactory
-
def process(self):
- if not self.channel.factory.root.is_method_allowed(self.method.upper()):
- ForbiddenResponse(self, "You are not permitted to use this HTTP method").render()
- return
-
- if self.method.upper() == 'CONNECT':
- self._process_connect()
+ parsed = urlparse.urlparse(self.uri)
+ if self.method.upper() != "CONNECT":
+ protocol = parsed[0]
+ host = parsed[1]
+ port = self.ports[protocol]
else:
- self._process()
+ host = parsed[0]
+ port = int(parsed[2])
- def _process(self):
- parsed = urlparse.urlparse(self.uri)
- protocol = parsed[0]
- host = parsed[1]
- port = self.ports[protocol]
if ':' in host:
host, port = host.split(':')
port = int(port)
+
rest = urlparse.urlunparse(('', '') + parsed[2:])
if not rest:
rest = rest + '/'
+
headers = self.getAllHeaders().copy()
if 'host' not in headers:
- headers['host'] = host
+ headers['host'] = self.host
+
self.content.seek(0, 0)
s = self.content.read()
+ root = self.channel.factory.root
+ ip = root.parent.resolver.lookup(host)
+
+ if host == "badger":
+ action = "internal"
+ elif not ip:
+ action = "deny"
+ else:
+ action = root.rules.check(
+ source = self.transport.getPeer()[1],
+ destination = ip,
+ port = port,
+ method = self.method,
+ )
+
+ log.msg("Chosen access: %s" % action)
+
+ funcname = "action_" + action.replace("-", "_")
+ if not hasattr(self, funcname):
+ log.msg("Invalid action: %s" % action)
+ funcname = "action_deny"
+
+ getattr(self, funcname)(host, ip, port, rest, headers, s, self.clientproto)
+
+ def action_deny(self, host, ip, port, resturi, headers, data, clientproto):
+ ForbiddenResponse(self, "You have been denied access by the proxy configuration").render()
+
+ def action_passthru(self, host, ip, port, resturi, headers, data, clientproto):
+ self.reactor.connectTCP(ip, port, PassthruFactory(self))
+
+ def action_internal(self, host, ip, port, resturi, headers, data, clientproto):
+ p = InternalProxier(self.channel.factory.root, host, port)
+ p.proxy(self.method, resturi, clientproto, headers, data, self)
+
+ def action_rewrite(self, host, ip, port, resturi, headers, data, clientproto):
if host == "badger":
P = InternalProxier
else:
@@ -115,54 +147,34 @@ def _process(self):
if not p.permitted():
ForbiddenResponse(self, "You are not permitted to access this host").render()
return
- p.proxy(self.method, rest, self.clientproto, headers, s, self)
-
- def _process_connect(self):
- try:
- host, portStr = self.uri.split(':', 1)
- port = int(portStr)
- except ValueError:
- # Either the connect parameter is not HOST:PORT or PORT is
- # not an integer, in which case this request is invalid.
- self.setResponseCode(400)
- self.finish()
- return
+ p.proxy(self.method, resturi, clientproto, headers, data, self)
- ip = self.channel.factory.root.parent.resolver.lookup(host)
- if not ip:
- ForbiddenResponse(self, "You are not permitted to access this host").render()
- return
+ def action_rewrite_ssl(self, host, ip, port, resturi, headers, data, clientproto):
+ class FakeFactory:
+ def log(self, meh):
+ pass
+ FakeFactory.host = host
+ FakeFactory.port = port
+ FakeFactory.root = self.channel.factory.root
+ rp = ReverseProxy()
+ rp.factory = FakeFactory()
- if port == 22:
- self.reactor.connectTCP(ip, port, PassthruFactory(self))
- elif port == 443:
- class FakeFactory:
- def log(self, meh):
- pass
- FakeFactory.host = host
- FakeFactory.port = port
- FakeFactory.root = self.channel.factory.root
- rp = ReverseProxy()
- rp.factory = FakeFactory()
-
- contextFactory = ssl.DefaultOpenSSLContextFactory(sibpath('server.key'), sibpath('server.crt'))
-
- class FakeFactory2:
- _contextFactory = contextFactory
- _isClient = False
- def registerProtocol(self, meh):
- pass
- def unregisterProtocol(self, meh):
- pass
- ssl_rp = TLSMemoryBIOProtocol(FakeFactory2(), rp)
-
- self.channel._registerTunnel(ssl_rp)
- ssl_rp.makeConnection(self.transport)
-
- self.setResponseCode(200)
- self.write("")
- else:
- ForbiddenResponse(self, "You are not permitted to access this port").render()
+ contextFactory = ssl.DefaultOpenSSLContextFactory(sibpath('server.key'), sibpath('server.crt'))
+
+ class FakeFactory2:
+ _contextFactory = contextFactory
+ _isClient = False
+ def registerProtocol(self, meh):
+ pass
+ def unregisterProtocol(self, meh):
+ pass
+ ssl_rp = TLSMemoryBIOProtocol(FakeFactory2(), rp)
+
+ self.channel._registerTunnel(ssl_rp)
+ ssl_rp.makeConnection(self.transport)
+
+ self.setResponseCode(200)
+ self.write("")
class TunnelProxy (Proxy):
@@ -203,20 +215,7 @@ class ProxyService(service.MultiService):
def __init__(self, config):
service.MultiService.__init__(self)
self.config = config
-
- def is_method_allowed(self, method):
- if not "methods" in self.config:
- return True
- methods = self.config["methods"]
- if "allowed" in method:
- if method in method["allowed"]:
- return True
- return False
- elif "blocked" in method:
- if method in method["blocked"]:
- return False
- return True
- return True
+ self.rules = Rules(config.get("rules", []))
def setServiceParent(self, parent):
service.MultiService.setServiceParent(self, parent)
View
117 badgerproxy/rules.py
@@ -0,0 +1,117 @@
+
+from collections import namedtuple
+import ipaddr
+from twisted.python import log
+
+
+class Rule(object):
+
+ def __init__(self, conditions, action):
+ self.conditions = conditions
+ self.action = action
+
+ def _get_list(self, key):
+ value = self.conditions.get(key, [])
+ if not isinstance(value, list):
+ value = [value]
+ return value
+
+ def _compare_network(self, ip, network):
+ ip = ipaddr.IPAddress(ip)
+ try:
+ return ip in ipaddr.IPNetwork(network)
+ except ipaddr.AddressValueError:
+ try:
+ return ip == ipaddr.IPAddress(network)
+ except:
+ log.err()
+ except:
+ log.err()
+
+ return False
+
+ def check_source(self, source):
+ if not "source" in self.conditions:
+ return True
+
+ for n in self._get_list("source"):
+ if self._compare_network(source, n):
+ return True
+
+ return False
+
+ def check_destination(self, destination):
+ if not "destination" in self.conditions:
+ return True
+
+ for n in self._get_list("destination"):
+ if self._compare_network(destination, n):
+ return True
+
+ return False
+
+ def check_port(self, port):
+ if not "port" in self.conditions:
+ return True
+
+ for p in self._get_list("port"):
+ if isinstance(p, int):
+ if port == p:
+ return True
+ continue
+
+ for rng in p.split(","):
+ rng = rng.strip()
+ if "-" in rng:
+ start, end = rng.split("-")
+ start, end = int(start.strip()), int(end.strip())
+ if start <= port and port <= end:
+ return True
+ else:
+ if port == int(p.strip()):
+ return True
+
+ return False
+
+ def check_method(self, method):
+ if not "method" in self.conditions:
+ return True
+
+ for m in self._get_list("method"):
+ if m.lower() == method.lower():
+ return True
+
+ return False
+
+ def check(self, source, destination, port, method):
+ log.msg("Checking access: %s %s %s %s" % (source,destination,port,method))
+ if not self.check_source(source):
+ log.msg(" and source is not allowed")
+ return False
+ if not self.check_destination(destination):
+ log.msg(" and destination is not allowed")
+ return False
+ if not self.check_port(port):
+ log.msg(" and port is not allowed")
+ return False
+ if not self.check_method(method):
+ log.msg(" and method is not allowed")
+ return False
+ return True
+
+
+class Rules(object):
+
+ def __init__(self, rules):
+ self._rules = []
+ for r in rules:
+ conditions = r.get("match", {})
+ action = r.get("action", "deny")
+ self._rules.append(Rule(conditions, action))
+
+ def check(self, source, destination, port, method):
+ for i, rule in enumerate(self._rules):
+ if rule.check(source, destination, port, method):
+ return rule.action
+ return "deny"
+
View
1  setup.py
@@ -23,6 +23,7 @@
'setuptools',
'yay >= 0.0.24',
'missingbits',
+ 'ipaddr',
],
entry_points = {
"console_scripts": [
View
18 test
@@ -0,0 +1,18 @@
+#! /bin/bash
+
+export https_proxy=http://localhost:8084
+export http_proxy=http://localhost:8084
+
+./bin/badgerproxyctl blog.isotoma.com 87.117.240.7 999999
+./bin/badgerproxyctl www.isotoma.com 87.117.240.55 999999
+
+curl -I http://badger/badgerproxy.gif
+curl -I --insecure https://blog.isotoma.com/
+curl -I http://www.isotoma.com/
+
+curl -i --silent --output /dev/null http://badger/badgerproxy.gif
+curl -i --insecure --silent --output /dev/null https://blog.isotoma.com/
+curl -i --silent --output /dev/null http://www.isotoma.com/
+
+corkscrew localhost 8084 localhost 22
+
Please sign in to comment.
Something went wrong with that request. Please try again.