add basic auth support for reverse proxy mode

This commit is contained in:
Maximilian Hils 2016-08-13 11:04:53 -07:00
parent a0391db1ae
commit a9401472cd
4 changed files with 55 additions and 13 deletions

View File

@ -132,6 +132,8 @@ class HttpLayer(base.Layer):
# We cannot rely on server_conn.tls_established,
# see https://github.com/mitmproxy/mitmproxy/issues/925
self.__initial_server_tls = None
# Requests happening after CONNECT do not need Proxy-Authorization headers.
self.http_authenticated = False
def __call__(self):
if self.mode == "transparent":
@ -146,7 +148,7 @@ class HttpLayer(base.Layer):
# Proxy Authentication conceptually does not work in transparent mode.
# We catch this misconfiguration on startup. Here, we sort out requests
# after a successful CONNECT request (which do not need to be validated anymore)
if self.mode != "transparent" and not self.authenticate(request):
if not (self.http_authenticated or self.authenticate(request)):
return
# Make sure that the incoming request matches our expectations
@ -239,6 +241,7 @@ class HttpLayer(base.Layer):
return self.set_server(address)
def handle_regular_mode_connect(self, request):
self.http_authenticated = True
self.set_server((request.host, request.port))
self.send_response(models.make_connect_response(request.data.http_version))
layer = self.ctx.next_layer(self)
@ -397,10 +400,17 @@ class HttpLayer(base.Layer):
if self.config.authenticator.authenticate(request.headers):
self.config.authenticator.clean(request.headers)
else:
self.send_response(models.make_error_response(
407,
"Proxy Authentication Required",
http.Headers(**self.config.authenticator.auth_challenge_headers())
))
if self.mode == "transparent":
self.send_response(models.make_error_response(
401,
"Authentication Required",
http.Headers(**self.config.authenticator.auth_challenge_headers())
))
else:
self.send_response(models.make_error_response(
407,
"Proxy Authentication Required",
http.Headers(**self.config.authenticator.auth_challenge_headers())
))
return False
return True

View File

@ -179,7 +179,13 @@ class ProxyConfig:
)
except ValueError as v:
raise exceptions.OptionsError(str(v))
self.authenticator = authentication.BasicProxyAuth(
password_manager,
"mitmproxy"
)
if options.mode == "reverse":
self.authenticator = authentication.BasicWebsiteAuth(
password_manager,
self.upstream_server.address
)
else:
self.authenticator = authentication.BasicProxyAuth(
password_manager,
"mitmproxy"
)

View File

@ -50,9 +50,9 @@ class NullProxyAuth(object):
return {}
class BasicProxyAuth(NullProxyAuth):
CHALLENGE_HEADER = 'Proxy-Authenticate'
AUTH_HEADER = 'Proxy-Authorization'
class BasicAuth(NullProxyAuth):
CHALLENGE_HEADER = None
AUTH_HEADER = None
def __init__(self, password_manager, realm):
NullProxyAuth.__init__(self, password_manager)
@ -80,6 +80,16 @@ class BasicProxyAuth(NullProxyAuth):
return {self.CHALLENGE_HEADER: 'Basic realm="%s"' % self.realm}
class BasicWebsiteAuth(BasicAuth):
CHALLENGE_HEADER = 'WWW-Authenticate'
AUTH_HEADER = 'Authorization'
class BasicProxyAuth(BasicAuth):
CHALLENGE_HEADER = 'Proxy-Authenticate'
AUTH_HEADER = 'Proxy-Authorization'
class PassMan(object):
def test(self, username_, password_token_):

View File

@ -313,6 +313,22 @@ class TestHTTPAuth(tservers.HTTPProxyTest):
assert ret.status_code == 202
class TestHTTPReverseAuth(tservers.ReverseProxyTest):
def test_auth(self):
self.master.options.auth_singleuser = "test:test"
assert self.pathod("202").status_code == 401
p = self.pathoc()
ret = p.request("""
get
'/p/202'
h'%s'='%s'
""" % (
http.authentication.BasicWebsiteAuth.AUTH_HEADER,
authentication.assemble_http_basic_auth("basic", "test", "test")
))
assert ret.status_code == 202
class TestHTTPS(tservers.HTTPProxyTest, CommonMixin, TcpMixin):
ssl = True
ssloptions = pathod.SSLOptions(request_client_cert=True)