mirror of
https://github.com/vee1e/mitmproxy.git
synced 2026-09-03 11:17:24 +00:00
Revamp SSL configuration.
- Move option parsing utiliities to proxy.py - Don't have a global config object. Pass it as an argument to ProxyServer. - Simplify certificate generation logic.
This commit is contained in:
parent
4fc807cedd
commit
c2ae8285f4
8 changed files with 102 additions and 91 deletions
|
|
@ -6,11 +6,10 @@
|
|||
Development started from Neil Schemenauer's munchy.py
|
||||
"""
|
||||
import sys, os, time, string, socket, urlparse, re, select, copy, base64
|
||||
import SocketServer, ssl
|
||||
import optparse, SocketServer, ssl
|
||||
import utils, controller
|
||||
|
||||
NAME = "mitmproxy"
|
||||
config = None
|
||||
|
||||
|
||||
class ProxyError(Exception):
|
||||
|
|
@ -495,7 +494,8 @@ class ServerConnection:
|
|||
|
||||
|
||||
class ProxyHandler(SocketServer.StreamRequestHandler):
|
||||
def __init__(self, request, client_address, server, q):
|
||||
def __init__(self, config, request, client_address, server, q):
|
||||
self.config = config
|
||||
self.mqueue = q
|
||||
SocketServer.StreamRequestHandler.__init__(self, request, client_address, server)
|
||||
|
||||
|
|
@ -545,22 +545,14 @@ class ProxyHandler(SocketServer.StreamRequestHandler):
|
|||
if server:
|
||||
server.terminate()
|
||||
|
||||
def find_cert(self, host, port=443):
|
||||
#return config.certpath + "/" + host + ":" + port + ".pem"
|
||||
if config.certpath is not None:
|
||||
cert = config.certpath + "/" + host + ".pem"
|
||||
if not os.path.exists(cert) and config.cacert is not None:
|
||||
utils.dummy_cert(config.certpath, config.cacert, host)
|
||||
if os.path.exists(cert):
|
||||
return cert
|
||||
print >> sys.stderr, "WARNING: Certificate missing for %s:%d! (%s)\n" % (host, port, cert)
|
||||
return config.certfile
|
||||
|
||||
def find_key(self, host, port=443):
|
||||
if config.cacert is not None:
|
||||
return config.cacert
|
||||
def find_cert(self, host):
|
||||
if self.config.certfile:
|
||||
return self.config.certfile
|
||||
else:
|
||||
return config.certfile
|
||||
ret = utils.dummy_cert(self.config.certpath, self.config.cacert, host)
|
||||
if not ret:
|
||||
raise ProxyError(400, "mitmproxy: Unable to generate dummy cert.")
|
||||
return ret
|
||||
|
||||
def read_request(self, client_conn):
|
||||
line = self.rfile.readline()
|
||||
|
|
@ -583,14 +575,14 @@ class ProxyHandler(SocketServer.StreamRequestHandler):
|
|||
)
|
||||
self.wfile.flush()
|
||||
kwargs = dict(
|
||||
certfile = self.find_cert(host,port),
|
||||
keyfile = self.find_key(host,port),
|
||||
certfile = self.find_cert(host),
|
||||
keyfile = self.config.certfile or self.config.cacert,
|
||||
server_side = True,
|
||||
ssl_version = ssl.PROTOCOL_SSLv23,
|
||||
do_handshake_on_connect = False
|
||||
)
|
||||
if sys.version_info[1] > 6:
|
||||
kwargs["ciphers"] = config.ciphers
|
||||
kwargs["ciphers"] = self.config.ciphers
|
||||
self.connection = ssl.wrap_socket(self.connection, **kwargs)
|
||||
self.rfile = FileLike(self.connection)
|
||||
self.wfile = FileLike(self.connection)
|
||||
|
|
@ -671,8 +663,8 @@ ServerBase.daemon_threads = True # Terminate workers when main thread ter
|
|||
class ProxyServer(ServerBase):
|
||||
request_queue_size = 20
|
||||
allow_reuse_address = True
|
||||
def __init__(self, port, address=''):
|
||||
self.port, self.address = port, address
|
||||
def __init__(self, config, port, address=''):
|
||||
self.config, self.port, self.address = config, port, address
|
||||
ServerBase.__init__(self, (address, port), ProxyHandler)
|
||||
self.masterq = None
|
||||
|
||||
|
|
@ -680,8 +672,62 @@ class ProxyServer(ServerBase):
|
|||
self.masterq = q
|
||||
|
||||
def finish_request(self, request, client_address):
|
||||
self.RequestHandlerClass(request, client_address, self, self.masterq)
|
||||
self.RequestHandlerClass(self.config, request, client_address, self, self.masterq)
|
||||
|
||||
def shutdown(self):
|
||||
ServerBase.shutdown(self)
|
||||
|
||||
|
||||
# Command-line utils
|
||||
def certificate_option_group(parser):
|
||||
group = optparse.OptionGroup(parser, "SSL")
|
||||
group.add_option(
|
||||
"--cert", action="store",
|
||||
type = "str", dest="cert", default=None,
|
||||
help = "User-created SSL certificate file."
|
||||
)
|
||||
group.add_option(
|
||||
"--cacert", action="store",
|
||||
type = "str", dest="cacert", default="~/.mitmproxy/ca.pem",
|
||||
help = "SSL CA certificate file. Generated if it doesn't exist."
|
||||
)
|
||||
group.add_option(
|
||||
"--certpath", action="store",
|
||||
type = "str", dest="certpath", default="~/.mitmproxy/",
|
||||
help = "SSL certificate store path."
|
||||
)
|
||||
group.add_option(
|
||||
"--ciphers", action="store",
|
||||
type = "str", dest="ciphers", default=None,
|
||||
help = "SSL ciphers."
|
||||
)
|
||||
parser.add_option_group(group)
|
||||
|
||||
|
||||
def process_certificate_option_group(parser, options):
|
||||
conf = {}
|
||||
if options.cert:
|
||||
options.cert = os.path.expanduser(options.cert)
|
||||
if not os.path.exists(options.cert):
|
||||
parser.error("Manually created certificate does not exist: %s"%options.cert)
|
||||
if options.cacert:
|
||||
options.cacert = os.path.expanduser(options.cacert)
|
||||
if not os.path.exists(options.cacert):
|
||||
dummy_ca(options.cacert)
|
||||
if options.certpath:
|
||||
options.certpath = os.path.expanduser(options.certpath)
|
||||
elif options.cacert:
|
||||
options.certpath = os.path.dirname(options.cacert)
|
||||
|
||||
if getattr(options, "cache", None) is not None:
|
||||
options.cache = os.path.expanduser(options.cache)
|
||||
|
||||
return Config(
|
||||
certfile = options.cert,
|
||||
certpath = options.certpath,
|
||||
cacert = options.cacert,
|
||||
ciphers = options.ciphers
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import re, os, subprocess, datetime, textwrap, errno, sys
|
||||
import optparse
|
||||
|
||||
def format_timestamp(s):
|
||||
d = datetime.datetime.fromtimestamp(s)
|
||||
|
|
@ -355,11 +354,14 @@ def dummy_cert(certdir, ca, commonname):
|
|||
ca: Path to the certificate authority file, or None.
|
||||
commonname: Common name for the generated certificate.
|
||||
|
||||
Returns True if operation succeeded, False if not.
|
||||
Returns cert path if operation succeeded, None if not.
|
||||
"""
|
||||
certpath = os.path.join(certdir, commonname + ".pem")
|
||||
if os.path.exists(certpath):
|
||||
return certpath
|
||||
|
||||
confpath = os.path.join(certdir, commonname + ".cnf")
|
||||
reqpath = os.path.join(certdir, commonname + ".req")
|
||||
certpath = os.path.join(certdir, commonname + ".pem")
|
||||
|
||||
template = open(data.path("resources/cert.cnf")).read()
|
||||
f = open(confpath, "w").write(template%(dict(commonname=commonname)))
|
||||
|
|
@ -381,7 +383,7 @@ def dummy_cert(certdir, ca, commonname):
|
|||
stdin=subprocess.PIPE
|
||||
)
|
||||
if ret:
|
||||
return False
|
||||
return None
|
||||
cmd = [
|
||||
"openssl",
|
||||
"x509",
|
||||
|
|
@ -401,7 +403,7 @@ def dummy_cert(certdir, ca, commonname):
|
|||
stdin=subprocess.PIPE
|
||||
)
|
||||
if ret:
|
||||
return False
|
||||
return None
|
||||
else:
|
||||
# Create a new selfsigned certificate + key
|
||||
cmd = [
|
||||
|
|
@ -423,8 +425,8 @@ def dummy_cert(certdir, ca, commonname):
|
|||
stdin=subprocess.PIPE
|
||||
)
|
||||
if ret:
|
||||
return False
|
||||
return True
|
||||
return None
|
||||
return certpath
|
||||
|
||||
|
||||
def mkdir_p(path):
|
||||
|
|
@ -437,45 +439,3 @@ def mkdir_p(path):
|
|||
raise
|
||||
|
||||
|
||||
def certificate_option_group(parser):
|
||||
group = optparse.OptionGroup(parser, "SSL")
|
||||
group.add_option(
|
||||
"--cert", action="store",
|
||||
type = "str", dest="cert", default=None,
|
||||
help = "SSL certificate file."
|
||||
)
|
||||
group.add_option(
|
||||
"--cacert", action="store",
|
||||
type = "str", dest="cacert", default="~/.mitmproxy/ca.pem",
|
||||
help = "SSL CA certificate file."
|
||||
)
|
||||
group.add_option(
|
||||
"--certpath", action="store",
|
||||
type = "str", dest="certpath", default="~/.mitmproxy/",
|
||||
help = "SSL certificate store path."
|
||||
)
|
||||
group.add_option(
|
||||
"--ciphers", action="store",
|
||||
type = "str", dest="ciphers", default=None,
|
||||
help = "SSL ciphers."
|
||||
)
|
||||
parser.add_option_group(group)
|
||||
|
||||
|
||||
def process_certificate_option_group(parser, options):
|
||||
if options.cert is not None:
|
||||
options.cert = os.path.expanduser(options.cert)
|
||||
if not os.path.exists(options.cert):
|
||||
parser.error("Manually created certificate does not exist: %s"%options.cert)
|
||||
if options.cacert is not None:
|
||||
options.cacert = os.path.expanduser(options.cacert)
|
||||
if not os.path.exists(options.cacert):
|
||||
print >> sys.stderr, "Creating dummy CA certificate at %s"%options.cacert
|
||||
dummy_ca(options.cacert)
|
||||
if options.certpath is not None:
|
||||
options.certpath = os.path.expanduser(options.certpath)
|
||||
elif options.cacert is not None:
|
||||
options.certpath = os.path.dirname(options.cacert)
|
||||
if getattr(options, "cache", None) is not None:
|
||||
options.cache = os.path.expanduser(options.cache)
|
||||
|
||||
|
|
|
|||
8
mitmdump
8
mitmdump
|
|
@ -26,7 +26,7 @@ if __name__ == '__main__':
|
|||
usage = "%prog [options] [filter]",
|
||||
version="%%prog %s"%VERSION,
|
||||
)
|
||||
utils.certificate_option_group(parser)
|
||||
proxy.certificate_option_group(parser)
|
||||
parser.add_option(
|
||||
"-p", "--port", action="store",
|
||||
type = "int", dest="port", default=8080,
|
||||
|
|
@ -54,15 +54,15 @@ if __name__ == '__main__':
|
|||
if options.quiet:
|
||||
options.verbose = 0
|
||||
|
||||
utils.process_certificate_option_group(parser, options)
|
||||
proxy.process_certificate_option_group(parser, options)
|
||||
|
||||
proxy.config = proxy.Config(
|
||||
config = proxy.Config(
|
||||
certfile = options.cert,
|
||||
certpath = options.certpath,
|
||||
cacert = options.cacert,
|
||||
ciphers = options.ciphers
|
||||
)
|
||||
server = proxy.ProxyServer(options.port)
|
||||
server = proxy.ProxyServer(config, options.port)
|
||||
|
||||
dumpopts = dump.Options(
|
||||
verbosity = options.verbose,
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ if __name__ == '__main__':
|
|||
version="%%prog %s"%VERSION,
|
||||
)
|
||||
|
||||
utils.certificate_option_group(parser)
|
||||
proxy.certificate_option_group(parser)
|
||||
|
||||
parser.add_option(
|
||||
"-p", "--port", action="store",
|
||||
|
|
@ -56,17 +56,17 @@ if __name__ == '__main__':
|
|||
if options.quiet:
|
||||
options.verbose = 0
|
||||
|
||||
utils.process_certificate_option_group(parser, options)
|
||||
proxy.process_certificate_option_group(parser, options)
|
||||
|
||||
if options.cache is not None:
|
||||
options.cache = os.path.expanduser(options.cache)
|
||||
|
||||
proxy.config = proxy.Config(
|
||||
config = proxy.Config(
|
||||
certfile = options.cert,
|
||||
certpath = options.certpath,
|
||||
cacert = options.cacert,
|
||||
ciphers = options.ciphers
|
||||
)
|
||||
server = proxy.ProxyServer(options.port)
|
||||
server = proxy.ProxyServer(config, options.port)
|
||||
m = playback.PlaybackMaster(server, options)
|
||||
m.run()
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ if __name__ == '__main__':
|
|||
usage = "%prog [options] [flowdump path]",
|
||||
version="%%prog %s"%VERSION,
|
||||
)
|
||||
utils.certificate_option_group(parser)
|
||||
proxy.certificate_option_group(parser)
|
||||
parser.add_option(
|
||||
"-a", "--addr", action="store",
|
||||
type = "str", dest="addr", default='',
|
||||
|
|
@ -85,12 +85,12 @@ if __name__ == '__main__':
|
|||
|
||||
options, args = parser.parse_args()
|
||||
|
||||
utils.process_certificate_option_group(parser, options)
|
||||
proxy.process_certificate_option_group(parser, options)
|
||||
|
||||
if options.cache is not None:
|
||||
options.cache = os.path.expanduser(options.cache)
|
||||
|
||||
proxy.config = proxy.Config(
|
||||
config = proxy.Config(
|
||||
certfile = options.cert,
|
||||
certpath = options.certpath,
|
||||
cacert = options.cacert,
|
||||
|
|
@ -101,7 +101,7 @@ if __name__ == '__main__':
|
|||
if os.path.exists(options.cache + "/index.txt"):
|
||||
print >> sys.stderr, "ERROR: data already recorded in %s"%options.cache
|
||||
sys.exit(1)
|
||||
server = proxy.ProxyServer(options.port, options.addr)
|
||||
server = proxy.ProxyServer(config, options.port, options.addr)
|
||||
m = console.ConsoleMaster(server, options)
|
||||
|
||||
for i in args:
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ if __name__ == '__main__':
|
|||
version="%%prog %s"%VERSION,
|
||||
)
|
||||
|
||||
utils.certificate_option_group(parser)
|
||||
proxy.certificate_option_group(parser)
|
||||
|
||||
parser.add_option(
|
||||
"-p", "--port", action="store",
|
||||
|
|
@ -61,15 +61,15 @@ if __name__ == '__main__':
|
|||
if options.quiet:
|
||||
options.verbose = 0
|
||||
|
||||
utils.process_certificate_option_group(parser, options)
|
||||
proxy.process_certificate_option_group(parser, options)
|
||||
|
||||
proxy.config = proxy.Config(
|
||||
config = proxy.Config(
|
||||
certfile = options.cert,
|
||||
certpath = options.certpath,
|
||||
cacert = options.cacert,
|
||||
ciphers = options.ciphers
|
||||
)
|
||||
server = proxy.ProxyServer(options.port)
|
||||
server = proxy.ProxyServer(config, options.port)
|
||||
utils.mkdir_p(options.cache)
|
||||
if os.path.exists(options.cache + "/index.txt"):
|
||||
print >> sys.stderr, "ERROR: data already recorded in %s"%options.cache
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ HTTPS_PORT = random.randint(30000, 40000)
|
|||
|
||||
class TestMaster(controller.Master):
|
||||
def __init__(self, port, testq):
|
||||
serv = proxy.ProxyServer(port)
|
||||
serv = proxy.ProxyServer(proxy.Config("data/testkey.pem"), port)
|
||||
controller.Master.__init__(self, serv)
|
||||
self.testq = testq
|
||||
self.log = []
|
||||
|
|
@ -54,7 +54,6 @@ class ServerThread(threading.Thread):
|
|||
|
||||
class _TestServers(libpry.TestContainer):
|
||||
def setUpAll(self):
|
||||
proxy.config = proxy.Config("data/testkey.pem")
|
||||
self.tqueue = Queue.Queue()
|
||||
# We don't make any concurrent requests, so we can access
|
||||
# the attributes on this object safely.
|
||||
|
|
|
|||
|
|
@ -293,6 +293,12 @@ class udummy_cert(libpry.AutoTree):
|
|||
"foo.com"
|
||||
)
|
||||
assert os.path.exists(os.path.join(d, "foo", "foo.com.pem"))
|
||||
# Short-circuit
|
||||
assert utils.dummy_cert(
|
||||
os.path.join(d, "foo"),
|
||||
cacert,
|
||||
"foo.com"
|
||||
)
|
||||
|
||||
def test_no_ca(self):
|
||||
d = self.tmpdir()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue