aboutsummaryrefslogtreecommitdiffstats
path: root/mitmproxy/cmdline.py
diff options
context:
space:
mode:
Diffstat (limited to 'mitmproxy/cmdline.py')
-rw-r--r--mitmproxy/cmdline.py789
1 files changed, 789 insertions, 0 deletions
diff --git a/mitmproxy/cmdline.py b/mitmproxy/cmdline.py
new file mode 100644
index 00000000..fedd4f13
--- /dev/null
+++ b/mitmproxy/cmdline.py
@@ -0,0 +1,789 @@
+from __future__ import absolute_import
+import os
+import re
+
+import configargparse
+
+from netlib.tcp import Address, sslversion_choices
+import netlib.utils
+from . import filt, utils, version
+from .proxy import config
+
+APP_HOST = "mitm.it"
+APP_PORT = 80
+
+
+class ParseException(Exception):
+ pass
+
+
+def _parse_hook(s):
+ sep, rem = s[0], s[1:]
+ parts = rem.split(sep, 2)
+ if len(parts) == 2:
+ patt = ".*"
+ a, b = parts
+ elif len(parts) == 3:
+ patt, a, b = parts
+ else:
+ raise ParseException(
+ "Malformed hook specifier - too few clauses: %s" % s
+ )
+
+ if not a:
+ raise ParseException("Empty clause: %s" % str(patt))
+
+ if not filt.parse(patt):
+ raise ParseException("Malformed filter pattern: %s" % patt)
+
+ return patt, a, b
+
+
+def parse_replace_hook(s):
+ """
+ Returns a (pattern, regex, replacement) tuple.
+
+ The general form for a replacement hook is as follows:
+
+ /patt/regex/replacement
+
+ The first character specifies the separator. Example:
+
+ :~q:foo:bar
+
+ If only two clauses are specified, the pattern is set to match
+ universally (i.e. ".*"). Example:
+
+ /foo/bar/
+
+ Clauses are parsed from left to right. Extra separators are taken to be
+ part of the final clause. For instance, the replacement clause below is
+ "foo/bar/":
+
+ /one/two/foo/bar/
+
+ Checks that pattern and regex are both well-formed. Raises
+ ParseException on error.
+ """
+ patt, regex, replacement = _parse_hook(s)
+ try:
+ re.compile(regex)
+ except re.error as e:
+ raise ParseException("Malformed replacement regex: %s" % str(e.message))
+ return patt, regex, replacement
+
+
+def parse_setheader(s):
+ """
+ Returns a (pattern, header, value) tuple.
+
+ The general form for a replacement hook is as follows:
+
+ /patt/header/value
+
+ The first character specifies the separator. Example:
+
+ :~q:foo:bar
+
+ If only two clauses are specified, the pattern is set to match
+ universally (i.e. ".*"). Example:
+
+ /foo/bar/
+
+ Clauses are parsed from left to right. Extra separators are taken to be
+ part of the final clause. For instance, the value clause below is
+ "foo/bar/":
+
+ /one/two/foo/bar/
+
+ Checks that pattern and regex are both well-formed. Raises
+ ParseException on error.
+ """
+ return _parse_hook(s)
+
+
+def parse_server_spec(url):
+ try:
+ p = netlib.utils.parse_url(url)
+ if p[0] not in ("http", "https"):
+ raise ValueError()
+ except ValueError:
+ raise configargparse.ArgumentTypeError(
+ "Invalid server specification: %s" % url
+ )
+
+ address = Address(p[1:3])
+ scheme = p[0].lower()
+ return config.ServerSpec(scheme, address)
+
+
+def get_common_options(options):
+ stickycookie, stickyauth = None, None
+ if options.stickycookie_filt:
+ stickycookie = options.stickycookie_filt
+
+ if options.stickyauth_filt:
+ stickyauth = options.stickyauth_filt
+
+ stream_large_bodies = utils.parse_size(options.stream_large_bodies)
+
+ reps = []
+ for i in options.replace:
+ try:
+ p = parse_replace_hook(i)
+ except ParseException as e:
+ raise configargparse.ArgumentTypeError(e.message)
+ reps.append(p)
+ for i in options.replace_file:
+ try:
+ patt, rex, path = parse_replace_hook(i)
+ except ParseException as e:
+ raise configargparse.ArgumentTypeError(e.message)
+ try:
+ v = open(path, "rb").read()
+ except IOError as e:
+ raise configargparse.ArgumentTypeError(
+ "Could not read replace file: %s" % path
+ )
+ reps.append((patt, rex, v))
+
+ setheaders = []
+ for i in options.setheader:
+ try:
+ p = parse_setheader(i)
+ except ParseException as e:
+ raise configargparse.ArgumentTypeError(e.message)
+ setheaders.append(p)
+
+ 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,
+ replacements=reps,
+ setheaders=setheaders,
+ server_replay=options.server_replay,
+ scripts=options.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
+ )
+
+
+def basic_options(parser):
+ parser.add_argument(
+ '--version',
+ action='version',
+ version="%(prog)s" + " " + version.VERSION
+ )
+ parser.add_argument(
+ '--shortversion',
+ action='version',
+ help="show program's short version number and exit",
+ version=version.VERSION
+ )
+ parser.add_argument(
+ "--anticache",
+ action="store_true", dest="anticache", default=False,
+
+ help="""
+ Strip out request headers that might cause the server to return
+ 304-not-modified.
+ """
+ )
+ 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
+ )
+ parser.add_argument(
+ "--host",
+ action="store_true", dest="showhost", default=False,
+ help="Use the Host header to construct URLs for display."
+ )
+ parser.add_argument(
+ "-q", "--quiet",
+ action="store_true", dest="quiet",
+ help="Quiet."
+ )
+ parser.add_argument(
+ "-r", "--read-flows",
+ action="store", dest="rfile", default=None,
+ help="Read flows from file."
+ )
+ parser.add_argument(
+ "-s", "--script",
+ action="append", type=str, dest="scripts", default=[],
+ metavar='"script.py --bar"',
+ help="""
+ Run a script. Surround with quotes to pass script arguments. Can be
+ passed multiple times.
+ """
+ )
+ parser.add_argument(
+ "-t", "--stickycookie",
+ action="store",
+ dest="stickycookie_filt",
+ default=None,
+ metavar="FILTER",
+ help="Set sticky cookie filter. Matched against requests."
+ )
+ parser.add_argument(
+ "-u", "--stickyauth",
+ action="store", dest="stickyauth_filt", default=None, metavar="FILTER",
+ help="Set sticky auth filter. Matched against requests."
+ )
+ parser.add_argument(
+ "-v", "--verbose",
+ action="store_const", dest="verbose", default=1, const=2,
+ help="Increase event log verbosity."
+ )
+ outfile = parser.add_mutually_exclusive_group()
+ outfile.add_argument(
+ "-w", "--wfile",
+ action="store", dest="outfile", type=lambda f: (f, "wb"),
+ help="Write flows to file."
+ )
+ outfile.add_argument(
+ "-a", "--afile",
+ action="store", dest="outfile", type=lambda f: (f, "ab"),
+ help="Append flows to file."
+ )
+ parser.add_argument(
+ "-z", "--anticomp",
+ action="store_true", dest="anticomp", default=False,
+ help="Try to convince servers to send us un-compressed data."
+ )
+ parser.add_argument(
+ "-Z", "--body-size-limit",
+ action="store", dest="body_size_limit", default=None,
+ metavar="SIZE",
+ help="Byte size limit of HTTP request and response bodies."
+ " Understands k/m/g suffixes, i.e. 3m for 3 megabytes."
+ )
+ parser.add_argument(
+ "--stream",
+ action="store", dest="stream_large_bodies", default=None,
+ metavar="SIZE",
+ help="""
+ Stream data to the client if response body exceeds the given
+ threshold. If streamed, the body will not be stored in any way.
+ Understands k/m/g suffixes, i.e. 3m for 3 megabytes.
+ """
+ )
+
+
+def proxy_modes(parser):
+ group = parser.add_argument_group("Proxy Modes").add_mutually_exclusive_group()
+ group.add_argument(
+ "-R", "--reverse",
+ action="store",
+ type=parse_server_spec,
+ dest="reverse_proxy",
+ default=None,
+ help="""
+ Forward all requests to upstream HTTP server:
+ http[s][2http[s]]://host[:port]
+ """
+ )
+ group.add_argument(
+ "--socks",
+ action="store_true", dest="socks_proxy", default=False,
+ help="Set SOCKS5 proxy mode."
+ )
+ group.add_argument(
+ "-T", "--transparent",
+ action="store_true", dest="transparent_proxy", default=False,
+ help="Set transparent proxy mode."
+ )
+ group.add_argument(
+ "-U", "--upstream",
+ action="store",
+ type=parse_server_spec,
+ dest="upstream_proxy",
+ default=None,
+ help="Forward all requests to upstream proxy server: http://host[:port]"
+ )
+
+
+def proxy_options(parser):
+ group = parser.add_argument_group("Proxy Options")
+ group.add_argument(
+ "-b", "--bind-address",
+ action="store", type=str, dest="addr", default='',
+ help="Address to bind proxy to (defaults to all interfaces)"
+ )
+ group.add_argument(
+ "-I", "--ignore",
+ action="append", type=str, dest="ignore_hosts", default=[],
+ metavar="HOST",
+ help="""
+ Ignore host and forward all traffic without processing it. In
+ transparent mode, it is recommended to use an IP address (range),
+ not the hostname. In regular mode, only SSL traffic is ignored and
+ the hostname should be used. The supplied value is interpreted as a
+ regular expression and matched on the ip or the hostname. Can be
+ passed multiple times.
+ """
+ )
+ group.add_argument(
+ "--tcp",
+ action="append", type=str, dest="tcp_hosts", default=[],
+ metavar="HOST",
+ help="""
+ Generic TCP SSL proxy mode for all hosts that match the pattern.
+ Similar to --ignore, but SSL connections are intercepted. The
+ communication contents are printed to the event log in verbose mode.
+ """
+ )
+ group.add_argument(
+ "-n", "--no-server",
+ action="store_true", dest="no_server",
+ help="Don't start a proxy server."
+ )
+ group.add_argument(
+ "-p", "--port",
+ action="store", type=int, dest="port", default=8080,
+ help="Proxy service port."
+ )
+ http2 = group.add_mutually_exclusive_group()
+ # !!!
+ # Watch out: We raise a RuntimeError in mitmproxy.proxy.config if http2 is enabled,
+ # but the OpenSSL version does not have ALPN support (which is the default on Ubuntu 14.04).
+ # Do not simply set --http2 as enabled by default.
+ # !!!
+ http2.add_argument("--http2", action="store_true", dest="http2")
+ http2.add_argument("--no-http2", action="store_false", dest="http2",
+ help="Explicitly enable/disable experimental HTTP2 support. "
+ "Disabled by default. "
+ "Default value will change in a future version."
+ )
+ rawtcp = group.add_mutually_exclusive_group()
+ rawtcp.add_argument("--raw-tcp", action="store_true", dest="rawtcp")
+ rawtcp.add_argument("--no-raw-tcp", action="store_false", dest="rawtcp",
+ help="Explicitly enable/disable experimental raw tcp support. "
+ "Disabled by default. "
+ "Default value will change in a future version."
+ )
+
+
+def proxy_ssl_options(parser):
+ # TODO: Agree to consistently either use "upstream" or "server".
+ group = parser.add_argument_group("SSL")
+ group.add_argument(
+ "--cert",
+ dest='certs',
+ default=[],
+ type=str,
+ metavar="SPEC",
+ action="append",
+ help='Add an SSL certificate. SPEC is of the form "[domain=]path". '
+ 'The domain may include a wildcard, and is equal to "*" if not specified. '
+ 'The file at path is a certificate in PEM format. If a private key is included '
+ 'in the PEM, it is used, else the default key in the conf dir is used. '
+ 'The PEM file should contain the full certificate chain, with the leaf certificate '
+ '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,
+ help="Set supported ciphers for client connections. (OpenSSL Syntax)"
+ )
+ group.add_argument(
+ "--ciphers-server", action="store",
+ type=str, dest="ciphers_server", default=None,
+ help="Set supported ciphers for server connections. (OpenSSL Syntax)"
+ )
+ group.add_argument(
+ "--client-certs", action="store",
+ type=str, dest="clientcerts", default=None,
+ help="Client certificate file or directory."
+ )
+ group.add_argument(
+ "--no-upstream-cert", default=False,
+ action="store_true", dest="no_upstream_cert",
+ help="Don't connect to upstream server to look up certificate details."
+ )
+ group.add_argument(
+ "--verify-upstream-cert", default=False,
+ action="store_true", dest="ssl_verify_upstream_cert",
+ help="Verify upstream server SSL/TLS certificates and fail if invalid "
+ "or not present."
+ )
+ group.add_argument(
+ "--upstream-trusted-cadir", default=None, action="store",
+ dest="ssl_verify_upstream_trusted_cadir",
+ help="Path to a directory of trusted CA certificates for upstream "
+ "server verification prepared using the c_rehash tool."
+ )
+ group.add_argument(
+ "--upstream-trusted-ca", default=None, action="store",
+ dest="ssl_verify_upstream_trusted_ca",
+ help="Path to a PEM formatted trusted CA certificate."
+ )
+ group.add_argument(
+ "--ssl-version-client", dest="ssl_version_client",
+ default="secure", action="store",
+ choices=sslversion_choices.keys(),
+ help="Set supported SSL/TLS versions for client connections. "
+ "SSLv2, SSLv3 and 'all' are INSECURE. Defaults to secure, which is TLS1.0+."
+ )
+ group.add_argument(
+ "--ssl-version-server", dest="ssl_version_server",
+ default="secure", action="store",
+ choices=sslversion_choices.keys(),
+ help="Set supported SSL/TLS versions for server connections. "
+ "SSLv2, SSLv3 and 'all' are INSECURE. Defaults to secure, which is TLS1.0+."
+ )
+
+
+def onboarding_app(parser):
+ group = parser.add_argument_group("Onboarding App")
+ group.add_argument(
+ "--noapp",
+ action="store_false", dest="app", default=True,
+ help="Disable the mitmproxy onboarding app."
+ )
+ group.add_argument(
+ "--app-host",
+ action="store", dest="app_host", default=APP_HOST, metavar="host",
+ help="""
+ Domain to serve the onboarding app from. For transparent mode, use
+ an IP when a DNS entry for the app domain is not present. Default:
+ %s
+ """ % APP_HOST
+ )
+ group.add_argument(
+ "--app-port",
+ action="store",
+ dest="app_port",
+ default=APP_PORT,
+ type=int,
+ metavar="80",
+ help="Port to serve the onboarding app from."
+ )
+
+
+def client_replay(parser):
+ group = parser.add_argument_group("Client Replay")
+ group.add_argument(
+ "-c", "--client-replay",
+ action="append", dest="client_replay", default=None, metavar="PATH",
+ help="Replay client requests from a saved file."
+ )
+
+
+def server_replay(parser):
+ group = parser.add_argument_group("Server Replay")
+ group.add_argument(
+ "-S", "--server-replay",
+ action="append", dest="server_replay", default=None, metavar="PATH",
+ help="Replay server responses from a saved file."
+ )
+ group.add_argument(
+ "-k", "--kill",
+ action="store_true", dest="kill", default=False,
+ help="Kill extra requests during replay."
+ )
+ group.add_argument(
+ "--rheader",
+ action="append", dest="rheaders", type=str,
+ help="Request headers to be considered during replay. "
+ "Can be passed multiple times."
+ )
+ group.add_argument(
+ "--norefresh",
+ action="store_true", dest="norefresh", default=False,
+ help="""
+ Disable response refresh, which updates times in cookies and headers
+ for replayed responses.
+ """
+ )
+ group.add_argument(
+ "--no-pop",
+ action="store_true", dest="nopop", default=False,
+ help="Disable response pop from response flow. "
+ "This makes it possible to replay same response multiple times."
+ )
+ payload = group.add_mutually_exclusive_group()
+ payload.add_argument(
+ "--replay-ignore-content",
+ action="store_true", dest="replay_ignore_content", default=False,
+ help="""
+ Ignore request's content while searching for a saved flow to replay
+ """
+ )
+ payload.add_argument(
+ "--replay-ignore-payload-param",
+ action="append", dest="replay_ignore_payload_params", type=str,
+ help="""
+ Request's payload parameters (application/x-www-form-urlencoded or multipart/form-data) to
+ be ignored while searching for a saved flow to replay.
+ Can be passed multiple times.
+ """
+ )
+
+ group.add_argument(
+ "--replay-ignore-param",
+ action="append", dest="replay_ignore_params", type=str,
+ help="""
+ Request's parameters to be ignored while searching for a saved flow
+ to replay. Can be passed multiple times.
+ """
+ )
+ group.add_argument(
+ "--replay-ignore-host",
+ action="store_true",
+ dest="replay_ignore_host",
+ default=False,
+ help="Ignore request's destination host while searching for a saved flow to replay")
+
+
+def replacements(parser):
+ group = parser.add_argument_group(
+ "Replacements",
+ """
+ Replacements are of the form "/pattern/regex/replacement", where
+ the separator can be any character. Please see the documentation
+ for more information.
+ """.strip()
+ )
+ group.add_argument(
+ "--replace",
+ action="append", type=str, dest="replace", default=[],
+ metavar="PATTERN",
+ help="Replacement pattern."
+ )
+ group.add_argument(
+ "--replace-from-file",
+ action="append", type=str, dest="replace_file", default=[],
+ metavar="PATH",
+ help="""
+ Replacement pattern, where the replacement clause is a path to a
+ file.
+ """
+ )
+
+
+def set_headers(parser):
+ group = parser.add_argument_group(
+ "Set Headers",
+ """
+ Header specifications are of the form "/pattern/header/value",
+ where the separator can be any character. Please see the
+ documentation for more information.
+ """.strip()
+ )
+ group.add_argument(
+ "--setheader",
+ action="append", type=str, dest="setheader", default=[],
+ metavar="PATTERN",
+ help="Header set pattern."
+ )
+
+
+def proxy_authentication(parser):
+ group = parser.add_argument_group(
+ "Proxy Authentication",
+ """
+ Specify which users are allowed to access the proxy and the method
+ used for authenticating them.
+ """
+ ).add_mutually_exclusive_group()
+ group.add_argument(
+ "--nonanonymous",
+ action="store_true", dest="auth_nonanonymous",
+ help="Allow access to any user long as a credentials are specified."
+ )
+
+ group.add_argument(
+ "--singleuser",
+ action="store", dest="auth_singleuser", type=str,
+ metavar="USER",
+ help="""
+ Allows access to a a single user, specified in the form
+ username:password.
+ """
+ )
+ group.add_argument(
+ "--htpasswd",
+ action="store", dest="auth_htpasswd", type=str,
+ metavar="PATH",
+ help="Allow access to users specified in an Apache htpasswd file."
+ )
+
+
+def common_options(parser):
+ basic_options(parser)
+ proxy_modes(parser)
+ proxy_options(parser)
+ proxy_ssl_options(parser)
+ onboarding_app(parser)
+ client_replay(parser)
+ server_replay(parser)
+ replacements(parser)
+ set_headers(parser)
+ proxy_authentication(parser)
+
+
+def mitmproxy():
+ # Don't import mitmproxy.console for mitmdump, urwid is not available on all
+ # platforms.
+ from .console import palettes
+
+ parser = configargparse.ArgumentParser(
+ 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")
+ ],
+ add_config_file_help=True,
+ add_env_var_help=True
+ )
+ common_options(parser)
+ parser.add_argument(
+ "--palette", type=str, default=palettes.DEFAULT,
+ action="store", dest="palette",
+ choices=sorted(palettes.palettes.keys()),
+ help="Select color palette: " + ", ".join(palettes.palettes.keys())
+ )
+ parser.add_argument(
+ "--palette-transparent",
+ action="store_true", dest="palette_transparent", default=False,
+ help="Set transparent background for palette."
+ )
+ parser.add_argument(
+ "-e", "--eventlog",
+ action="store_true", dest="eventlog",
+ help="Show event log."
+ )
+ parser.add_argument(
+ "-f", "--follow",
+ action="store_true", dest="follow",
+ help="Follow flow list."
+ )
+ parser.add_argument(
+ "--no-mouse",
+ action="store_true", dest="no_mouse",
+ help="Disable mouse interaction."
+ )
+ group = parser.add_argument_group(
+ "Filters",
+ "See help in mitmproxy for filter expression syntax."
+ )
+ group.add_argument(
+ "-i", "--intercept", action="store",
+ type=str, dest="intercept", default=None,
+ help="Intercept filter expression."
+ )
+ group.add_argument(
+ "-l", "--limit", action="store",
+ type=str, dest="limit", default=None,
+ help="Limit filter expression."
+ )
+ return parser
+
+
+def mitmdump():
+ parser = configargparse.ArgumentParser(
+ 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")
+ ],
+ add_config_file_help=True,
+ add_env_var_help=True
+ )
+
+ common_options(parser)
+ parser.add_argument(
+ "--keepserving",
+ action="store_true", dest="keepserving", default=False,
+ help="""
+ Continue serving after client playback or file read. We exit by
+ default.
+ """
+ )
+ parser.add_argument(
+ "-d", "--detail",
+ action="count", dest="flow_detail", default=1,
+ help="Increase flow detail display level. Can be passed multiple times."
+ )
+ parser.add_argument('args', nargs="...")
+ return parser
+
+
+def mitmweb():
+ parser = configargparse.ArgumentParser(
+ 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")
+ ],
+ add_config_file_help=True,
+ add_env_var_help=True
+ )
+
+ group = parser.add_argument_group("Mitmweb")
+ group.add_argument(
+ "--wport",
+ action="store", type=int, dest="wport", default=8081,
+ metavar="PORT",
+ help="Mitmweb port."
+ )
+ group.add_argument(
+ "--wiface",
+ action="store", dest="wiface", default="127.0.0.1",
+ metavar="IFACE",
+ help="Mitmweb interface."
+ )
+ group.add_argument(
+ "--wdebug",
+ action="store_true", dest="wdebug",
+ help="Turn on mitmweb debugging"
+ )
+ group.add_argument(
+ "--wsingleuser",
+ action="store", dest="wsingleuser", type=str,
+ metavar="USER",
+ help="""
+ Allows access to a a single user, specified in the form
+ username:password.
+ """
+ )
+ group.add_argument(
+ "--whtpasswd",
+ action="store", dest="whtpasswd", type=str,
+ metavar="PATH",
+ help="Allow access to users specified in an Apache htpasswd file."
+ )
+
+ common_options(parser)
+ group = parser.add_argument_group(
+ "Filters",
+ "See help in mitmproxy for filter expression syntax."
+ )
+ group.add_argument(
+ "-i", "--intercept", action="store",
+ type=str, dest="intercept", default=None,
+ help="Intercept filter expression."
+ )
+ return parser