aboutsummaryrefslogtreecommitdiffstats
path: root/mitmproxy/cmdline.py
diff options
context:
space:
mode:
Diffstat (limited to 'mitmproxy/cmdline.py')
-rw-r--r--mitmproxy/cmdline.py225
1 files changed, 145 insertions, 80 deletions
diff --git a/mitmproxy/cmdline.py b/mitmproxy/cmdline.py
index 507ddfc7..696542f6 100644
--- a/mitmproxy/cmdline.py
+++ b/mitmproxy/cmdline.py
@@ -1,21 +1,32 @@
from __future__ import absolute_import, print_function, division
-import base64
import os
import re
import configargparse
+from mitmproxy import exceptions
from mitmproxy import filt
-from mitmproxy.proxy import config
+from mitmproxy import platform
from netlib import human
-from netlib import strutils
from netlib import tcp
from netlib import version
-from netlib.http import url
APP_HOST = "mitm.it"
APP_PORT = 80
+CA_DIR = "~/.mitmproxy"
+
+# We manually need to specify this, otherwise OpenSSL may select a non-HTTP2 cipher by default.
+# https://mozilla.github.io/server-side-tls/ssl-config-generator/?server=apache-2.2.15&openssl=1.0.2&hsts=yes&profile=old
+DEFAULT_CLIENT_CIPHERS = "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:" \
+ "ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:" \
+ "ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:" \
+ "ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:" \
+ "DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:" \
+ "DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:" \
+ "AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:DES-CBC3-SHA:" \
+ "HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:" \
+ "!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA"
class ParseException(Exception):
@@ -107,110 +118,164 @@ def parse_setheader(s):
return _parse_hook(s)
-def parse_server_spec(spec):
- try:
- p = url.parse(spec)
- if p[0] not in (b"http", b"https"):
- raise ValueError()
- except ValueError:
- raise configargparse.ArgumentTypeError(
- "Invalid server specification: %s" % spec
- )
-
- address = tcp.Address(p[1:3])
- scheme = p[0].lower()
- 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 b"Basic" + b" " + base64.b64encode(strutils.always_bytes(auth))
-
-
-def get_common_options(options):
+def get_common_options(args):
stickycookie, stickyauth = None, None
- if options.stickycookie_filt:
- stickycookie = options.stickycookie_filt
+ if args.stickycookie_filt:
+ stickycookie = args.stickycookie_filt
- if options.stickyauth_filt:
- stickyauth = options.stickyauth_filt
+ if args.stickyauth_filt:
+ stickyauth = args.stickyauth_filt
- stream_large_bodies = options.stream_large_bodies
+ stream_large_bodies = args.stream_large_bodies
if stream_large_bodies:
stream_large_bodies = human.parse_size(stream_large_bodies)
reps = []
- for i in options.replace:
+ for i in args.replace:
try:
p = parse_replace_hook(i)
except ParseException as e:
- raise configargparse.ArgumentTypeError(e)
+ raise exceptions.OptionsError(e)
reps.append(p)
- for i in options.replace_file:
+ for i in args.replace_file:
try:
patt, rex, path = parse_replace_hook(i)
except ParseException as e:
- raise configargparse.ArgumentTypeError(e)
+ raise exceptions.OptionsError(e)
try:
v = open(path, "rb").read()
except IOError as e:
- raise configargparse.ArgumentTypeError(
+ raise exceptions.OptionsError(
"Could not read replace file: %s" % path
)
reps.append((patt, rex, v))
setheaders = []
- for i in options.setheader:
+ for i in args.setheader:
try:
p = parse_setheader(i)
except ParseException as e:
- raise configargparse.ArgumentTypeError(e)
+ raise exceptions.OptionsError(e)
setheaders.append(p)
- if options.outfile and options.outfile[0] == options.rfile:
- if options.outfile[1] == "wb":
- raise configargparse.ArgumentTypeError(
+ if args.outfile and args.outfile[0] == args.rfile:
+ if args.outfile[1] == "wb":
+ raise exceptions.OptionsError(
"Cannot use '{}' for both reading and writing flows. "
- "Are you looking for --afile?".format(options.rfile)
+ "Are you looking for --afile?".format(args.rfile)
)
else:
- raise configargparse.ArgumentTypeError(
+ raise exceptions.OptionsError(
"Cannot use '{}' for both reading and appending flows. "
"That would trigger an infinite loop."
)
+ # Proxy config
+ certs = []
+ for i in args.certs:
+ parts = i.split("=", 1)
+ if len(parts) == 1:
+ parts = ["*", parts[0]]
+ certs.append(parts)
+
+ body_size_limit = args.body_size_limit
+ if body_size_limit:
+ try:
+ body_size_limit = human.parse_size(body_size_limit)
+ except ValueError as e:
+ raise exceptions.OptionsError(
+ "Invalid body size limit specification: %s" % body_size_limit
+ )
+
+ # Establish proxy mode
+ c = 0
+ mode, upstream_server = "regular", None
+ if args.transparent_proxy:
+ c += 1
+ if not platform.resolver:
+ raise exceptions.OptionsError(
+ "Transparent mode not supported on this platform."
+ )
+ mode = "transparent"
+ if args.socks_proxy:
+ c += 1
+ mode = "socks5"
+ if args.reverse_proxy:
+ c += 1
+ mode = "reverse"
+ upstream_server = args.reverse_proxy
+ if args.upstream_proxy:
+ c += 1
+ mode = "upstream"
+ upstream_server = args.upstream_proxy
+ if c > 1:
+ raise exceptions.OptionsError(
+ "Transparent, SOCKS5, reverse and upstream proxy mode "
+ "are mutually exclusive. Read the docs on proxy modes "
+ "to understand why."
+ )
+ if args.add_upstream_certs_to_client_chain and args.no_upstream_cert:
+ raise exceptions.OptionsError(
+ "The no-upstream-cert and add-upstream-certs-to-client-chain "
+ "options are mutually exclusive. If no-upstream-cert is enabled "
+ "then the upstream certificate is not retrieved before generating "
+ "the client certificate chain."
+ )
+
return dict(
- app=options.app,
- app_host=options.app_host,
- app_port=options.app_port,
-
- anticache=options.anticache,
- anticomp=options.anticomp,
- client_replay=options.client_replay,
- kill=options.kill,
- no_server=options.no_server,
- refresh_server_playback=not options.norefresh,
- rheaders=options.rheaders,
- rfile=options.rfile,
+ app=args.app,
+ app_host=args.app_host,
+ app_port=args.app_port,
+
+ anticache=args.anticache,
+ anticomp=args.anticomp,
+ client_replay=args.client_replay,
+ kill=args.kill,
+ no_server=args.no_server,
+ refresh_server_playback=not args.norefresh,
+ rheaders=args.rheaders,
+ rfile=args.rfile,
replacements=reps,
setheaders=setheaders,
- server_replay=options.server_replay,
- scripts=options.scripts,
+ server_replay=args.server_replay,
+ scripts=args.scripts,
stickycookie=stickycookie,
stickyauth=stickyauth,
stream_large_bodies=stream_large_bodies,
- showhost=options.showhost,
- outfile=options.outfile,
- verbosity=options.verbose,
- nopop=options.nopop,
- replay_ignore_content=options.replay_ignore_content,
- replay_ignore_params=options.replay_ignore_params,
- replay_ignore_payload_params=options.replay_ignore_payload_params,
- replay_ignore_host=options.replay_ignore_host
+ showhost=args.showhost,
+ outfile=args.outfile,
+ verbosity=args.verbose,
+ nopop=args.nopop,
+ replay_ignore_content=args.replay_ignore_content,
+ replay_ignore_params=args.replay_ignore_params,
+ replay_ignore_payload_params=args.replay_ignore_payload_params,
+ replay_ignore_host=args.replay_ignore_host,
+
+ auth_nonanonymous = args.auth_nonanonymous,
+ auth_singleuser = args.auth_singleuser,
+ auth_htpasswd = args.auth_htpasswd,
+ add_upstream_certs_to_client_chain = args.add_upstream_certs_to_client_chain,
+ body_size_limit = body_size_limit,
+ cadir = args.cadir,
+ certs = certs,
+ ciphers_client = args.ciphers_client,
+ ciphers_server = args.ciphers_server,
+ clientcerts = args.clientcerts,
+ http2 = args.http2,
+ ignore_hosts = args.ignore_hosts,
+ listen_host = args.addr,
+ listen_port = args.port,
+ mode = mode,
+ no_upstream_cert = args.no_upstream_cert,
+ rawtcp = args.rawtcp,
+ upstream_server = upstream_server,
+ upstream_auth = args.upstream_auth,
+ ssl_version_client = args.ssl_version_client,
+ ssl_version_server = args.ssl_version_server,
+ ssl_verify_upstream_cert = args.ssl_verify_upstream_cert,
+ ssl_verify_upstream_trusted_cadir = args.ssl_verify_upstream_trusted_cadir,
+ ssl_verify_upstream_trusted_ca = args.ssl_verify_upstream_trusted_ca,
+ tcp_hosts = args.tcp_hosts,
)
@@ -242,8 +307,8 @@ def basic_options(parser):
)
parser.add_argument(
"--cadir",
- action="store", type=str, dest="cadir", default=config.CA_DIR,
- help="Location of the default mitmproxy CA files. (%s)" % config.CA_DIR
+ action="store", type=str, dest="cadir", default=CA_DIR,
+ help="Location of the default mitmproxy CA files. (%s)" % CA_DIR
)
parser.add_argument(
"--host",
@@ -327,7 +392,7 @@ def proxy_modes(parser):
group.add_argument(
"-R", "--reverse",
action="store",
- type=parse_server_spec,
+ type=str,
dest="reverse_proxy",
default=None,
help="""
@@ -350,7 +415,7 @@ def proxy_modes(parser):
group.add_argument(
"-U", "--upstream",
action="store",
- type=parse_server_spec,
+ type=str,
dest="upstream_proxy",
default=None,
help="Forward all requests to upstream proxy server: http://host[:port]"
@@ -408,7 +473,7 @@ def proxy_options(parser):
parser.add_argument(
"--upstream-auth",
action="store", dest="upstream_auth", default=None,
- type=parse_upstream_auth,
+ type=str,
help="""
Proxy Authentication:
username:password
@@ -441,7 +506,7 @@ def proxy_ssl_options(parser):
'as the first entry. Can be passed multiple times.')
group.add_argument(
"--ciphers-client", action="store",
- type=str, dest="ciphers_client", default=config.DEFAULT_CLIENT_CIPHERS,
+ type=str, dest="ciphers_client", default=DEFAULT_CLIENT_CIPHERS,
help="Set supported ciphers for client connections. (OpenSSL Syntax)"
)
group.add_argument(
@@ -696,8 +761,8 @@ def mitmproxy():
usage="%(prog)s [options]",
args_for_setting_config_path=["--conf"],
default_config_files=[
- os.path.join(config.CA_DIR, "common.conf"),
- os.path.join(config.CA_DIR, "mitmproxy.conf")
+ os.path.join(CA_DIR, "common.conf"),
+ os.path.join(CA_DIR, "mitmproxy.conf")
],
add_config_file_help=True,
add_env_var_help=True
@@ -751,8 +816,8 @@ def mitmdump():
usage="%(prog)s [options] [filter]",
args_for_setting_config_path=["--conf"],
default_config_files=[
- os.path.join(config.CA_DIR, "common.conf"),
- os.path.join(config.CA_DIR, "mitmdump.conf")
+ os.path.join(CA_DIR, "common.conf"),
+ os.path.join(CA_DIR, "mitmdump.conf")
],
add_config_file_help=True,
add_env_var_help=True
@@ -781,8 +846,8 @@ def mitmweb():
usage="%(prog)s [options]",
args_for_setting_config_path=["--conf"],
default_config_files=[
- os.path.join(config.CA_DIR, "common.conf"),
- os.path.join(config.CA_DIR, "mitmweb.conf")
+ os.path.join(CA_DIR, "common.conf"),
+ os.path.join(CA_DIR, "mitmweb.conf")
],
add_config_file_help=True,
add_env_var_help=True