From a9401472cd8002f6e35da8f180400a8b980fc22c Mon Sep 17 00:00:00 2001 From: Maximilian Hils Date: Sat, 13 Aug 2016 11:04:53 -0700 Subject: [PATCH] add basic auth support for reverse proxy mode --- mitmproxy/protocol/http.py | 22 ++++++++++++++++------ mitmproxy/proxy/config.py | 14 ++++++++++---- netlib/http/authentication.py | 16 +++++++++++++--- test/mitmproxy/test_server.py | 16 ++++++++++++++++ 4 files changed, 55 insertions(+), 13 deletions(-) diff --git a/mitmproxy/protocol/http.py b/mitmproxy/protocol/http.py index 2c70f2889..309a0844e 100644 --- a/mitmproxy/protocol/http.py +++ b/mitmproxy/protocol/http.py @@ -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 diff --git a/mitmproxy/proxy/config.py b/mitmproxy/proxy/config.py index cf75830a5..26f6a2369 100644 --- a/mitmproxy/proxy/config.py +++ b/mitmproxy/proxy/config.py @@ -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" + ) \ No newline at end of file diff --git a/netlib/http/authentication.py b/netlib/http/authentication.py index 38ea46d6c..58fc9bdcb 100644 --- a/netlib/http/authentication.py +++ b/netlib/http/authentication.py @@ -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_): diff --git a/test/mitmproxy/test_server.py b/test/mitmproxy/test_server.py index 78e9b5c7b..78a97dc34 100644 --- a/test/mitmproxy/test_server.py +++ b/test/mitmproxy/test_server.py @@ -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)