Merge pull request #870 from bazzinotti/master

[docs/libmproxy/test] Support single client-side cert file
This commit is contained in:
Maximilian Hils 2015-12-29 17:51:11 +01:00
commit 7b093b46b6
7 changed files with 48 additions and 21 deletions

View File

@ -175,10 +175,21 @@ no such file exists, it will be generated automatically.
Using a client side certificate Using a client side certificate
------------------------------- -------------------------------
You can use a client certificate by passing the ``--client-certs DIRECTORY`` option to mitmproxy. You can use a client certificate by passing the ``--client-certs DIRECTORY|FILE``
option to mitmproxy. Using a directory allows certs to be selected based on
hostname, while using a filename allows a single specific certificate to be used for
all SSL connections. Certificate files must be in the PEM format and should
contain both the unencrypted private key and the certificate.
Multiple certs by Hostname
^^^^^^^^^^^^^^^^^^^^^^^^^^
If you've specified a directory to ``--client-certs``, then the following
behavior will be taken:
If you visit example.org, mitmproxy looks for a file named ``example.org.pem`` in the specified If you visit example.org, mitmproxy looks for a file named ``example.org.pem`` in the specified
directory and uses this as the client cert. The certificate file needs to be in the PEM format and directory and uses this as the client cert.
should contain both the unencrypted private key and the certificate.
.. _Certificate Pinning: http://security.stackexchange.com/questions/29988/what-is-certificate-pinning/ .. _Certificate Pinning: http://security.stackexchange.com/questions/29988/what-is-certificate-pinning/

View File

@ -407,7 +407,7 @@ def proxy_ssl_options(parser):
group.add_argument( group.add_argument(
"--client-certs", action="store", "--client-certs", action="store",
type=str, dest="clientcerts", default=None, type=str, dest="clientcerts", default=None,
help="Client certificate directory." help="Client certificate file or directory."
) )
group.add_argument( group.add_argument(
"--no-upstream-cert", default=False, "--no-upstream-cert", default=False,

View File

@ -174,6 +174,9 @@ class ServerConnection(tcp.TCPClient, stateobject.StateObject):
def establish_ssl(self, clientcerts, sni, **kwargs): def establish_ssl(self, clientcerts, sni, **kwargs):
clientcert = None clientcert = None
if clientcerts: if clientcerts:
if os.path.isfile(clientcerts):
clientcert = clientcerts
else:
path = os.path.join( path = os.path.join(
clientcerts, clientcerts,
self.address.host.encode("idna")) + ".pem" self.address.host.encode("idna")) + ".pem"

View File

@ -133,10 +133,9 @@ def process_proxy_options(parser, options):
if options.clientcerts: if options.clientcerts:
options.clientcerts = os.path.expanduser(options.clientcerts) options.clientcerts = os.path.expanduser(options.clientcerts)
if not os.path.exists(options.clientcerts) or not os.path.isdir(options.clientcerts): if not os.path.exists(options.clientcerts):
return parser.error( return parser.error(
"Client certificate directory does not exist or is not a directory: %s" % "Client certificate path does not exist: %s" % options.clientcerts
options.clientcerts
) )
if options.auth_nonanonymous or options.auth_singleuser or options.auth_htpasswd: if options.auth_nonanonymous or options.auth_singleuser or options.auth_htpasswd:

View File

@ -1,3 +1,4 @@
import os
import mock import mock
from OpenSSL import SSL from OpenSSL import SSL
@ -99,8 +100,11 @@ class TestProcessProxyOptions:
def test_client_certs(self): def test_client_certs(self):
with tutils.tmpdir() as cadir: with tutils.tmpdir() as cadir:
self.assert_noerr("--client-certs", cadir) self.assert_noerr("--client-certs", cadir)
self.assert_noerr(
"--client-certs",
os.path.join(tutils.test_data.path("data/clientcert"), "client.pem"))
self.assert_err( self.assert_err(
"directory does not exist", "path does not exist",
"--client-certs", "--client-certs",
"nonexistent") "nonexistent")

View File

@ -1,3 +1,4 @@
import os
import socket import socket
import time import time
from OpenSSL import SSL from OpenSSL import SSL
@ -313,13 +314,24 @@ class TestHTTPAuth(tservers.HTTPProxTest):
class TestHTTPS(tservers.HTTPProxTest, CommonMixin, TcpMixin): class TestHTTPS(tservers.HTTPProxTest, CommonMixin, TcpMixin):
ssl = True ssl = True
ssloptions = pathod.SSLOptions(request_client_cert=True) ssloptions = pathod.SSLOptions(request_client_cert=True)
clientcerts = True
def test_clientcert(self): def test_clientcert_file(self):
try:
self.config.clientcerts = os.path.join(
tutils.test_data.path("data/clientcert"), "client.pem")
f = self.pathod("304") f = self.pathod("304")
assert f.status_code == 304 assert f.status_code == 304
assert self.server.last_log()["request"]["clientcert"]["keyinfo"] assert self.server.last_log()["request"]["clientcert"]["keyinfo"]
finally:
self.config.clientcerts = None
def test_clientcert_dir(self):
try:
self.config.clientcerts = tutils.test_data.path("data/clientcert")
f = self.pathod("304")
assert f.status_code == 304
assert self.server.last_log()["request"]["clientcert"]["keyinfo"]
finally:
self.config.clientcerts = None
def test_error_post_connect(self): def test_error_post_connect(self):
p = self.pathoc() p = self.pathoc()
assert p.request("get:/:i0,'invalid\r\n\r\n'").status_code == 400 assert p.request("get:/:i0,'invalid\r\n\r\n'").status_code == 400

View File

@ -83,7 +83,6 @@ class ProxTestBase(object):
# Test Configuration # Test Configuration
ssl = None ssl = None
ssloptions = False ssloptions = False
clientcerts = False
no_upstream_cert = False no_upstream_cert = False
authenticator = None authenticator = None
masterclass = TestMaster masterclass = TestMaster
@ -130,7 +129,6 @@ class ProxTestBase(object):
no_upstream_cert = cls.no_upstream_cert, no_upstream_cert = cls.no_upstream_cert,
cadir = cls.cadir, cadir = cls.cadir,
authenticator = cls.authenticator, authenticator = cls.authenticator,
clientcerts = tutils.test_data.path("data/clientcert") if cls.clientcerts else None
) )