Source code

Revision control

Copy as Markdown

Other Tools

"""Functions to download, install, setup, and use the mitmproxy playback tool"""
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import os
import sys
import mozinfo
from mozproxy.backends.mitm.mitm import Mitmproxy
from mozproxy.utils import LOG
# to install mitmproxy certificate into Firefox and turn on/off proxy
POLICIES_CONTENT_ON = """{
"policies": {
"Certificates": {
"Install": ["%(cert)s"]
},
"Proxy": {
"Mode": "manual",
"HTTPProxy": "%(host)s:%(port)d",
"SSLProxy": "%(host)s:%(port)d",
"Passthrough": "%(host)s",
"Locked": true
}
}
}"""
POLICIES_CONTENT_OFF = """{
"policies": {
"Proxy": {
"Mode": "none",
"Locked": false
}
}
}"""
# path for mitmproxy certificate, generated auto after mitmdump is started
# on local machine it is 'HOME', however it is different on production machines
try:
DEFAULT_CERT_PATH = os.path.join(
os.getenv("HOME"), ".mitmproxy", "mitmproxy-ca-cert.cer"
)
except Exception:
DEFAULT_CERT_PATH = os.path.join(
os.getenv("HOMEDRIVE"),
os.getenv("HOMEPATH"),
".mitmproxy",
"mitmproxy-ca-cert.cer",
)
# On Windows, deal with mozilla-build having forward slashes in $HOME:
if os.name == "nt" and "/" in DEFAULT_CERT_PATH:
DEFAULT_CERT_PATH = DEFAULT_CERT_PATH.replace("/", "\\")
class MitmproxyDesktop(Mitmproxy):
def setup(self):
"""
Installs certificates.
For Firefox we need to install the generated mitmproxy CA cert. For
Chromium this is not necessary as it will be started with the
--ignore-certificate-errors cmd line arg.
"""
if not self.config["app"] == "firefox":
return
# install the generated CA certificate into Firefox desktop
self.install_mitmproxy_cert(self.browser_path)
def install_mitmproxy_cert(self, browser_path):
"""Install the CA certificate generated by mitmproxy, into Firefox
1. Create a dir called 'distribution' in the same directory as the Firefox executable
2. Create the policies.json file inside that folder; which points to the certificate
location, and turns on the the browser proxy settings
"""
LOG.info("Installing mitmproxy CA certificate into Firefox")
# browser_path is the exe, we want the folder
self.policies_dir = os.path.dirname(browser_path)
# on macosx we need to remove the last folders 'MacOS'
# and the policies json needs to go in ../Content/Resources/
if "mac" in self.config["platform"]:
self.policies_dir = os.path.join(self.policies_dir[:-6], "Resources")
# for all platforms the policies json goes in a 'distribution' dir
self.policies_dir = os.path.join(self.policies_dir, "distribution")
self.cert_path = DEFAULT_CERT_PATH
# for windows only
if mozinfo.os == "win":
self.cert_path = self.cert_path.replace("\\", "\\\\")
if not os.path.exists(self.policies_dir):
LOG.info("creating folder: %s" % self.policies_dir)
os.makedirs(self.policies_dir)
else:
LOG.info("folder already exists: %s" % self.policies_dir)
self.write_policies_json(
self.policies_dir,
policies_content=POLICIES_CONTENT_ON
% {"cert": self.cert_path, "host": self.host, "port": self.port},
)
# cannot continue if failed to add CA cert to Firefox, need to check
if not self.is_mitmproxy_cert_installed():
LOG.error(
"Aborting: failed to install mitmproxy CA cert into Firefox desktop"
)
self.stop_mitmproxy_playback()
sys.exit()
def write_policies_json(self, location, policies_content):
policies_file = os.path.join(location, "policies.json")
LOG.info("writing: %s" % policies_file)
with open(policies_file, "w") as fd:
fd.write(policies_content)
def read_policies_json(self, location):
policies_file = os.path.join(location, "policies.json")
LOG.info("reading: %s" % policies_file)
with open(policies_file, "r") as fd:
return fd.read()
def is_mitmproxy_cert_installed(self):
"""Verify mitmxproy CA cert was added to Firefox"""
try:
# read autoconfig file, confirm mitmproxy cert is in there
contents = self.read_policies_json(self.policies_dir)
LOG.info("Firefox policies file contents:")
LOG.info(contents)
if (
POLICIES_CONTENT_ON
% {"cert": self.cert_path, "host": self.host, "port": self.port}
) in contents:
LOG.info("Verified mitmproxy CA certificate is installed in Firefox")
else:
return False
except Exception as e:
LOG.info("failed to read Firefox policies file, exeption: %s" % e)
return False
return True
def stop(self):
LOG.info("MitmproxyDesktop stop!!")
super(MitmproxyDesktop, self).stop()
self.turn_off_browser_proxy()
def turn_off_browser_proxy(self):
"""Turn off the browser proxy that was used for mitmproxy playback. In Firefox
we need to change the autoconfig files to revert the proxy; for Chromium the proxy
was setup on the cmd line, so nothing is required here."""
if self.config["app"] == "firefox" and self.policies_dir is not None:
LOG.info("Turning off the browser proxy")
self.write_policies_json(
self.policies_dir, policies_content=POLICIES_CONTENT_OFF
)