This commit is contained in:
Thomas Kriechbaumer 2017-01-19 20:55:19 +01:00
parent 86174eb6ad
commit 006eb39cc5
11 changed files with 133 additions and 42 deletions

View File

@ -1,35 +1,39 @@
from mitmproxy.addons import anticache from mitmproxy.addons import anticache
from mitmproxy.addons import anticomp 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 clientplayback
from mitmproxy.addons import streamfile from mitmproxy.addons import disable_h2c_upgrade
from mitmproxy.addons import onboarding from mitmproxy.addons import onboarding
from mitmproxy.addons import proxyauth from mitmproxy.addons import proxyauth
from mitmproxy.addons import replace from mitmproxy.addons import replace
from mitmproxy.addons import script from mitmproxy.addons import script
from mitmproxy.addons import setheaders
from mitmproxy.addons import serverplayback from mitmproxy.addons import serverplayback
from mitmproxy.addons import setheaders
from mitmproxy.addons import stickyauth from mitmproxy.addons import stickyauth
from mitmproxy.addons import stickycookie from mitmproxy.addons import stickycookie
from mitmproxy.addons import streambodies from mitmproxy.addons import streambodies
from mitmproxy.addons import streamfile
from mitmproxy.addons import upstream_auth from mitmproxy.addons import upstream_auth
from mitmproxy.addons import disable_h2c_upgrade
def default_addons(): def default_addons():
return [ return [
onboarding.Onboarding(),
proxyauth.ProxyAuth(),
anticache.AntiCache(), anticache.AntiCache(),
anticomp.AntiComp(), 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(), stickyauth.StickyAuth(),
stickycookie.StickyCookie(), stickycookie.StickyCookie(),
script.ScriptLoader(),
streamfile.StreamFile(),
streambodies.StreamBodies(), streambodies.StreamBodies(),
replace.Replace(), streamfile.StreamFile(),
setheaders.SetHeaders(),
serverplayback.ServerPlayback(),
clientplayback.ClientPlayback(),
upstream_auth.UpstreamAuth(), upstream_auth.UpstreamAuth(),
disable_h2c_upgrade.DisableH2CleartextUpgrade(),
] ]

View File

@ -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",
)

View File

@ -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",
)

View File

@ -5,9 +5,9 @@ from mitmproxy import log
class TermLog: class TermLog:
def __init__(self, outfile=sys.stdout): def __init__(self, outfile=None):
self.options = None self.options = None
self.outfile = outfile self.outfile = outfile or sys.stdout
def configure(self, options, updated): def configure(self, options, updated):
self.options = options self.options = options

View File

@ -3,8 +3,8 @@ import ssl
import time import time
import datetime import datetime
import ipaddress import ipaddress
import sys import sys
from pyasn1.type import univ, constraint, char, namedtype, tag from pyasn1.type import univ, constraint, char, namedtype, tag
from pyasn1.codec.der.decoder import decode from pyasn1.codec.der.decoder import decode
from pyasn1.error import PyAsn1Error from pyasn1.error import PyAsn1Error
@ -13,8 +13,8 @@ import OpenSSL
from mitmproxy.types import serializable from mitmproxy.types import serializable
# Default expiry must not be too long: https://github.com/mitmproxy/mitmproxy/issues/815 # Default expiry must not be too long: https://github.com/mitmproxy/mitmproxy/issues/815
DEFAULT_EXP = 94608000 # = 24 * 60 * 60 * 365 * 3 DEFAULT_EXP = 94608000 # = 24 * 60 * 60 * 365 * 3
# Generated with "openssl dhparam". It's too slow to generate this on startup. # Generated with "openssl dhparam". It's too slow to generate this on startup.
DEFAULT_DHPARAM = b""" DEFAULT_DHPARAM = b"""
-----BEGIN DH PARAMETERS----- -----BEGIN DH PARAMETERS-----

View File

@ -33,8 +33,6 @@ from mitmproxy.tools.console import statusbar
from mitmproxy.tools.console import window from mitmproxy.tools.console import window
from mitmproxy.utils import strutils from mitmproxy.utils import strutils
from mitmproxy.net import tcp
EVENTLOG_SIZE = 10000 EVENTLOG_SIZE = 10000
@ -272,16 +270,6 @@ class ConsoleMaster(master.Master):
print("Could not load file: {}".format(ret), file=sys.stderr) print("Could not load file: {}".format(ret), file=sys.stderr)
sys.exit(1) 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( self.loop.set_alarm_in(
0.0001, 0.0001,
lambda *args: self.view_flowlist() lambda *args: self.view_flowlist()

View File

@ -6,7 +6,6 @@ from mitmproxy import addons
from mitmproxy import options from mitmproxy import options
from mitmproxy import master from mitmproxy import master
from mitmproxy.addons import dumper, termlog from mitmproxy.addons import dumper, termlog
from mitmproxy.net import tcp
class DumpError(Exception): class DumpError(Exception):
@ -30,7 +29,13 @@ class Options(options.Options):
class DumpMaster(master.Master): 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) master.Master.__init__(self, options, server)
self.has_errored = False self.has_errored = False
if with_termlog: if with_termlog:
@ -38,8 +43,6 @@ class DumpMaster(master.Master):
self.addons.add(*addons.default_addons()) self.addons.add(*addons.default_addons())
if with_dumper: if with_dumper:
self.addons.add(dumper.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: if not self.options.no_server:
self.add_log( self.add_log(
@ -47,13 +50,6 @@ class DumpMaster(master.Master):
"info" "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: if options.rfile:
try: try:
self.load_flows_file(options.rfile) self.load_flows_file(options.rfile)

View File

@ -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)

View File

@ -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

View File

@ -1,10 +1,13 @@
import os import os
import pytest
from unittest import mock
from mitmproxy.tools import dump
from mitmproxy import proxy from mitmproxy import proxy
from mitmproxy.test import tutils
from mitmproxy import log from mitmproxy import log
from mitmproxy import controller from mitmproxy import controller
from mitmproxy.tools import dump
from mitmproxy.test import tutils
from . import mastertest from . import mastertest
@ -37,3 +40,17 @@ class TestDumpMaster(mastertest.MasterTest):
ent.reply = controller.DummyReply() ent.reply = controller.DummyReply()
m.log(ent) m.log(ent)
assert m.has_errored 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

View File

@ -1,13 +1,16 @@
from mitmproxy.tools.web import master from mitmproxy.tools.web import master
from mitmproxy import proxy from mitmproxy import proxy
from mitmproxy import options from mitmproxy import options
from mitmproxy.proxy.config import ProxyConfig
from . import mastertest from . import mastertest
class TestWebMaster(mastertest.MasterTest): class TestWebMaster(mastertest.MasterTest):
def mkmaster(self, **opts): def mkmaster(self, **opts):
o = options.Options(**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): def test_basic(self):
m = self.mkmaster() m = self.mkmaster()