Merge pull request #988 from xhy940801/master

Add upstream proxy authentication
This commit is contained in:
Thomas Kriechbaumer 2016-03-04 19:47:31 +01:00
commit d7e9dda85c
6 changed files with 46 additions and 1 deletions

View File

@ -1,6 +1,7 @@
from __future__ import absolute_import from __future__ import absolute_import
import os import os
import re import re
import base64
import configargparse import configargparse
@ -117,6 +118,15 @@ def parse_server_spec(url):
return config.ServerSpec(scheme, address) return config.ServerSpec(scheme, address)
def parse_upstream_auth(auth):
pattern = re.compile(".+:")
if pattern.search(auth) is None:
raise configargparse.ArgumentTypeError(
"Invalid upstream auth specification: %s" % auth
)
return "Basic" + " " + base64.b64encode(auth)
def get_common_options(options): def get_common_options(options):
stickycookie, stickyauth = None, None stickycookie, stickyauth = None, None
if options.stickycookie_filt: if options.stickycookie_filt:
@ -370,6 +380,15 @@ def proxy_options(parser):
If your OpenSSL version supports ALPN, HTTP/2 is enabled by default. If your OpenSSL version supports ALPN, HTTP/2 is enabled by default.
""" """
) )
parser.add_argument(
"--upstream-auth",
action="store", dest="upstream_auth", default=None,
type=parse_upstream_auth,
help="""
Proxy Authentication:
username:password
"""
)
rawtcp = group.add_mutually_exclusive_group() rawtcp = group.add_mutually_exclusive_group()
rawtcp.add_argument("--raw-tcp", action="store_true", dest="rawtcp") rawtcp.add_argument("--raw-tcp", action="store_true", dest="rawtcp")
rawtcp.add_argument("--no-raw-tcp", action="store_false", dest="rawtcp", rawtcp.add_argument("--no-raw-tcp", action="store_false", dest="rawtcp",

View File

@ -192,6 +192,9 @@ class HTTPRequest(MessageMixin, Request):
def __hash__(self): def __hash__(self):
return id(self) return id(self)
def set_auth(self, auth):
self.data.headers.set_all("Proxy-Authorization", (auth,))
def replace(self, pattern, repl, *args, **kwargs): def replace(self, pattern, repl, *args, **kwargs):
""" """
Replaces a regular expression pattern with repl in the headers, the Replaces a regular expression pattern with repl in the headers, the

View File

@ -179,6 +179,9 @@ class HttpLayer(Layer):
try: try:
flow = HTTPFlow(self.client_conn, self.server_conn, live=self) flow = HTTPFlow(self.client_conn, self.server_conn, live=self)
flow.request = request flow.request = request
# set upstream auth
if self.mode == "upstream" and self.config.upstream_auth is not None:
flow.request.set_auth(self.config.upstream_auth)
self.process_request_hook(flow) self.process_request_hook(flow)
if not flow.response: if not flow.response:

View File

@ -53,6 +53,7 @@ class ProxyConfig:
body_size_limit=None, body_size_limit=None,
mode="regular", mode="regular",
upstream_server=None, upstream_server=None,
upstream_auth = None,
authenticator=None, authenticator=None,
ignore_hosts=tuple(), ignore_hosts=tuple(),
tcp_hosts=tuple(), tcp_hosts=tuple(),
@ -77,8 +78,10 @@ class ProxyConfig:
self.mode = mode self.mode = mode
if upstream_server: if upstream_server:
self.upstream_server = ServerSpec(upstream_server[0], Address.wrap(upstream_server[1])) self.upstream_server = ServerSpec(upstream_server[0], Address.wrap(upstream_server[1]))
self.upstream_auth = upstream_auth
else: else:
self.upstream_server = None self.upstream_server = None
self.upstream_auth = None
self.check_ignore = HostMatcher(ignore_hosts) self.check_ignore = HostMatcher(ignore_hosts)
self.check_tcp = HostMatcher(tcp_hosts) self.check_tcp = HostMatcher(tcp_hosts)
@ -110,7 +113,7 @@ def process_proxy_options(parser, options):
body_size_limit = utils.parse_size(options.body_size_limit) body_size_limit = utils.parse_size(options.body_size_limit)
c = 0 c = 0
mode, upstream_server = "regular", None mode, upstream_server, upstream_auth = "regular", None, None
if options.transparent_proxy: if options.transparent_proxy:
c += 1 c += 1
if not platform.resolver: if not platform.resolver:
@ -127,6 +130,7 @@ def process_proxy_options(parser, options):
c += 1 c += 1
mode = "upstream" mode = "upstream"
upstream_server = options.upstream_proxy upstream_server = options.upstream_proxy
upstream_auth = options.upstream_auth
if c > 1: if c > 1:
return parser.error( return parser.error(
"Transparent, SOCKS5, reverse and upstream proxy mode " "Transparent, SOCKS5, reverse and upstream proxy mode "
@ -189,6 +193,7 @@ def process_proxy_options(parser, options):
body_size_limit=body_size_limit, body_size_limit=body_size_limit,
mode=mode, mode=mode,
upstream_server=upstream_server, upstream_server=upstream_server,
upstream_auth=upstream_auth,
ignore_hosts=options.ignore_hosts, ignore_hosts=options.ignore_hosts,
tcp_hosts=options.tcp_hosts, tcp_hosts=options.tcp_hosts,
http2=options.http2, http2=options.http2,

View File

@ -1,4 +1,5 @@
import argparse import argparse
import base64
from mitmproxy import cmdline from mitmproxy import cmdline
from . import tutils from . import tutils
@ -53,6 +54,16 @@ def test_parse_server_spec():
"http://") "http://")
def test_parse_upstream_auth():
tutils.raises("Invalid upstream auth specification", cmdline.parse_upstream_auth, "")
tutils.raises("Invalid upstream auth specification", cmdline.parse_upstream_auth, ":")
tutils.raises("Invalid upstream auth specification", cmdline.parse_upstream_auth, ":test")
assert cmdline.parse_upstream_auth(
"test:test") == "Basic" + " " + base64.b64encode("test:test")
assert cmdline.parse_upstream_auth(
"test:") == "Basic" + " " + base64.b64encode("test:")
def test_parse_setheaders(): def test_parse_setheaders():
x = cmdline.parse_setheader("/foo/bar/voing") x = cmdline.parse_setheader("/foo/bar/voing")
assert x == ("foo", "bar", "voing") assert x == ("foo", "bar", "voing")

View File

@ -92,6 +92,10 @@ class TestProcessProxyOptions:
self.assert_err("expected one argument", "-U") self.assert_err("expected one argument", "-U")
self.assert_err("Invalid server specification", "-U", "upstream") self.assert_err("Invalid server specification", "-U", "upstream")
self.assert_noerr("--upstream-auth", "test:test")
self.assert_err("expected one argument", "--upstream-auth")
self.assert_err("Invalid upstream auth specification", "--upstream-auth", "test")
self.assert_err("not allowed with", "-R", "http://localhost", "-T") self.assert_err("not allowed with", "-R", "http://localhost", "-T")
def test_socks_auth(self): def test_socks_auth(self):