diff options
-rw-r--r-- | docs/scripting/events.rst | 8 | ||||
-rw-r--r-- | docs/transparent/osx.rst | 3 | ||||
-rw-r--r-- | mitmproxy/connections.py | 18 | ||||
-rw-r--r-- | mitmproxy/net/tls.py | 21 | ||||
-rw-r--r-- | mitmproxy/proxy/protocol/http_replay.py | 10 | ||||
-rw-r--r-- | mitmproxy/proxy/protocol/tls.py | 15 | ||||
-rw-r--r-- | mitmproxy/tools/console/commander/commander.py | 2 | ||||
-rw-r--r-- | mitmproxy/tools/console/consoleaddons.py | 11 | ||||
-rw-r--r-- | mitmproxy/tools/console/defaultkeys.py | 6 | ||||
-rw-r--r-- | setup.py | 2 | ||||
-rw-r--r-- | test/mitmproxy/test_connections.py | 9 | ||||
-rw-r--r-- | test/mitmproxy/tools/console/test_keymap.py | 2 | ||||
-rw-r--r-- | web/src/js/components/FlowTable/FlowColumns.jsx | 2 |
13 files changed, 69 insertions, 40 deletions
diff --git a/docs/scripting/events.rst b/docs/scripting/events.rst index 4d74b220..d8b1fbb8 100644 --- a/docs/scripting/events.rst +++ b/docs/scripting/events.rst @@ -100,10 +100,10 @@ HTTP Events * - .. py:function:: http_connect(flow) - Called when we receive an HTTP CONNECT request. Setting a non 2xx - response on the flow will return the response to the client abort the - connection. CONNECT requests and responses do not generate the usual - HTTP handler events. CONNECT requests are only valid in regular and - upstream proxy modes. + response on the flow will return the response to the client and abort + the connection. CONNECT requests and responses do not generate the + usual HTTP handler events. CONNECT requests are only valid in regular + and upstream proxy modes. *flow* A ``models.HTTPFlow`` object. The flow is guaranteed to have diff --git a/docs/transparent/osx.rst b/docs/transparent/osx.rst index 40e91fac..5d4ec612 100644 --- a/docs/transparent/osx.rst +++ b/docs/transparent/osx.rst @@ -17,8 +17,7 @@ Note that this means we don't support transparent mode for earlier versions of O .. code-block:: none - rdr on en2 inet proto tcp to any port 80 -> 127.0.0.1 port 8080 - rdr on en2 inet proto tcp to any port 443 -> 127.0.0.1 port 8080 + rdr on en0 inet proto tcp to any port {80, 443} -> 127.0.0.1 port 8080 These rules tell pf to redirect all traffic destined for port 80 or 443 to the local mitmproxy instance running on port 8080. You should diff --git a/mitmproxy/connections.py b/mitmproxy/connections.py index 86565b7b..9c47985c 100644 --- a/mitmproxy/connections.py +++ b/mitmproxy/connections.py @@ -253,7 +253,7 @@ class ServerConnection(tcp.TCPClient, stateobject.StateObject): address=address, ip_address=address, cert=None, - sni=None, + sni=address[0], alpn_proto_negotiated=None, tls_version=None, source_address=('', 0), @@ -276,21 +276,21 @@ class ServerConnection(tcp.TCPClient, stateobject.StateObject): self.wfile.write(message) self.wfile.flush() - def establish_tls(self, clientcerts, sni, **kwargs): + def establish_tls(self, *, sni=None, client_certs=None, **kwargs): if sni and not isinstance(sni, str): raise ValueError("sni must be str, not " + type(sni).__name__) - clientcert = None - if clientcerts: - if os.path.isfile(clientcerts): - clientcert = clientcerts + client_cert = None + if client_certs: + if os.path.isfile(client_certs): + client_cert = client_certs else: path = os.path.join( - clientcerts, + client_certs, self.address[0].encode("idna").decode()) + ".pem" if os.path.exists(path): - clientcert = path + client_cert = path - self.convert_to_tls(cert=clientcert, sni=sni, **kwargs) + self.convert_to_tls(cert=client_cert, sni=sni, **kwargs) self.sni = sni self.alpn_proto_negotiated = self.get_alpn_proto_negotiated() self.tls_version = self.connection.get_protocol_version_name() diff --git a/mitmproxy/net/tls.py b/mitmproxy/net/tls.py index 0e43a2ac..f8eeb44b 100644 --- a/mitmproxy/net/tls.py +++ b/mitmproxy/net/tls.py @@ -13,6 +13,7 @@ import certifi from OpenSSL import SSL from kaitaistruct import KaitaiStream +import mitmproxy.options # noqa from mitmproxy import exceptions, certs from mitmproxy.contrib.kaitaistruct import tls_client_hello from mitmproxy.net import check @@ -57,6 +58,26 @@ METHOD_NAMES = { } +def client_arguments_from_options(options: "mitmproxy.options.Options") -> dict: + + if options.ssl_insecure: + verify = SSL.VERIFY_NONE + else: + verify = SSL.VERIFY_PEER + + method, tls_options = VERSION_CHOICES[options.ssl_version_server] + + return { + "verify": verify, + "method": method, + "options": tls_options, + "ca_path": options.ssl_verify_upstream_trusted_cadir, + "ca_pemfile": options.ssl_verify_upstream_trusted_ca, + "client_certs": options.client_certs, + "cipher_list": options.ciphers_server, + } + + class MasterSecretLogger: def __init__(self, filename): self.filename = filename diff --git a/mitmproxy/proxy/protocol/http_replay.py b/mitmproxy/proxy/protocol/http_replay.py index 022e8133..0f3be1ea 100644 --- a/mitmproxy/proxy/protocol/http_replay.py +++ b/mitmproxy/proxy/protocol/http_replay.py @@ -9,7 +9,7 @@ from mitmproxy import http from mitmproxy import flow from mitmproxy import options from mitmproxy import connections -from mitmproxy.net import server_spec +from mitmproxy.net import server_spec, tls from mitmproxy.net.http import http1 from mitmproxy.coretypes import basethread from mitmproxy.utils import human @@ -76,8 +76,8 @@ class RequestReplayThread(basethread.BaseThread): if resp.status_code != 200: raise exceptions.ReplayException("Upstream server refuses CONNECT request") server.establish_tls( - self.options.client_certs, - sni=self.f.server_conn.sni + sni=self.f.server_conn.sni, + **tls.client_arguments_from_options(self.options) ) r.first_line_format = "relative" else: @@ -91,8 +91,8 @@ class RequestReplayThread(basethread.BaseThread): server.connect() if r.scheme == "https": server.establish_tls( - self.options.client_certs, - sni=self.f.server_conn.sni + sni=self.f.server_conn.sni, + **tls.client_arguments_from_options(self.options) ) r.first_line_format = "relative" diff --git a/mitmproxy/proxy/protocol/tls.py b/mitmproxy/proxy/protocol/tls.py index d04c9801..876c1162 100644 --- a/mitmproxy/proxy/protocol/tls.py +++ b/mitmproxy/proxy/protocol/tls.py @@ -424,6 +424,9 @@ class TlsLayer(base.Layer): # * which results in garbage because the layers don' match. alpn = [self.client_conn.get_alpn_proto_negotiated()] + # We pass through the list of ciphers send by the client, because some HTTP/2 servers + # will select a non-HTTP/2 compatible cipher from our default list and then hang up + # because it's incompatible with h2. :-) ciphers_server = self.config.options.ciphers_server if not ciphers_server and self._client_tls: ciphers_server = [] @@ -432,16 +435,12 @@ class TlsLayer(base.Layer): ciphers_server.append(CIPHER_ID_NAME_MAP[id]) ciphers_server = ':'.join(ciphers_server) + args = net_tls.client_arguments_from_options(self.config.options) + args["cipher_list"] = ciphers_server self.server_conn.establish_tls( - self.config.client_certs, - self.server_sni, - method=self.config.openssl_method_server, - options=self.config.openssl_options_server, - verify=self.config.openssl_verification_mode_server, - ca_path=self.config.options.ssl_verify_upstream_trusted_cadir, - ca_pemfile=self.config.options.ssl_verify_upstream_trusted_ca, - cipher_list=ciphers_server, + sni=self.server_sni, alpn_protos=alpn, + **args ) tls_cert_err = self.server_conn.ssl_verification_error if tls_cert_err is not None: diff --git a/mitmproxy/tools/console/commander/commander.py b/mitmproxy/tools/console/commander/commander.py index e2088e71..566c42e6 100644 --- a/mitmproxy/tools/console/commander/commander.py +++ b/mitmproxy/tools/console/commander/commander.py @@ -47,7 +47,7 @@ CompletionState = typing.NamedTuple( ) -class CommandBuffer(): +class CommandBuffer: def __init__(self, master: mitmproxy.master.Master, start: str = "") -> None: self.master = master self.text = self.flatten(start) diff --git a/mitmproxy/tools/console/consoleaddons.py b/mitmproxy/tools/console/consoleaddons.py index 5907fe95..aa46501b 100644 --- a/mitmproxy/tools/console/consoleaddons.py +++ b/mitmproxy/tools/console/consoleaddons.py @@ -277,6 +277,17 @@ class ConsoleAddon: """ signals.status_prompt_command.send(partial=" ".join(partial)) # type: ignore + @command.command("console.command.set") + def console_command_set(self, option: str) -> None: + """ + Prompt the user to set an option of the form "key[=value]". + """ + option_value = getattr(self.master.options, option, None) + current_value = option_value if option_value else "" + self.master.commands.call( + "console.command set %s=%s" % (option, current_value) + ) + @command.command("console.view.keybindings") def view_keybindings(self) -> None: """View the commands list.""" diff --git a/mitmproxy/tools/console/defaultkeys.py b/mitmproxy/tools/console/defaultkeys.py index d01d9b7e..084ef262 100644 --- a/mitmproxy/tools/console/defaultkeys.py +++ b/mitmproxy/tools/console/defaultkeys.py @@ -26,8 +26,8 @@ def map(km): km.add("ctrl b", "console.nav.pageup", ["global"], "Page up") km.add("I", "console.intercept.toggle", ["global"], "Toggle intercept") - km.add("i", "console.command set intercept=", ["global"], "Set intercept") - km.add("W", "console.command set save_stream_file=", ["global"], "Stream to file") + km.add("i", "console.command.set intercept", ["global"], "Set intercept") + km.add("W", "console.command.set save_stream_file", ["global"], "Stream to file") km.add("A", "flow.resume @all", ["flowlist", "flowview"], "Resume all intercepted flows") km.add("a", "flow.resume @focus", ["flowlist", "flowview"], "Resume this intercepted flow") km.add( @@ -46,7 +46,7 @@ def map(km): ["flowlist", "flowview"], "Export this flow to file" ) - km.add("f", "console.command set view_filter=", ["flowlist"], "Set view filter") + km.add("f", "console.command.set view_filter", ["flowlist"], "Set view filter") km.add("F", "set console_focus_follow=toggle", ["flowlist"], "Set focus follow") km.add( "ctrl l", @@ -69,7 +69,7 @@ setup( 'h11>=0.7.0,<0.8', "h2>=3.0.1,<4", "hyperframe>=5.1.0,<6", - "kaitaistruct>=0.7, <0.8", + "kaitaistruct>=0.7,<0.9", "ldap3>=2.4,<2.5", "passlib>=1.6.5, <1.8", "pyasn1>=0.3.1,<0.5", diff --git a/test/mitmproxy/test_connections.py b/test/mitmproxy/test_connections.py index 9e5d89f1..00cdbc87 100644 --- a/test/mitmproxy/test_connections.py +++ b/test/mitmproxy/test_connections.py @@ -155,7 +155,7 @@ class TestServerConnection: def test_sni(self): c = connections.ServerConnection(('', 1234)) with pytest.raises(ValueError, matches='sni must be str, not '): - c.establish_tls(None, b'foobar') + c.establish_tls(sni=b'foobar') def test_state(self): c = tflow.tserver_conn() @@ -222,17 +222,16 @@ class TestServerConnectionTLS(tservers.ServerTestBase): def handle(self): self.finish() - @pytest.mark.parametrize("clientcert", [ + @pytest.mark.parametrize("client_certs", [ None, tutils.test_data.path("mitmproxy/data/clientcert"), tutils.test_data.path("mitmproxy/data/clientcert/client.pem"), ]) - def test_tls(self, clientcert): + def test_tls(self, client_certs): c = connections.ServerConnection(("127.0.0.1", self.port)) c.connect() - c.establish_tls(clientcert, "foo.com") + c.establish_tls(client_certs=client_certs) assert c.connected() - assert c.sni == "foo.com" assert c.tls_established c.close() c.finish() diff --git a/test/mitmproxy/tools/console/test_keymap.py b/test/mitmproxy/tools/console/test_keymap.py index 00e64991..7b475ff8 100644 --- a/test/mitmproxy/tools/console/test_keymap.py +++ b/test/mitmproxy/tools/console/test_keymap.py @@ -42,7 +42,7 @@ def test_join(): km = keymap.Keymap(tctx.master) km.add("key", "str", ["options"], "help1") km.add("key", "str", ["commands"]) - return + assert len(km.bindings) == 1 assert len(km.bindings[0].contexts) == 2 assert km.bindings[0].help == "help1" diff --git a/web/src/js/components/FlowTable/FlowColumns.jsx b/web/src/js/components/FlowTable/FlowColumns.jsx index 02a4fba1..e60ed487 100644 --- a/web/src/js/components/FlowTable/FlowColumns.jsx +++ b/web/src/js/components/FlowTable/FlowColumns.jsx @@ -119,7 +119,7 @@ export function TimeColumn({ flow }) { return ( <td className="col-time"> {flow.response ? ( - formatTimeDelta(1000 * (flow.response.timestamp_end - flow.server_conn.timestamp_start)) + formatTimeDelta(1000 * (flow.response.timestamp_end - flow.request.timestamp_start)) ) : ( '...' )} |