From 006eb39cc562ff3f8b741f9d022503081861827f Mon Sep 17 00:00:00 2001 From: Thomas Kriechbaumer Date: Thu, 19 Jan 2017 20:55:19 +0100 Subject: [PATCH] fix #939 --- mitmproxy/addons/__init__.py | 28 ++++++++++++++---------- mitmproxy/addons/check_alpn.py | 17 ++++++++++++++ mitmproxy/addons/check_ca.py | 24 ++++++++++++++++++++ mitmproxy/addons/termlog.py | 4 ++-- mitmproxy/certs.py | 4 ++-- mitmproxy/tools/console/master.py | 12 ---------- mitmproxy/tools/dump.py | 18 ++++++--------- test/mitmproxy/addons/test_check_alpn.py | 23 +++++++++++++++++++ test/mitmproxy/addons/test_check_ca.py | 19 ++++++++++++++++ test/mitmproxy/test_tools_dump.py | 21 ++++++++++++++++-- test/mitmproxy/test_web_master.py | 5 ++++- 11 files changed, 133 insertions(+), 42 deletions(-) create mode 100644 mitmproxy/addons/check_alpn.py create mode 100644 mitmproxy/addons/check_ca.py create mode 100644 test/mitmproxy/addons/test_check_alpn.py create mode 100644 test/mitmproxy/addons/test_check_ca.py diff --git a/mitmproxy/addons/__init__.py b/mitmproxy/addons/__init__.py index 8a2f2974f..2e1d1c67e 100644 --- a/mitmproxy/addons/__init__.py +++ b/mitmproxy/addons/__init__.py @@ -1,35 +1,39 @@ from mitmproxy.addons import anticache from mitmproxy.addons import anticomp +from mitmproxy.addons import check_alpn +from mitmproxy.addons import check_ca from mitmproxy.addons import clientplayback -from mitmproxy.addons import streamfile +from mitmproxy.addons import disable_h2c_upgrade from mitmproxy.addons import onboarding from mitmproxy.addons import proxyauth from mitmproxy.addons import replace from mitmproxy.addons import script -from mitmproxy.addons import setheaders from mitmproxy.addons import serverplayback +from mitmproxy.addons import setheaders from mitmproxy.addons import stickyauth from mitmproxy.addons import stickycookie from mitmproxy.addons import streambodies +from mitmproxy.addons import streamfile from mitmproxy.addons import upstream_auth -from mitmproxy.addons import disable_h2c_upgrade def default_addons(): return [ - onboarding.Onboarding(), - proxyauth.ProxyAuth(), anticache.AntiCache(), anticomp.AntiComp(), + check_alpn.CheckALPN(), + check_ca.CheckCA(), + clientplayback.ClientPlayback(), + disable_h2c_upgrade.DisableH2CleartextUpgrade(), + onboarding.Onboarding(), + proxyauth.ProxyAuth(), + replace.Replace(), + script.ScriptLoader(), + serverplayback.ServerPlayback(), + setheaders.SetHeaders(), stickyauth.StickyAuth(), stickycookie.StickyCookie(), - script.ScriptLoader(), - streamfile.StreamFile(), streambodies.StreamBodies(), - replace.Replace(), - setheaders.SetHeaders(), - serverplayback.ServerPlayback(), - clientplayback.ClientPlayback(), + streamfile.StreamFile(), upstream_auth.UpstreamAuth(), - disable_h2c_upgrade.DisableH2CleartextUpgrade(), ] diff --git a/mitmproxy/addons/check_alpn.py b/mitmproxy/addons/check_alpn.py new file mode 100644 index 000000000..c288d788c --- /dev/null +++ b/mitmproxy/addons/check_alpn.py @@ -0,0 +1,17 @@ +import mitmproxy +from mitmproxy.net import tcp + + +class CheckALPN: + def __init__(self): + self.failed = False + + def configure(self, options, updated): + self.failed = mitmproxy.ctx.master.options.http2 and not tcp.HAS_ALPN + if self.failed: + mitmproxy.ctx.master.add_log( + "HTTP/2 is disabled because ALPN support missing!\n" + "OpenSSL 1.0.2+ required to support HTTP/2 connections.\n" + "Use --no-http2 to silence this warning.", + "warn", + ) diff --git a/mitmproxy/addons/check_ca.py b/mitmproxy/addons/check_ca.py new file mode 100644 index 000000000..a83ab8e19 --- /dev/null +++ b/mitmproxy/addons/check_ca.py @@ -0,0 +1,24 @@ +import mitmproxy + + +class CheckCA: + def __init__(self): + self.failed = False + + def configure(self, options, updated): + has_ca = ( + mitmproxy.ctx.master.server and + mitmproxy.ctx.master.server.config and + mitmproxy.ctx.master.server.config.certstore and + mitmproxy.ctx.master.server.config.certstore.default_ca + ) + if has_ca: + self.failed = mitmproxy.ctx.master.server.config.certstore.default_ca.has_expired() + if self.failed: + mitmproxy.ctx.master.add_log( + "The mitmproxy certificate authority has expired!\n" + "Please delete all CA-related files in your ~/.mitmproxy folder.\n" + "The CA will be regenerated automatically after restarting mitmproxy.\n" + "Then make sure all your clients have the new CA installed.", + "warn", + ) diff --git a/mitmproxy/addons/termlog.py b/mitmproxy/addons/termlog.py index b75f5f5a9..f7739efee 100644 --- a/mitmproxy/addons/termlog.py +++ b/mitmproxy/addons/termlog.py @@ -5,9 +5,9 @@ from mitmproxy import log class TermLog: - def __init__(self, outfile=sys.stdout): + def __init__(self, outfile=None): self.options = None - self.outfile = outfile + self.outfile = outfile or sys.stdout def configure(self, options, updated): self.options = options diff --git a/mitmproxy/certs.py b/mitmproxy/certs.py index 4e4eb4d19..4b939c805 100644 --- a/mitmproxy/certs.py +++ b/mitmproxy/certs.py @@ -3,8 +3,8 @@ import ssl import time import datetime import ipaddress - import sys + from pyasn1.type import univ, constraint, char, namedtype, tag from pyasn1.codec.der.decoder import decode from pyasn1.error import PyAsn1Error @@ -13,8 +13,8 @@ import OpenSSL from mitmproxy.types import serializable # Default expiry must not be too long: https://github.com/mitmproxy/mitmproxy/issues/815 - DEFAULT_EXP = 94608000 # = 24 * 60 * 60 * 365 * 3 + # Generated with "openssl dhparam". It's too slow to generate this on startup. DEFAULT_DHPARAM = b""" -----BEGIN DH PARAMETERS----- diff --git a/mitmproxy/tools/console/master.py b/mitmproxy/tools/console/master.py index 8afdce2c3..10f8cbf50 100644 --- a/mitmproxy/tools/console/master.py +++ b/mitmproxy/tools/console/master.py @@ -33,8 +33,6 @@ from mitmproxy.tools.console import statusbar from mitmproxy.tools.console import window from mitmproxy.utils import strutils -from mitmproxy.net import tcp - EVENTLOG_SIZE = 10000 @@ -272,16 +270,6 @@ class ConsoleMaster(master.Master): print("Could not load file: {}".format(ret), file=sys.stderr) sys.exit(1) - self.loop.set_alarm_in(0.01, self.ticker) - if self.options.http2 and not tcp.HAS_ALPN: # pragma: no cover - def http2err(*args, **kwargs): - signals.status_message.send( - message = "HTTP/2 disabled - OpenSSL 1.0.2+ required." - " Use --no-http2 to silence this warning.", - expire=5 - ) - self.loop.set_alarm_in(0.01, http2err) - self.loop.set_alarm_in( 0.0001, lambda *args: self.view_flowlist() diff --git a/mitmproxy/tools/dump.py b/mitmproxy/tools/dump.py index 4e2844a1f..e1e40fb0e 100644 --- a/mitmproxy/tools/dump.py +++ b/mitmproxy/tools/dump.py @@ -6,7 +6,6 @@ from mitmproxy import addons from mitmproxy import options from mitmproxy import master from mitmproxy.addons import dumper, termlog -from mitmproxy.net import tcp class DumpError(Exception): @@ -30,7 +29,13 @@ class Options(options.Options): class DumpMaster(master.Master): - def __init__(self, options, server, with_termlog=True, with_dumper=True): + def __init__( + self, + options: Options, + server, + with_termlog=True, + with_dumper=True, + ) -> None: master.Master.__init__(self, options, server) self.has_errored = False if with_termlog: @@ -38,8 +43,6 @@ class DumpMaster(master.Master): self.addons.add(*addons.default_addons()) if with_dumper: self.addons.add(dumper.Dumper()) - # This line is just for type hinting - self.options = self.options # type: Options if not self.options.no_server: self.add_log( @@ -47,13 +50,6 @@ class DumpMaster(master.Master): "info" ) - if self.server and self.options.http2 and not tcp.HAS_ALPN: # pragma: no cover - self.add_log( - "ALPN support missing (OpenSSL 1.0.2+ required)!\n" - "HTTP/2 is disabled. Use --no-http2 to silence this warning.", - "error" - ) - if options.rfile: try: self.load_flows_file(options.rfile) diff --git a/test/mitmproxy/addons/test_check_alpn.py b/test/mitmproxy/addons/test_check_alpn.py new file mode 100644 index 000000000..2dc0c8357 --- /dev/null +++ b/test/mitmproxy/addons/test_check_alpn.py @@ -0,0 +1,23 @@ +from mitmproxy.addons import check_alpn +from mitmproxy.test import taddons +from ...conftest import requires_alpn + + +class TestCheckALPN: + + @requires_alpn + def test_check_alpn(self): + msg = 'ALPN support missing' + + with taddons.context() as tctx: + a = check_alpn.CheckALPN() + tctx.configure(a) + assert not any(msg in m for l, m in tctx.master.event_log) + + def test_check_no_alpn(self, disable_alpn): + msg = 'ALPN support missing' + + with taddons.context() as tctx: + a = check_alpn.CheckALPN() + tctx.configure(a) + assert any(msg in m for l, m in tctx.master.event_log) diff --git a/test/mitmproxy/addons/test_check_ca.py b/test/mitmproxy/addons/test_check_ca.py new file mode 100644 index 000000000..fc64621c4 --- /dev/null +++ b/test/mitmproxy/addons/test_check_ca.py @@ -0,0 +1,19 @@ +import pytest +from unittest import mock + +from mitmproxy.addons import check_ca +from mitmproxy.test import taddons + + +class TestCheckCA: + + @pytest.mark.parametrize('expired', [False, True]) + def test_check_ca(self, expired): + msg = 'The mitmproxy certificate authority has expired!' + + with taddons.context() as tctx: + tctx.master.server = mock.MagicMock() + tctx.master.server.config.certstore.default_ca.has_expired = mock.MagicMock(return_value=expired) + a = check_ca.CheckCA() + tctx.configure(a) + assert any(msg in m for l, m in tctx.master.event_log) is expired diff --git a/test/mitmproxy/test_tools_dump.py b/test/mitmproxy/test_tools_dump.py index 2e64d2d2b..505f514b9 100644 --- a/test/mitmproxy/test_tools_dump.py +++ b/test/mitmproxy/test_tools_dump.py @@ -1,10 +1,13 @@ import os +import pytest +from unittest import mock -from mitmproxy.tools import dump from mitmproxy import proxy -from mitmproxy.test import tutils from mitmproxy import log from mitmproxy import controller +from mitmproxy.tools import dump + +from mitmproxy.test import tutils from . import mastertest @@ -37,3 +40,17 @@ class TestDumpMaster(mastertest.MasterTest): ent.reply = controller.DummyReply() m.log(ent) assert m.has_errored + + @pytest.mark.parametrize("termlog", [False, True]) + def test_addons_termlog(self, termlog): + with mock.patch('sys.stdout'): + o = dump.Options() + m = dump.DumpMaster(o, proxy.DummyServer(), with_termlog=termlog) + assert (m.addons.get('termlog') is not None) == termlog + + @pytest.mark.parametrize("dumper", [False, True]) + def test_addons_dumper(self, dumper): + with mock.patch('sys.stdout'): + o = dump.Options() + m = dump.DumpMaster(o, proxy.DummyServer(), with_dumper=dumper) + assert (m.addons.get('dumper') is not None) == dumper diff --git a/test/mitmproxy/test_web_master.py b/test/mitmproxy/test_web_master.py index 08dce8f31..3591284dd 100644 --- a/test/mitmproxy/test_web_master.py +++ b/test/mitmproxy/test_web_master.py @@ -1,13 +1,16 @@ from mitmproxy.tools.web import master from mitmproxy import proxy from mitmproxy import options +from mitmproxy.proxy.config import ProxyConfig + from . import mastertest class TestWebMaster(mastertest.MasterTest): def mkmaster(self, **opts): o = options.Options(**opts) - return master.WebMaster(o, proxy.DummyServer(o)) + c = ProxyConfig(o) + return master.WebMaster(o, proxy.DummyServer(c)) def test_basic(self): m = self.mkmaster()