aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--libmproxy/app.py7
-rw-r--r--libmproxy/proxy.py98
-rw-r--r--test/test_app.py4
-rw-r--r--test/test_console_contentview.py2
-rw-r--r--test/test_proxy.py14
-rw-r--r--test/test_server.py5
-rw-r--r--test/tservers.py13
7 files changed, 63 insertions, 80 deletions
diff --git a/libmproxy/app.py b/libmproxy/app.py
index b046f712..3ebbb61b 100644
--- a/libmproxy/app.py
+++ b/libmproxy/app.py
@@ -1,5 +1,6 @@
import flask
import os.path
+import proxy
mapp = flask.Flask(__name__)
mapp.debug = True
@@ -16,14 +17,12 @@ def index():
@mapp.route("/cert/pem")
def certs_pem():
- capath = master().server.config.cacert
- p = os.path.splitext(capath)[0] + "-cert.pem"
+ p = os.path.join(master().server.config.confdir, proxy.CONF_BASENAME + "-cert.pem")
return flask.Response(open(p, "rb").read(), mimetype='application/x-x509-ca-cert')
@mapp.route("/cert/p12")
def certs_p12():
- capath = master().server.config.cacert
- p = os.path.splitext(capath)[0] + "-cert.p12"
+ p = os.path.join(master().server.config.confdir, proxy.CONF_BASENAME + "-cert.p12")
return flask.Response(open(p, "rb").read(), mimetype='application/x-pkcs12')
diff --git a/libmproxy/proxy.py b/libmproxy/proxy.py
index b0fb9449..1eebba07 100644
--- a/libmproxy/proxy.py
+++ b/libmproxy/proxy.py
@@ -4,9 +4,12 @@ from netlib import tcp, http, certutils, http_auth
import utils, version, platform, controller, stateobject
TRANSPARENT_SSL_PORTS = [443, 8443]
+CONF_BASENAME = "mitmproxy"
+CONF_DIR = "~/.mitmproxy"
CA_CERT_NAME = "mitmproxy-ca.pem"
+
class AddressPriority(object):
"""
Enum that signifies the priority of the given address when choosing the destination host.
@@ -38,15 +41,12 @@ class Log:
class ProxyConfig:
- def __init__(self, certfile=None, keyfile=None, cacert=None, clientcerts=None,
+ def __init__(self, confdir=CONF_DIR, clientcerts=None,
no_upstream_cert=False, body_size_limit=None, reverse_proxy=None,
forward_proxy=None, transparent_proxy=None, authenticator=None,
- ciphers=None
+ ciphers=None, certs=None
):
self.ciphers = ciphers
- self.certfile = certfile
- self.keyfile = keyfile
- self.cacert = cacert
self.clientcerts = clientcerts
self.no_upstream_cert = no_upstream_cert
self.body_size_limit = body_size_limit
@@ -54,7 +54,9 @@ class ProxyConfig:
self.forward_proxy = forward_proxy
self.transparent_proxy = transparent_proxy
self.authenticator = authenticator
- self.certstore = certutils.CertStore(cacert)
+ self.confdir = os.path.expanduser(confdir)
+ self.certstore = certutils.CertStore.from_store(confdir, CONF_BASENAME)
+
class ClientConnection(tcp.BaseHandler, stateobject.SimpleStateObject):
@@ -386,10 +388,9 @@ class ConnectionHandler:
if client:
if self.client_conn.ssl_established:
raise ProxyError(502, "SSL to Client already established.")
- dummycert = self.find_cert()
+ cert, key = self.find_cert()
self.client_conn.convert_to_ssl(
- dummycert,
- self.config.keyfile or self.config.cacert,
+ cert, key,
handle_sni = self.handle_sni,
cipher_list = self.config.ciphers
)
@@ -420,22 +421,18 @@ class ConnectionHandler:
self.channel.tell("log", Log(msg))
def find_cert(self):
- if self.config.certfile:
- with open(self.config.certfile, "rb") as f:
- return certutils.SSLCert.from_pem(f.read())
- else:
- host = self.server_conn.address.host
- sans = []
- if not self.config.no_upstream_cert or not self.server_conn.ssl_established:
- upstream_cert = self.server_conn.cert
- if upstream_cert.cn:
- host = upstream_cert.cn.decode("utf8").encode("idna")
- sans = upstream_cert.altnames
-
- ret = self.config.certstore.get_cert(host, sans)
- if not ret:
- raise ProxyError(502, "Unable to generate dummy cert.")
- return ret
+ host = self.server_conn.address.host
+ sans = []
+ if not self.config.no_upstream_cert or not self.server_conn.ssl_established:
+ upstream_cert = self.server_conn.cert
+ if upstream_cert.cn:
+ host = upstream_cert.cn.decode("utf8").encode("idna")
+ sans = upstream_cert.altnames
+
+ ret = self.config.certstore.get_cert(host, sans)
+ if not ret:
+ raise ProxyError(502, "Unable to generate dummy cert.")
+ return ret
def handle_sni(self, connection):
"""
@@ -451,9 +448,9 @@ class ConnectionHandler:
self.server_reconnect() # reconnect to upstream server with SNI
# Now, change client context to reflect changed certificate:
new_context = SSL.Context(SSL.TLSv1_METHOD)
- new_context.use_privatekey_file(self.config.certfile or self.config.cacert)
- dummycert = self.find_cert()
- new_context.use_certificate(dummycert.x509)
+ cert, key = self.find_cert()
+ new_context.use_privatekey_file(key)
+ new_context.use_certificate(cert.X509)
connection.set_context(new_context)
# An unhandled exception in this method will core dump PyOpenSSL, so
# make dang sure it doesn't happen.
@@ -510,14 +507,12 @@ class DummyServer:
def ssl_option_group(parser):
group = parser.add_argument_group("SSL")
group.add_argument(
- "--certfile", action="store",
- type=str, dest="certfile", default=None,
- help="SSL certificate in PEM format, optionally with the key in the same file."
- )
- group.add_argument(
- "--keyfile", action="store",
- type=str, dest="keyfile", default=None,
- help="Key matching certfile."
+ "--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. Can be passed multiple times.'
)
group.add_argument(
"--client-certs", action="store",
@@ -532,23 +527,6 @@ def ssl_option_group(parser):
def process_proxy_options(parser, options):
- if options.certfile:
- options.certfile = os.path.expanduser(options.certfile)
- if not os.path.exists(options.certfile):
- return parser.error("Certificate file does not exist: %s" % options.certfile)
-
- if options.keyfile:
- options.keyfile = os.path.expanduser(options.keyfile)
- if not os.path.exists(options.keyfile):
- return parser.error("Key file does not exist: %s" % options.keyfile)
-
- if options.certfile and not options.keyfile:
- options.keyfile = options.certfile
-
- cacert = os.path.join(options.confdir, CA_CERT_NAME)
- cacert = os.path.expanduser(cacert)
- if not os.path.exists(cacert):
- certutils.dummy_ca(cacert)
body_size_limit = utils.parse_size(options.body_size_limit)
if options.reverse_proxy and options.transparent_proxy:
return parser.error("Can't set both reverse proxy and transparent proxy.")
@@ -601,10 +579,17 @@ def process_proxy_options(parser, options):
else:
authenticator = http_auth.NullProxyAuth(None)
+ certs = []
+ for i in options.certs:
+ parts = i.split("=", 1)
+ if len(parts) == 1:
+ parts = ["*", parts[0]]
+ parts[1] = os.path.expanduser(parts[1])
+ if not os.path.exists(parts[1]):
+ parser.error("Certificate file does not exist: %s"%parts[1])
+ certs.append(parts)
+
return ProxyConfig(
- certfile=options.certfile,
- keyfile=options.keyfile,
- cacert=cacert,
clientcerts=options.clientcerts,
body_size_limit=body_size_limit,
no_upstream_cert=options.no_upstream_cert,
@@ -613,4 +598,5 @@ def process_proxy_options(parser, options):
transparent_proxy=trans,
authenticator=authenticator,
ciphers=options.ciphers,
+ certs = certs,
)
diff --git a/test/test_app.py b/test/test_app.py
index f0eab7cc..52cd1ba6 100644
--- a/test/test_app.py
+++ b/test/test_app.py
@@ -9,11 +9,9 @@ class TestApp(tservers.HTTPProxTest):
assert self.app("/").status_code == 200
def test_cert(self):
- path = tutils.test_data.path("data/confdir/") + "mitmproxy-ca-cert."
with tutils.tmpdir() as d:
for ext in ["pem", "p12"]:
resp = self.app("/cert/%s" % ext)
assert resp.status_code == 200
- with open(path + ext, "rb") as f:
- assert resp.content == f.read()
+ assert resp.content
diff --git a/test/test_console_contentview.py b/test/test_console_contentview.py
index a878ad4e..0aabd2c5 100644
--- a/test/test_console_contentview.py
+++ b/test/test_console_contentview.py
@@ -120,7 +120,7 @@ class TestContentView:
def test_view_css(self):
v = cv.ViewCSS()
- with open('./test/data/1.css', 'r') as fp:
+ with open(tutils.test_data.path('data/1.css'), 'r') as fp:
fixture_1 = fp.read()
result = v([], 'a', 100)
diff --git a/test/test_proxy.py b/test/test_proxy.py
index 5ff00290..b15e3f84 100644
--- a/test/test_proxy.py
+++ b/test/test_proxy.py
@@ -70,13 +70,6 @@ class TestProcessProxyOptions:
def test_simple(self):
assert self.p()
- def test_certfile_keyfile(self):
- self.assert_noerr("--certfile", tutils.test_data.path("data/testkey.pem"))
- self.assert_err("does not exist", "--certfile", "nonexistent")
-
- self.assert_noerr("--keyfile", tutils.test_data.path("data/testkey.pem"))
- self.assert_err("does not exist", "--keyfile", "nonexistent")
-
def test_confdir(self):
with tutils.tmpdir() as confdir:
self.assert_noerr("--confdir", confdir)
@@ -93,11 +86,16 @@ class TestProcessProxyOptions:
self.assert_err("invalid reverse proxy", "-P", "reverse")
self.assert_noerr("-P", "http://localhost")
- def test_certs(self):
+ def test_client_certs(self):
with tutils.tmpdir() as confdir:
self.assert_noerr("--client-certs", confdir)
self.assert_err("directory does not exist", "--client-certs", "nonexistent")
+ def test_certs(self):
+ with tutils.tmpdir() as confdir:
+ self.assert_noerr("--cert", tutils.test_data.path("data/testkey.pem"))
+ self.assert_err("does not exist", "--cert", "nonexistent")
+
def test_auth(self):
p = self.assert_noerr("--nonanonymous")
assert p.authenticator
diff --git a/test/test_server.py b/test/test_server.py
index 2714ef52..ed21e75c 100644
--- a/test/test_server.py
+++ b/test/test_server.py
@@ -213,8 +213,9 @@ class TestHTTPSNoCommonName(tservers.HTTPProxTest):
"""
ssl = True
ssloptions=pathod.SSLOptions(
- certfile = tutils.test_data.path("data/no_common_name.pem"),
- keyfile = tutils.test_data.path("data/no_common_name.pem"),
+ certs = [
+ ("*", tutils.test_data.path("data/no_common_name.pem"))
+ ]
)
def test_http(self):
f = self.pathod("202")
diff --git a/test/tservers.py b/test/tservers.py
index 540cda60..3a6a610f 100644
--- a/test/tservers.py
+++ b/test/tservers.py
@@ -1,4 +1,5 @@
import threading, Queue
+import shutil, tempfile
import flask
import libpathod.test, libpathod.pathoc
from libmproxy import proxy, flow, controller
@@ -72,7 +73,6 @@ class ProxTestBase(object):
ssl = None
ssloptions = False
clientcerts = False
- certfile = None
no_upstream_cert = False
authenticator = None
masterclass = TestMaster
@@ -82,9 +82,10 @@ class ProxTestBase(object):
cls.server = libpathod.test.Daemon(ssl=cls.ssl, ssloptions=cls.ssloptions)
cls.server2 = libpathod.test.Daemon(ssl=cls.ssl, ssloptions=cls.ssloptions)
pconf = cls.get_proxy_config()
+ cls.confdir = tempfile.gettempdir()
config = proxy.ProxyConfig(
no_upstream_cert = cls.no_upstream_cert,
- cacert = tutils.test_data.path("data/confdir/mitmproxy-ca.pem"),
+ confdir = cls.confdir,
authenticator = cls.authenticator,
**pconf
)
@@ -93,6 +94,10 @@ class ProxTestBase(object):
cls.proxy = ProxyThread(tmaster)
cls.proxy.start()
+ @classmethod
+ def tearDownAll(cls):
+ shutil.rmtree(cls.confdir)
+
@property
def master(cls):
return cls.proxy.tmaster
@@ -127,9 +132,6 @@ class ProxTestBase(object):
d = dict()
if cls.clientcerts:
d["clientcerts"] = tutils.test_data.path("data/clientcert")
- if cls.certfile:
- d["certfile"] =tutils.test_data.path("data/testkey.pem")
- d["keyfile"] =tutils.test_data.path("data/testkey.pem")
return d
@@ -254,7 +256,6 @@ class ChainProxTest(ProxTestBase):
"""
n = 2
chain_config = [lambda: proxy.ProxyConfig(
- cacert = tutils.test_data.path("data/confdir/mitmproxy-ca.pem"),
)] * n
@classmethod
def setupAll(cls):