mirror of
https://github.com/Grasscutters/mitmproxy.git
synced 2024-11-22 15:37:45 +00:00
Merge pull request #1014 from ikoz/master
New option: Add server certs to client chain
This commit is contained in:
commit
983b0dd4f6
@ -434,6 +434,12 @@ def proxy_ssl_options(parser):
|
||||
action="store_true", dest="no_upstream_cert",
|
||||
help="Don't connect to upstream server to look up certificate details."
|
||||
)
|
||||
group.add_argument(
|
||||
"--add-upstream-certs-to-client-chain", default=False,
|
||||
action="store_true", dest="add_upstream_certs_to_client_chain",
|
||||
help="Add all certificates of the upstream server to the certificate chain "
|
||||
"that will be served to the proxy client, as extras."
|
||||
)
|
||||
group.add_argument(
|
||||
"--verify-upstream-cert", default=False,
|
||||
action="store_true", dest="ssl_verify_upstream_cert",
|
||||
|
@ -432,6 +432,11 @@ class TlsLayer(Layer):
|
||||
self.log("Establish TLS with client", "debug")
|
||||
cert, key, chain_file = self._find_cert()
|
||||
|
||||
if self.config.add_upstream_certs_to_client_chain:
|
||||
extra_certs = self.server_conn.server_certs
|
||||
else:
|
||||
extra_certs = None
|
||||
|
||||
try:
|
||||
self.client_conn.convert_to_ssl(
|
||||
cert, key,
|
||||
@ -441,6 +446,7 @@ class TlsLayer(Layer):
|
||||
dhparams=self.config.certstore.dhparams,
|
||||
chain_file=chain_file,
|
||||
alpn_select_callback=self.__alpn_select_callback,
|
||||
extra_chain_certs = extra_certs,
|
||||
)
|
||||
# Some TLS clients will not fail the handshake,
|
||||
# but will immediately throw an "unexpected eof" error on the first read.
|
||||
|
@ -67,6 +67,7 @@ class ProxyConfig:
|
||||
ssl_verify_upstream_cert=False,
|
||||
ssl_verify_upstream_trusted_cadir=None,
|
||||
ssl_verify_upstream_trusted_ca=None,
|
||||
add_upstream_certs_to_client_chain=False,
|
||||
):
|
||||
self.host = host
|
||||
self.port = port
|
||||
@ -107,6 +108,7 @@ class ProxyConfig:
|
||||
self.openssl_verification_mode_server = SSL.VERIFY_NONE
|
||||
self.openssl_trusted_cadir_server = ssl_verify_upstream_trusted_cadir
|
||||
self.openssl_trusted_ca_server = ssl_verify_upstream_trusted_ca
|
||||
self.add_upstream_certs_to_client_chain = add_upstream_certs_to_client_chain
|
||||
|
||||
|
||||
def process_proxy_options(parser, options):
|
||||
@ -136,14 +138,26 @@ def process_proxy_options(parser, options):
|
||||
"Transparent, SOCKS5, reverse and upstream proxy mode "
|
||||
"are mutually exclusive. Read the docs on proxy modes to understand why."
|
||||
)
|
||||
|
||||
if options.add_upstream_certs_to_client_chain and options.no_upstream_cert:
|
||||
return parser.error(
|
||||
"The no-upstream-cert and add-upstream-certs-to-client-chain "
|
||||
"options are mutually exclusive. If no-upstream-cert is enabled "
|
||||
"then the upstream certificate is not retrieved before generating "
|
||||
"the client certificate chain."
|
||||
)
|
||||
if options.add_upstream_certs_to_client_chain and options.ssl_verify_upstream_cert:
|
||||
return parser.error(
|
||||
"The verify-upstream-cert and add-upstream-certs-to-client-chain "
|
||||
"options are mutually exclusive. If upstream certificates are verified "
|
||||
"then extra upstream certificates are not available for inclusion "
|
||||
"to the client chain."
|
||||
)
|
||||
if options.clientcerts:
|
||||
options.clientcerts = os.path.expanduser(options.clientcerts)
|
||||
if not os.path.exists(options.clientcerts):
|
||||
return parser.error(
|
||||
"Client certificate path does not exist: %s" % options.clientcerts
|
||||
"Client certificate path does not exist: %s" % options.clientcerts
|
||||
)
|
||||
|
||||
if options.auth_nonanonymous or options.auth_singleuser or options.auth_htpasswd:
|
||||
|
||||
if options.transparent_proxy:
|
||||
@ -206,5 +220,6 @@ def process_proxy_options(parser, options):
|
||||
ssl_version_server=options.ssl_version_server,
|
||||
ssl_verify_upstream_cert=options.ssl_verify_upstream_cert,
|
||||
ssl_verify_upstream_trusted_cadir=options.ssl_verify_upstream_trusted_cadir,
|
||||
ssl_verify_upstream_trusted_ca=options.ssl_verify_upstream_trusted_ca
|
||||
ssl_verify_upstream_trusted_ca=options.ssl_verify_upstream_trusted_ca,
|
||||
add_upstream_certs_to_client_chain=options.add_upstream_certs_to_client_chain,
|
||||
)
|
||||
|
@ -586,6 +586,7 @@ class TCPClient(_Connection):
|
||||
self.address = address
|
||||
self.source_address = source_address
|
||||
self.cert = None
|
||||
self.server_certs = []
|
||||
self.ssl_verification_error = None
|
||||
self.sni = None
|
||||
|
||||
@ -670,6 +671,10 @@ class TCPClient(_Connection):
|
||||
|
||||
self.cert = certutils.SSLCert(self.connection.get_peer_certificate())
|
||||
|
||||
# Keep all server certificates in a list
|
||||
for i in self.connection.get_peer_cert_chain():
|
||||
self.server_certs.append(certutils.SSLCert(i))
|
||||
|
||||
# Validate TLS Hostname
|
||||
try:
|
||||
crt = dict(
|
||||
@ -737,6 +742,7 @@ class BaseHandler(_Connection):
|
||||
request_client_cert=None,
|
||||
chain_file=None,
|
||||
dhparams=None,
|
||||
extra_chain_certs=None,
|
||||
**sslctx_kwargs):
|
||||
"""
|
||||
cert: A certutils.SSLCert object or the path to a certificate
|
||||
@ -772,6 +778,10 @@ class BaseHandler(_Connection):
|
||||
else:
|
||||
context.use_certificate_chain_file(cert)
|
||||
|
||||
if extra_chain_certs:
|
||||
for i in extra_chain_certs:
|
||||
context.add_extra_chain_cert(i.x509)
|
||||
|
||||
if handle_sni:
|
||||
# SNI callback happens during do_handshake()
|
||||
context.set_tlsext_servername_callback(handle_sni)
|
||||
|
@ -42,7 +42,8 @@ class SSLInfo(object):
|
||||
"Cipher: %s, %s bit, %s" % self.cipher,
|
||||
"SSL certificate chain:"
|
||||
]
|
||||
for i in self.certchain:
|
||||
for n,i in enumerate(self.certchain):
|
||||
parts.append(" Certificate [%s]" % n)
|
||||
parts.append("\tSubject: ")
|
||||
for cn in i.get_subject().get_components():
|
||||
parts.append("\t\t%s=%s" % cn)
|
||||
@ -69,7 +70,7 @@ class SSLInfo(object):
|
||||
s = certutils.SSLCert(i)
|
||||
if s.altnames:
|
||||
parts.append("\tSANs: %s" % " ".join(s.altnames))
|
||||
return "\n".join(parts)
|
||||
return "\n".join(parts)
|
||||
|
||||
|
||||
|
||||
|
@ -999,3 +999,43 @@ class TestProxyChainingSSLReconnect(tservers.HTTPUpstreamProxyTest):
|
||||
# (both terminated)
|
||||
# nothing happened here
|
||||
assert self.chain[1].tmaster.state.flow_count() == 2
|
||||
|
||||
|
||||
class AddUpstreamCertsToClientChainMixin:
|
||||
|
||||
ssl = True
|
||||
servercert = tutils.test_data.path("data/trusted-server.crt")
|
||||
ssloptions = pathod.SSLOptions(
|
||||
cn="trusted-cert",
|
||||
certs=[
|
||||
("trusted-cert", servercert)
|
||||
]
|
||||
)
|
||||
|
||||
def test_add_upstream_certs_to_client_chain(self):
|
||||
with open(self.servercert, "rb") as f:
|
||||
d = f.read()
|
||||
upstreamCert = SSLCert.from_pem(d)
|
||||
p = self.pathoc()
|
||||
upstream_cert_found_in_client_chain = False
|
||||
for receivedCert in p.server_certs:
|
||||
if receivedCert.digest('sha256') == upstreamCert.digest('sha256'):
|
||||
upstream_cert_found_in_client_chain = True
|
||||
break
|
||||
assert(upstream_cert_found_in_client_chain == self.add_upstream_certs_to_client_chain)
|
||||
|
||||
|
||||
class TestHTTPSAddUpstreamCertsToClientChainTrue(AddUpstreamCertsToClientChainMixin, tservers.HTTPProxyTest):
|
||||
|
||||
"""
|
||||
If --add-server-certs-to-client-chain is True, then the client should receive the upstream server's certificates
|
||||
"""
|
||||
add_upstream_certs_to_client_chain = True
|
||||
|
||||
|
||||
class TestHTTPSAddUpstreamCertsToClientChainFalse(AddUpstreamCertsToClientChainMixin, tservers.HTTPProxyTest):
|
||||
|
||||
"""
|
||||
If --add-server-certs-to-client-chain is False, then the client should not receive the upstream server's certificates
|
||||
"""
|
||||
add_upstream_certs_to_client_chain = False
|
||||
|
@ -86,6 +86,7 @@ class ProxyTestBase(object):
|
||||
no_upstream_cert = False
|
||||
authenticator = None
|
||||
masterclass = TestMaster
|
||||
add_upstream_certs_to_client_chain = False
|
||||
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
@ -129,6 +130,7 @@ class ProxyTestBase(object):
|
||||
no_upstream_cert = cls.no_upstream_cert,
|
||||
cadir = cls.cadir,
|
||||
authenticator = cls.authenticator,
|
||||
add_upstream_certs_to_client_chain = cls.add_upstream_certs_to_client_chain,
|
||||
)
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user