From 50046d3f68d75896a618ba4bb18f7267df00aae9 Mon Sep 17 00:00:00 2001 From: Abhinav Singh <126065+abhinavsingh@users.noreply.github.com> Date: Sat, 10 Aug 2024 12:12:55 +0530 Subject: [PATCH] TLS intercept self-signed servers using `--insecure-tls-interception` (#1446) * Disable mandatory TLS verification with --insecure * Fix lint issues * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix type issues with `cert_der_to_dict` * Flake8 exception * Fix `cert_der_to_dict` where file may not be writter before it is gets used by `_test_decode_cert` * Silence lint issue due to pylint bug * Rename flag to `--insecure-tls-interception` * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Reuse `DEFAULT_SSL_CONTEXT_OPTIONS` * # noqa: WPS436 --------- Co-authored-by: d4x Co-authored-by: d4xfe <168460626+d4xfe@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- README.md | 75 +++++++++++-------- proxy/common/constants.py | 1 + proxy/common/utils.py | 21 ++++++ proxy/core/connection/server.py | 11 +-- proxy/http/proxy/server.py | 23 +++++- tests/certificates/test_cert_data.py | 45 +++++++++++ .../proxy/test_http_proxy_tls_interception.py | 6 +- ...ttp_proxy_plugins_with_tls_interception.py | 7 +- 8 files changed, 142 insertions(+), 47 deletions(-) create mode 100644 tests/certificates/test_cert_data.py diff --git a/README.md b/README.md index d38bc4ed..bf3964d9 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,7 @@ - [Plugin Ordering](#plugin-ordering) - [End-to-End Encryption](#end-to-end-encryption) - [TLS Interception](#tls-interception) + - [Insecure TLS Interception](#insecure-tls-interception) - [TLS Interception With Docker](#tls-interception-with-docker) - [GROUT (NGROK Alternative)](#grout-ngrok-alternative) - [Grout Usage](#grout-usage) @@ -1241,6 +1242,13 @@ cached file instead of plain text. Now use CA flags with other [plugin examples](#plugin-examples) to see them work with `https` traffic. +## Insecure TLS Interception + +To intercept TLS traffic from a server using a self-signed certificate +add the `--insecure-tls-interception` flag to disable mandatory TLS certificate validation. + +NOTE: This flag disables certificate check for all servers. + ## TLS Interception With Docker Important notes about TLS Interception with Docker container: @@ -2510,17 +2518,16 @@ To run standalone benchmark for `proxy.py`, use the following command from repo ```console ❯ proxy -h -usage: -m [-h] [--enable-proxy-protocol] [--threadless] [--threaded] - [--num-workers NUM_WORKERS] [--enable-events] [--enable-conn-pool] - [--key-file KEY_FILE] [--cert-file CERT_FILE] - [--client-recvbuf-size CLIENT_RECVBUF_SIZE] - [--server-recvbuf-size SERVER_RECVBUF_SIZE] - [--max-sendbuf-size MAX_SENDBUF_SIZE] [--timeout TIMEOUT] - [--tunnel-hostname TUNNEL_HOSTNAME] [--tunnel-port TUNNEL_PORT] +usage: -m [-h] [--tunnel-hostname TUNNEL_HOSTNAME] [--tunnel-port TUNNEL_PORT] [--tunnel-username TUNNEL_USERNAME] [--tunnel-ssh-key TUNNEL_SSH_KEY] [--tunnel-ssh-key-passphrase TUNNEL_SSH_KEY_PASSPHRASE] - [--tunnel-remote-port TUNNEL_REMOTE_PORT] + [--tunnel-remote-port TUNNEL_REMOTE_PORT] [--threadless] + [--threaded] [--num-workers NUM_WORKERS] [--enable-events] + [--enable-proxy-protocol] [--enable-conn-pool] [--key-file KEY_FILE] + [--cert-file CERT_FILE] [--client-recvbuf-size CLIENT_RECVBUF_SIZE] + [--server-recvbuf-size SERVER_RECVBUF_SIZE] + [--max-sendbuf-size MAX_SENDBUF_SIZE] [--timeout TIMEOUT] [--local-executor LOCAL_EXECUTOR] [--backlog BACKLOG] [--hostname HOSTNAME] [--hostnames HOSTNAMES [HOSTNAMES ...]] [--port PORT] [--ports PORTS [PORTS ...]] [--port-file PORT_FILE] @@ -2533,9 +2540,9 @@ usage: -m [-h] [--enable-proxy-protocol] [--threadless] [--threaded] [--work-klass WORK_KLASS] [--pid-file PID_FILE] [--openssl OPENSSL] [--data-dir DATA_DIR] [--ssh-listener-klass SSH_LISTENER_KLASS] [--disable-http-proxy] [--disable-headers DISABLE_HEADERS] - [--ca-key-file CA_KEY_FILE] [--ca-cert-dir CA_CERT_DIR] - [--ca-cert-file CA_CERT_FILE] [--ca-file CA_FILE] - [--ca-signing-key-file CA_SIGNING_KEY_FILE] + [--ca-key-file CA_KEY_FILE] [--insecure-tls-interception] + [--ca-cert-dir CA_CERT_DIR] [--ca-cert-file CA_CERT_FILE] + [--ca-file CA_FILE] [--ca-signing-key-file CA_SIGNING_KEY_FILE] [--auth-plugin AUTH_PLUGIN] [--cache-requests] [--cache-by-content-type] [--cache-dir CACHE_DIR] [--proxy-pool PROXY_POOL] [--enable-web-server] @@ -2549,13 +2556,25 @@ usage: -m [-h] [--enable-proxy-protocol] [--threadless] [--threaded] [--filtered-client-ips FILTERED_CLIENT_IPS] [--filtered-url-regex-config FILTERED_URL_REGEX_CONFIG] -proxy.py v2.4.4rc6.dev191+gef5a8922 +proxy.py v2.4.5 options: -h, --help show this help message and exit - --enable-proxy-protocol - Default: False. If used, will enable proxy protocol. - Only version 1 is currently supported. + --tunnel-hostname TUNNEL_HOSTNAME + Default: None. Remote hostname or IP address to which + SSH tunnel will be established. + --tunnel-port TUNNEL_PORT + Default: 22. SSH port of the remote host. + --tunnel-username TUNNEL_USERNAME + Default: None. Username to use for establishing SSH + tunnel. + --tunnel-ssh-key TUNNEL_SSH_KEY + Default: None. Private key path in pem format + --tunnel-ssh-key-passphrase TUNNEL_SSH_KEY_PASSPHRASE + Default: None. Private key passphrase + --tunnel-remote-port TUNNEL_REMOTE_PORT + Default: 8899. Remote port which will be forwarded + locally for proxy. --threadless Default: True. Enabled by default on Python 3.8+ (mac, linux). When disabled a new thread is spawned to handle each client connection. @@ -2567,6 +2586,9 @@ options: --enable-events Default: False. Enables core to dispatch lifecycle events. Plugins can be used to subscribe for core events. + --enable-proxy-protocol + Default: False. If used, will enable proxy protocol. + Only version 1 is currently supported. --enable-conn-pool Default: False. (WIP) Enable upstream connection pooling. --key-file KEY_FILE Default: None. Server key file to enable end-to-end @@ -2588,21 +2610,6 @@ options: --timeout TIMEOUT Default: 10.0. Number of seconds after which an inactive connection must be dropped. Inactivity is defined by no data sent or received by the client. - --tunnel-hostname TUNNEL_HOSTNAME - Default: None. Remote hostname or IP address to which - SSH tunnel will be established. - --tunnel-port TUNNEL_PORT - Default: 22. SSH port of the remote host. - --tunnel-username TUNNEL_USERNAME - Default: None. Username to use for establishing SSH - tunnel. - --tunnel-ssh-key TUNNEL_SSH_KEY - Default: None. Private key path in pem format - --tunnel-ssh-key-passphrase TUNNEL_SSH_KEY_PASSPHRASE - Default: None. Private key passphrase - --tunnel-remote-port TUNNEL_REMOTE_PORT - Default: 8899. Remote port which will be forwarded - locally for proxy. --local-executor LOCAL_EXECUTOR Default: 1. Enabled by default. Use 0 to disable. When enabled acceptors will make use of local (same @@ -2668,6 +2675,8 @@ options: Default: None. CA key to use for signing dynamically generated HTTPS certificates. If used, must also pass --ca-cert-file and --ca-signing-key-file + --insecure-tls-interception + Default: False. Disables certificate verification --ca-cert-dir CA_CERT_DIR Default: ~/.proxy/certificates. Directory to store dynamically generated certificates. Also see --ca-key- @@ -2676,9 +2685,9 @@ options: Default: None. Signing certificate to use for signing dynamically generated HTTPS certificates. If used, must also pass --ca-key-file and --ca-signing-key-file - --ca-file CA_FILE Default: /Users/abhinavsingh/Dev/proxy.py/.venv31013/l - ib/python3.10/site-packages/certifi/cacert.pem. - Provide path to custom CA bundle for peer certificate + --ca-file CA_FILE Default: /Users/abhinavsingh/Dev/proxy.py/.venv3118/li + b/python3.11/site-packages/certifi/cacert.pem. Provide + path to custom CA bundle for peer certificate verification --ca-signing-key-file CA_SIGNING_KEY_FILE Default: None. CA signing key to use for dynamic diff --git a/proxy/common/constants.py b/proxy/common/constants.py index 46558227..ca47abdb 100644 --- a/proxy/common/constants.py +++ b/proxy/common/constants.py @@ -91,6 +91,7 @@ DEFAULT_BASIC_AUTH = None DEFAULT_MAX_SEND_SIZE = 64 * 1024 DEFAULT_BUFFER_SIZE = 128 * 1024 DEFAULT_CA_CERT_DIR = None +DEFAULT_INSECURE_TLS_INTERCEPTION = False DEFAULT_CA_CERT_FILE = None DEFAULT_CA_KEY_FILE = None DEFAULT_CA_SIGNING_KEY_FILE = None diff --git a/proxy/common/utils.py b/proxy/common/utils.py index 9ac8883d..84f3484a 100644 --- a/proxy/common/utils.py +++ b/proxy/common/utils.py @@ -12,17 +12,21 @@ utils """ +import os import ssl import sys import socket import logging import argparse +import tempfile import functools import ipaddress import contextlib from types import TracebackType from typing import Any, Dict, List, Type, Tuple, Callable, Optional +import _ssl # noqa: WPS436 + from .types import HostPort from .constants import ( CRLF, COLON, HTTP_1_1, IS_WINDOWS, WHITESPACE, DEFAULT_TIMEOUT, @@ -36,6 +40,23 @@ if not IS_WINDOWS: # pragma: no cover logger = logging.getLogger(__name__) +def cert_der_to_dict(der: Optional[bytes]) -> Dict[str, Any]: + """Parse a DER formatted certificate to a python dict""" + if not der: + return {} + with tempfile.NamedTemporaryFile(delete=False) as cert_file: + pem = ssl.DER_cert_to_PEM_cert(der) + cert_file.write(pem.encode()) + cert_file.flush() + cert_file.seek(0) + try: + certificate = _ssl._test_decode_cert(cert_file.name) + finally: + cert_file.close() + os.remove(cert_file.name) + return certificate or {} + + def tls_interception_enabled(flags: argparse.Namespace) -> bool: return flags.ca_key_file is not None and \ flags.ca_cert_dir is not None and \ diff --git a/proxy/core/connection/server.py b/proxy/core/connection/server.py index 31233049..619ada4f 100644 --- a/proxy/core/connection/server.py +++ b/proxy/core/connection/server.py @@ -15,6 +15,7 @@ from .types import tcpConnectionTypes from .connection import TcpConnection, TcpConnectionUninitializedException from ...common.types import HostPort, TcpOrTlsSocket from ...common.utils import new_socket_connection +from ...common.constants import DEFAULT_SSL_CONTEXT_OPTIONS class TcpServerConnection(TcpConnection): @@ -51,12 +52,12 @@ class TcpServerConnection(TcpConnection): # Ref https://github.com/PyCQA/pylint/issues/3691 verify_mode: ssl.VerifyMode = ssl.VerifyMode.CERT_REQUIRED, # pylint: disable=E1101 ) -> None: - ctx = ssl.create_default_context( - ssl.Purpose.SERVER_AUTH, - cafile=ca_file, + ctx = ssl.create_default_context(ssl.Purpose.SERVER_AUTH, cafile=ca_file) + ctx.options |= DEFAULT_SSL_CONTEXT_OPTIONS + # pylint: disable=E1101 + ctx.check_hostname = ( + False if verify_mode == ssl.VerifyMode.CERT_NONE else hostname is not None ) - ctx.options |= ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3 | ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1 - ctx.check_hostname = hostname is not None ctx.verify_mode = verify_mode self.connection.setblocking(True) self._conn = ctx.wrap_socket( diff --git a/proxy/http/proxy/server.py b/proxy/http/proxy/server.py index 4e2f44ac..80af1686 100644 --- a/proxy/http/proxy/server.py +++ b/proxy/http/proxy/server.py @@ -21,7 +21,7 @@ import socket import logging import threading import subprocess -from typing import Any, Dict, List, Union, Optional, cast +from typing import Any, Dict, List, Union, Optional from .plugin import HttpProxyBasePlugin from ..parser import HttpParser, httpParserTypes, httpParserStates @@ -35,7 +35,7 @@ from ...common.pki import gen_csr, sign_csr, gen_public_key from ...core.event import eventNames from ...common.flag import flags from ...common.types import Readables, Writables, Descriptors -from ...common.utils import text_ +from ...common.utils import text_, cert_der_to_dict from ...core.connection import ( TcpServerConnection, TcpConnectionUninitializedException, ) @@ -43,7 +43,8 @@ from ...common.constants import ( COMMA, DEFAULT_CA_FILE, PLUGIN_PROXY_AUTH, DEFAULT_CA_CERT_DIR, DEFAULT_CA_KEY_FILE, DEFAULT_CA_CERT_FILE, DEFAULT_DISABLE_HEADERS, PROXY_AGENT_HEADER_VALUE, DEFAULT_DISABLE_HTTP_PROXY, - DEFAULT_CA_SIGNING_KEY_FILE, DEFAULT_HTTP_PROXY_ACCESS_LOG_FORMAT, + DEFAULT_CA_SIGNING_KEY_FILE, DEFAULT_INSECURE_TLS_INTERCEPTION, + DEFAULT_HTTP_PROXY_ACCESS_LOG_FORMAT, DEFAULT_HTTPS_PROXY_ACCESS_LOG_FORMAT, ) @@ -74,6 +75,13 @@ flags.add_argument( 'HTTPS certificates. If used, must also pass --ca-cert-file and --ca-signing-key-file', ) +flags.add_argument( + '--insecure-tls-interception', + action='store_true', + default=DEFAULT_INSECURE_TLS_INTERCEPTION, + help='Default: False. Disables certificate verification', +) + flags.add_argument( '--ca-cert-dir', type=str, @@ -760,10 +768,17 @@ class HttpProxyPlugin(HttpProtocolHandlerPlugin): assert isinstance(self.upstream.connection, socket.socket) do_close = False try: + # pylint: disable=E1101 + verify_mode = ( + ssl.VerifyMode.CERT_NONE + if self.flags.insecure_tls_interception + else ssl.VerifyMode.CERT_REQUIRED + ) self.upstream.wrap( text_(self.request.host), self.flags.ca_file, as_non_blocking=True, + verify_mode=verify_mode, ) except ssl.SSLCertVerificationError: # Server raised certificate verification error # When --disable-interception-on-ssl-cert-verification-error flag is on, @@ -802,7 +817,7 @@ class HttpProxyPlugin(HttpProtocolHandlerPlugin): try: # TODO: Perform async certificate generation generated_cert = self.generate_upstream_certificate( - cast(Dict[str, Any], self.upstream.connection.getpeercert()), + cert_der_to_dict(self.upstream.connection.getpeercert(True)), ) self.client.wrap(self.flags.ca_signing_key_file, generated_cert) except subprocess.TimeoutExpired as e: # Popen communicate timeout diff --git a/tests/certificates/test_cert_data.py b/tests/certificates/test_cert_data.py new file mode 100644 index 00000000..612429bf --- /dev/null +++ b/tests/certificates/test_cert_data.py @@ -0,0 +1,45 @@ +# -*- coding: utf-8 -*- +""" + proxy.py + ~~~~~~~~ + ⚡⚡⚡ Fast, Lightweight, Pluggable, TLS interception capable proxy server focused on + Network monitoring, controls & Application development, testing, debugging. + + :copyright: (c) 2013-present by Abhinav Singh and contributors. + :license: BSD, see LICENSE for more details. +""" +from typing import Any + + +# pylint: disable=line-too-long +test_cert_bytes = b"0\x82\x03\xa30\x82\x02\x8b\xa0\x03\x02\x01\x02\x02\x14PE\x01\x8c\xa6\xea\xd8#\xcf\x90\xb0D\xc7\x04\xde\x9b9Y\xf3 0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x0b\x05\x000a1\x0b0\t\x06\x03U\x04\x06\x13\x02as1\x0b0\t\x06\x03U\x04\x08\x0c\x02as1\x0b0\t\x06\x03U\x04\x07\x0c\x02as1\x0b0\t\x06\x03U\x04\n\x0c\x02as1\x0b0\t\x06\x03U\x04\x0b\x0c\x02as1\x0b0\t\x06\x03U\x04\x03\x0c\x02as1\x110\x0f\x06\t*\x86H\x86\xf7\r\x01\t\x01\x16\x02as0\x1e\x17\r240429125057Z\x17\r250429125057Z0a1\x0b0\t\x06\x03U\x04\x06\x13\x02as1\x0b0\t\x06\x03U\x04\x08\x0c\x02as1\x0b0\t\x06\x03U\x04\x07\x0c\x02as1\x0b0\t\x06\x03U\x04\n\x0c\x02as1\x0b0\t\x06\x03U\x04\x0b\x0c\x02as1\x0b0\t\x06\x03U\x04\x03\x0c\x02as1\x110\x0f\x06\t*\x86H\x86\xf7\r\x01\t\x01\x16\x02as0\x82\x01\"0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x01\x05\x00\x03\x82\x01\x0f\x000\x82\x01\n\x02\x82\x01\x01\x00\xee\xcbU\xe3\xc4]\x83\xb9\x9d\xb1(v0\x18\x18\xc3\x00\x96\xc0\x0f\xc29\x84\xe7/W\xc7\x0b\xec\xdf\x9d-\xec\xd9\x876\xe5m\xda\x96\xea\xb0\xc6\x00\x7f\xb6\x93;\xd6\x1bK`\xd4Hc<\xa0g\xe5Q[\xe3\xe1\xd1DD5\x9b\x12\xdf\xd0\xd0\xc6X\xc9\x98\xc9\xb1\x81\xf5\xa2\x12\xaa\xc1\xb0\x80\xe8)R\xa7\xed\xe3P6\x82\x05\xbcA4\x91\xbcs?\xc2\xf2\xfd-\xe65'};\xa7E\xb2yN\x0fiO7\x82-`CX\xdb\xe0\x9c\xd7\x8e\x00N\nAu\xac/\xb3o\xcaG;\xa4\x8d\xca\x92\xe3F\x96\xe5\xbd\x1dq\xf6\xa5\x9f\xc5@I=\xfc\x1cl\x81\xb3y\x93FaPa^\x08\x0f\x80t\xb8J\xfd\xb8]\xd52\xf5\x9bE\xe8J:\x08\x8c\x98m0\xba\x85\x1b\xb6\x97\xe5\xba4\xe3nU\xa5\xc7\xeb\xde_z\x1a(j\xa7\xeb\x8a\xb4\xe1'?\x91\x80MhG=y\xc7\xf1|\xcaJ@\xae\xc4'\xd6\xd6}L\xf4\x91NV`\x98\x80\xef%\xa2hq\x05s\x02\x03\x01\x00\x01\xa3S0Q0\x1d\x06\x03U\x1d\x0e\x04\x16\x04\x14\xc6\xa4,\xe5\xe3\x15j\x18\x15@Xw!\xdd\xbf\xc6\xe5\xf0vG0\x1f\x06\x03U\x1d#\x04\x180\x16\x80\x14\xc6\xa4,\xe5\xe3\x15j\x18\x15@Xw!\xdd\xbf\xc6\xe5\xf0vG0\x0f\x06\x03U\x1d\x13\x01\x01\xff\x04\x050\x03\x01\x01\xff0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x0b\x05\x00\x03\x82\x01\x01\x00\xacx\xeb\x02\x8a\xd3\x966\xb73\xfb\n\x1eb\ng\xda\x84\x18\x97P\xb4\x7f\x8a\xbd\x82\xf3\x1b\xe8k%\xcc\x0f\xbd\x7fB\xb9\x1df|-k\x01\xf3\x89\x08r\xb9\x93\xf5?Z\x16\xff\x0f\x97\x91b#\xef$I\x11\x9e\x16\xb2J\x97\xd1\x0e\xd6\xabD\xca@\xe7\xb3\xbe\x84S\x1e\xdb;\x9b\xc4\xf4\x18\xf4\x9a\x1b\xcej\xe0qmx\xe4N?K\n.p\xa8\xa6\xfa\xb0\xf7y\xe8\x0f\xbd\x0c216\xb0\xa1d\x1f\x7f3\xa1l?\xbe\x9a\x06\xed]\x1a\x00\xab\xb4e\x13:\x17\x1b\x88\x8e\xcaqp\"\x8f\xa6\xf7\x06J?`\xe0\xf7\xce\xf8K\x08\x15\x18\xa1\xc4\xb5\xd9hB\xb0\xc6\\\xae?\xa9\x83FL\x8cm\xd1\xad^\xf0\xa5:\x8e\x97\x07\xd2\xd0l\x0e\x9d\x01\xa00c)\xae\xd0@\xefr\xe7,\xb7[\xd3H\xfe1\xfb\xa9|\xd0\xac\xc6i\x98\xe5\xd5\xd1\xf2\x97<\xf9\xe1?=\x93\xfaM\x86\xa2\x9dy\xdeZj\x93&\xa6\x84d\x07a\xbf\xd6\xdde\xaa)\t\xd6\x0e\x99\x85K" # noqa: WPS342, E501 + + +def mock_cert(_: Any) -> Any: + return test_cert_bytes + + +cert_dict = { + 'subject': ( + (('countryName', 'as'),), + (('stateOrProvinceName', 'as'),), + (('localityName', 'as'),), + (('organizationName', 'as'),), + (('organizationalUnitName', 'as'),), + (('commonName', 'as'),), + (('emailAddress', 'as'),), + ), + 'issuer': ( + (('countryName', 'as'),), + (('stateOrProvinceName', 'as'),), + (('localityName', 'as'),), + (('organizationName', 'as'),), + (('organizationalUnitName', 'as'),), + (('commonName', 'as'),), + (('emailAddress', 'as'),), + ), + 'version': 3, + 'serialNumber': '5045018CA6EAD823CF90B044C704DE9B3959F320', + 'notBefore': 'Apr 29 12:50:57 2024 GMT', + 'notAfter': 'Apr 29 12:50:57 2025 GMT', +} diff --git a/tests/http/proxy/test_http_proxy_tls_interception.py b/tests/http/proxy/test_http_proxy_tls_interception.py index 2fbdaef9..fad6eff1 100644 --- a/tests/http/proxy/test_http_proxy_tls_interception.py +++ b/tests/http/proxy/test_http_proxy_tls_interception.py @@ -30,6 +30,7 @@ from proxy.http.responses import PROXY_TUNNEL_ESTABLISHED_RESPONSE_PKT from proxy.core.connection import TcpServerConnection from proxy.common.constants import DEFAULT_CA_FILE from ...test_assertions import Assertions +from ...certificates.test_cert_data import mock_cert class TestHttpProxyTlsInterception(Assertions): @@ -59,6 +60,7 @@ class TestHttpProxyTlsInterception(Assertions): # Used for server side wrapping self.mock_ssl_context = mocker.patch('ssl.create_default_context') upstream_tls_sock = mock.MagicMock(spec=ssl.SSLSocket) + upstream_tls_sock.getpeercert = mock_cert self.mock_ssl_context.return_value.wrap_socket.return_value = upstream_tls_sock # Used for client wrapping @@ -75,8 +77,8 @@ class TestHttpProxyTlsInterception(Assertions): # Do not mock the original wrap method self.mock_server_conn.return_value.wrap.side_effect = \ - lambda x, y, as_non_blocking: TcpServerConnection.wrap( - self.mock_server_conn.return_value, x, y, as_non_blocking=as_non_blocking, + lambda x, y, as_non_blocking, verify_mode: TcpServerConnection.wrap( + self.mock_server_conn.return_value, x, y, as_non_blocking=as_non_blocking, verify_mode=verify_mode, ) type(self.mock_server_conn.return_value).connection = \ diff --git a/tests/plugin/test_http_proxy_plugins_with_tls_interception.py b/tests/plugin/test_http_proxy_plugins_with_tls_interception.py index a0a05b61..d07ab647 100644 --- a/tests/plugin/test_http_proxy_plugins_with_tls_interception.py +++ b/tests/plugin/test_http_proxy_plugins_with_tls_interception.py @@ -29,6 +29,7 @@ from proxy.http.responses import ( from proxy.core.connection import TcpServerConnection from .utils import get_plugin_by_test_name from ..test_assertions import Assertions +from ..certificates.test_cert_data import mock_cert class TestHttpProxyPluginExamplesWithTlsInterception(Assertions): @@ -78,8 +79,8 @@ class TestHttpProxyPluginExamplesWithTlsInterception(Assertions): self.protocol_handler.initialize() self.server = self.mock_server_conn.return_value - self.server_ssl_connection = mocker.MagicMock(spec=ssl.SSLSocket) + self.server_ssl_connection.getpeercert = mock_cert self.mock_ssl_context.return_value.wrap_socket.return_value = self.server_ssl_connection self.client_ssl_connection = mocker.MagicMock(spec=ssl.SSLSocket) self.mock_ssl_wrap.return_value.wrap_socket.return_value = self.client_ssl_connection @@ -97,8 +98,8 @@ class TestHttpProxyPluginExamplesWithTlsInterception(Assertions): # Do not mock the original wrap method self.server.wrap.side_effect = \ - lambda x, y, as_non_blocking: TcpServerConnection.wrap( - self.server, x, y, as_non_blocking=as_non_blocking, + lambda x, y, as_non_blocking, verify_mode: TcpServerConnection.wrap( + self.server, x, y, as_non_blocking=as_non_blocking, verify_mode=verify_mode, ) self.server.has_buffer.side_effect = has_buffer