From e2f8dab7b89cf482bac71b882532b139d58f844a Mon Sep 17 00:00:00 2001 From: d4x Date: Mon, 29 Apr 2024 17:13:53 -0400 Subject: [PATCH 01/11] Disable mandatory TLS verification with --insecure --- README.md | 77 ++++++++++--------- proxy/common/constants.py | 1 + proxy/common/utils.py | 15 ++++ proxy/core/connection/server.py | 5 +- proxy/core/listener/pool.py | 4 +- proxy/core/ssh/handler.py | 12 ++- proxy/http/proxy/server.py | 24 ++++-- proxy/proxy.py | 11 ++- tests/certificates/test_cert_data.py | 13 ++++ .../proxy/test_http_proxy_tls_interception.py | 9 ++- ...ttp_proxy_plugins_with_tls_interception.py | 10 ++- 11 files changed, 122 insertions(+), 59 deletions(-) create mode 100644 tests/certificates/test_cert_data.py diff --git a/README.md b/README.md index 83b68c6486..f6739fb0bf 100644 --- a/README.md +++ b/README.md @@ -1206,6 +1206,8 @@ cached file instead of plain text. Now use CA flags with other [plugin examples](#plugin-examples) to see them work with `https` traffic. +To intercept TLS traffic from a server using a self-signed certificate add the `--insecure` flag to disable mandatory TLS certificate validation. + ## TLS Interception With Docker Important notes about TLS Interception with Docker container: @@ -1303,17 +1305,16 @@ See [requirements-tunnel.txt](https://github.com/abhinavsingh/proxy.py/blob/deve | +------------+ | +----------+ | LOCAL | | | REMOTE | - | HOST | <== SSH ==== :8900 == | SERVER | + | HOST | <== SSH ==== :8900 == | PROXY | +------------+ | +----------+ :8899 proxy.py | | FIREWALL (allow tcp/22) -## What +### What -Proxy HTTP(s) requests made on a `remote` server through `proxy.py` server -running on `localhost`. +Proxy HTTP(s) requests made on a `remote` proxy server through `proxy.py` server running on `localhost`. ### How @@ -1335,7 +1336,7 @@ Start `proxy.py` as: ```console ❯ # On localhost -❯ proxy --enable-tunnel \ +❯ proxy --enable-ssh-tunnel \ --tunnel-username username \ --tunnel-hostname ip.address.or.domain.name \ --tunnel-port 22 \ @@ -2341,19 +2342,19 @@ To run standalone benchmark for `proxy.py`, use the following command from repo ```console ❯ proxy -h -usage: -m [-h] [--threadless] [--threaded] [--num-workers NUM_WORKERS] - [--enable-events] [--local-executor LOCAL_EXECUTOR] - [--backlog BACKLOG] [--hostname HOSTNAME] - [--hostnames HOSTNAMES [HOSTNAMES ...]] [--port PORT] - [--ports PORTS [PORTS ...]] [--port-file PORT_FILE] - [--unix-socket-path UNIX_SOCKET_PATH] - [--num-acceptors NUM_ACCEPTORS] [--tunnel-hostname TUNNEL_HOSTNAME] - [--tunnel-port TUNNEL_PORT] [--tunnel-username TUNNEL_USERNAME] +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] [--version] - [--log-level LOG_LEVEL] [--log-file LOG_FILE] - [--log-format LOG_FORMAT] [--open-file-limit OPEN_FILE_LIMIT] + [--tunnel-remote-port TUNNEL_REMOTE_PORT] [--threadless] + [--threaded] [--num-workers NUM_WORKERS] [--enable-events] + [--local-executor LOCAL_EXECUTOR] [--backlog BACKLOG] + [--hostname HOSTNAME] [--hostnames HOSTNAMES [HOSTNAMES ...]] + [--port PORT] [--ports PORTS [PORTS ...]] [--port-file PORT_FILE] + [--unix-socket-path UNIX_SOCKET_PATH] + [--num-acceptors NUM_ACCEPTORS] [--version] [--log-level LOG_LEVEL] + [--log-file LOG_FILE] [--log-format LOG_FORMAT] + [--open-file-limit OPEN_FILE_LIMIT] [--plugins PLUGINS [PLUGINS ...]] [--enable-dashboard] [--basic-auth BASIC_AUTH] [--enable-ssh-tunnel] [--work-klass WORK_KLASS] [--pid-file PID_FILE] [--openssl OPENSSL] @@ -2363,7 +2364,7 @@ usage: -m [-h] [--threadless] [--threaded] [--num-workers NUM_WORKERS] [--server-recvbuf-size SERVER_RECVBUF_SIZE] [--max-sendbuf-size MAX_SENDBUF_SIZE] [--timeout TIMEOUT] [--disable-http-proxy] [--disable-headers DISABLE_HEADERS] - [--ca-key-file CA_KEY_FILE] [--ca-cert-dir CA_CERT_DIR] + [--ca-key-file CA_KEY_FILE] [--insecure] [--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] @@ -2379,10 +2380,25 @@ usage: -m [-h] [--threadless] [--threaded] [--num-workers NUM_WORKERS] [--filtered-client-ips FILTERED_CLIENT_IPS] [--filtered-url-regex-config FILTERED_URL_REGEX_CONFIG] -proxy.py v2.4.4rc6.dev164+g73497f30 +proxy.py v2.4.4rc6.dev8+g81aa82b.d20240429 options: -h, --help show this help message and exit + --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. @@ -2419,21 +2435,6 @@ options: --host and --port flags are ignored --num-acceptors NUM_ACCEPTORS Defaults to number of CPU cores. - --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. --version, -v Prints proxy.py version. --log-level LOG_LEVEL Valid options: DEBUG, INFO (default), WARNING, ERROR, @@ -2498,6 +2499,7 @@ 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 Default: False. Disables certificate verification --ca-cert-dir CA_CERT_DIR Default: ~/.proxy/certificates. Directory to store dynamically generated certificates. Also see --ca-key- @@ -2506,8 +2508,8 @@ 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/.venv31010/l - ib/python3.10/site-packages/certifi/cacert.pem. + --ca-file CA_FILE Default: /home/kali/projects/proxy_add_selfsinged/venv + /lib/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 @@ -2524,9 +2526,8 @@ options: from responses. Extracted content type is written to the cache directory e.g. video.mp4. --cache-dir CACHE_DIR - Default: /Users/abhinavsingh/.proxy/cache. Flag only - applicable when cache plugin is used with on-disk - storage. + Default: /home/kali/.proxy/cache. Flag only applicable + when cache plugin is used with on-disk storage. --proxy-pool PROXY_POOL List of upstream proxies to use in the pool --enable-web-server Default: False. Whether to enable diff --git a/proxy/common/constants.py b/proxy/common/constants.py index dcb1f46638..bfcf6375bc 100644 --- a/proxy/common/constants.py +++ b/proxy/common/constants.py @@ -90,6 +90,7 @@ def _env_threadless_compliant() -> bool: DEFAULT_MAX_SEND_SIZE = 64 * 1024 DEFAULT_BUFFER_SIZE = 128 * 1024 DEFAULT_CA_CERT_DIR = None +DEFAULT_INSECURE = 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 4e2eed8144..dc8886a420 100644 --- a/proxy/common/utils.py +++ b/proxy/common/utils.py @@ -17,12 +17,15 @@ 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 +39,18 @@ logger = logging.getLogger(__name__) +def cert_der_to_dict(certificate_der: Optional[bytes]) -> dict[str, Any]: + """Parse a DER formatted certificate to a python dict""" + if not certificate_der: + return {} + with tempfile.NamedTemporaryFile() as cert_file: + certificate_pem = ssl.DER_cert_to_PEM_cert(certificate_der) + cert_file.write(certificate_pem.encode()) + cert_file.seek(0) + certificate = _ssl._test_decode_cert(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 31233049f7..238a9307cd 100644 --- a/proxy/core/connection/server.py +++ b/proxy/core/connection/server.py @@ -56,7 +56,10 @@ def wrap( cafile=ca_file, ) 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 + if verify_mode == ssl.VerifyMode.CERT_NONE: + ctx.check_hostname = False + else: + 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/core/listener/pool.py b/proxy/core/listener/pool.py index f9befa9c17..b0945bfd10 100644 --- a/proxy/core/listener/pool.py +++ b/proxy/core/listener/pool.py @@ -39,9 +39,9 @@ def setup(self) -> None: if self.flags.unix_socket_path: self.add(UnixSocketListener) hostnames = {self.flags.hostname, *self.flags.hostnames} - ports = set(self.flags.ports) + ports = list(self.flags.ports) if not self.flags.unix_socket_path: - ports.add(self.flags.port) + ports.append(self.flags.port) for hostname, port in itertools.product(hostnames, ports): self.add(TcpSocketListener, hostname=hostname, port=port) diff --git a/proxy/core/ssh/handler.py b/proxy/core/ssh/handler.py index ed6ea789ff..1281d4675e 100644 --- a/proxy/core/ssh/handler.py +++ b/proxy/core/ssh/handler.py @@ -8,9 +8,14 @@ :copyright: (c) 2013-present by Abhinav Singh and contributors. :license: BSD, see LICENSE for more details. """ +import logging import argparse from typing import TYPE_CHECKING +from .base import BaseSshTunnelHandler + + +logger = logging.getLogger(__name__) if TYPE_CHECKING: # pragma: no cover from ...common.types import HostPort @@ -20,7 +25,7 @@ pass -class SshHttpProtocolHandler: +class SshHttpProtocolHandler(BaseSshTunnelHandler): """Handles incoming connections over forwarded SSH transport.""" def __init__(self, flags: argparse.Namespace) -> None: @@ -32,4 +37,7 @@ def on_connection( origin: 'HostPort', server: 'HostPort', ) -> None: - pass + logger.debug('handle proxy request') + + def shutdown(self) -> None: + logger.debug('ssh handler shutdown') diff --git a/proxy/http/proxy/server.py b/proxy/http/proxy/server.py index 4e2f44ac3f..1ba37bc236 100644 --- a/proxy/http/proxy/server.py +++ b/proxy/http/proxy/server.py @@ -21,7 +21,7 @@ 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,15 +35,16 @@ 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, ) 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, + COMMA, DEFAULT_CA_FILE, DEFAULT_INSECURE, 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_HTTPS_PROXY_ACCESS_LOG_FORMAT, ) @@ -74,6 +75,13 @@ 'HTTPS certificates. If used, must also pass --ca-cert-file and --ca-signing-key-file', ) +flags.add_argument( + '--insecure', + action='store_true', + default=DEFAULT_INSECURE, + help='Default: False. Disables certificate verification', +) + flags.add_argument( '--ca-cert-dir', type=str, @@ -760,10 +768,12 @@ def wrap_server(self) -> bool: assert isinstance(self.upstream.connection, socket.socket) do_close = False try: + verify_mode = ssl.VerifyMode.CERT_NONE if self.flags.insecure 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 +812,7 @@ def wrap_client(self) -> bool: 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/proxy/proxy.py b/proxy/proxy.py index 2350f4e88f..233893f15f 100644 --- a/proxy/proxy.py +++ b/proxy/proxy.py @@ -18,6 +18,7 @@ import threading from typing import TYPE_CHECKING, Any, List, Type, Optional, cast +from .core.ssh import SshTunnelListener, SshHttpProtocolHandler from .core.work import ThreadlessPool from .core.event import EventManager from .common.flag import FlagParser, flags @@ -284,13 +285,15 @@ def setup(self) -> None: @staticmethod def _setup_tunnel( flags: argparse.Namespace, - ssh_handler_klass: Type['BaseSshTunnelHandler'], - ssh_listener_klass: Any, + ssh_handler_klass: Optional[Type['BaseSshTunnelHandler']] = None, + ssh_listener_klass: Optional[Any] = None, **kwargs: Any, ) -> BaseSshTunnelListener: - tunnel = cast(Type[BaseSshTunnelListener], ssh_listener_klass)( + listener_klass = ssh_listener_klass or SshTunnelListener + handler_klass = ssh_handler_klass or SshHttpProtocolHandler + tunnel = cast(Type[BaseSshTunnelListener], listener_klass)( flags=flags, - handler=ssh_handler_klass(flags=flags), + handler=handler_klass(flags=flags), **kwargs, ) tunnel.setup() diff --git a/tests/certificates/test_cert_data.py b/tests/certificates/test_cert_data.py new file mode 100644 index 0000000000..515ba11313 --- /dev/null +++ b/tests/certificates/test_cert_data.py @@ -0,0 +1,13 @@ +# -*- 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. +""" +# flake8: noqa +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' +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 654bbc5fcd..3b412d190d 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.core.connection import TcpServerConnection from proxy.common.constants import DEFAULT_CA_FILE from ...test_assertions import Assertions +from ...certificates.test_cert_data import test_cert_bytes class TestHttpProxyTlsInterception(Assertions): @@ -56,9 +57,13 @@ async def test_e2e(self, mocker: MockerFixture) -> None: self.fileno = 10 self.mock_socket_dup.side_effect = lambda fd: fd + def mock_cert(a: Any) -> Any: + return test_cert_bytes + # 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 +80,8 @@ def mock_connection() -> Any: # 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 3d8d6a28f4..2d6d0d60af 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.core.connection import TcpServerConnection from .utils import get_plugin_by_test_name from ..test_assertions import Assertions +from ..certificates.test_cert_data import test_cert_bytes class TestHttpProxyPluginExamplesWithTlsInterception(Assertions): @@ -77,9 +78,12 @@ def _setUp(self, request: Any, mocker: MockerFixture) -> None: ) self.protocol_handler.initialize() - self.server = self.mock_server_conn.return_value + def mock_cert(a: Any) -> Any: + return test_cert_bytes + 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 = self.client_ssl_connection @@ -97,8 +101,8 @@ def mock_connection() -> Any: # 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 From dbf65b25c4782e230633901a5ffdddfa7f88b059 Mon Sep 17 00:00:00 2001 From: Abhinav Singh Date: Sat, 10 Aug 2024 11:02:03 +0530 Subject: [PATCH 02/11] Fix lint issues --- tests/certificates/test_cert_data.py | 38 +++++++++++++++++-- .../proxy/test_http_proxy_tls_interception.py | 5 +-- ...ttp_proxy_plugins_with_tls_interception.py | 5 +-- 3 files changed, 37 insertions(+), 11 deletions(-) diff --git a/tests/certificates/test_cert_data.py b/tests/certificates/test_cert_data.py index 515ba11313..7bf24bf6f4 100644 --- a/tests/certificates/test_cert_data.py +++ b/tests/certificates/test_cert_data.py @@ -8,6 +8,38 @@ :copyright: (c) 2013-present by Abhinav Singh and contributors. :license: BSD, see LICENSE for more details. """ -# flake8: noqa -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' -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'} +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" + + +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 96795f4a8d..fad6eff189 100644 --- a/tests/http/proxy/test_http_proxy_tls_interception.py +++ b/tests/http/proxy/test_http_proxy_tls_interception.py @@ -30,7 +30,7 @@ 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 test_cert_bytes +from ...certificates.test_cert_data import mock_cert class TestHttpProxyTlsInterception(Assertions): @@ -57,9 +57,6 @@ async def test_e2e(self, mocker: MockerFixture) -> None: self.fileno = 10 self.mock_socket_dup.side_effect = lambda fd: fd - def mock_cert(a: Any) -> Any: - return test_cert_bytes - # Used for server side wrapping self.mock_ssl_context = mocker.patch('ssl.create_default_context') upstream_tls_sock = mock.MagicMock(spec=ssl.SSLSocket) 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 6383817abc..d07ab647d2 100644 --- a/tests/plugin/test_http_proxy_plugins_with_tls_interception.py +++ b/tests/plugin/test_http_proxy_plugins_with_tls_interception.py @@ -29,7 +29,7 @@ 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 test_cert_bytes +from ..certificates.test_cert_data import mock_cert class TestHttpProxyPluginExamplesWithTlsInterception(Assertions): @@ -78,9 +78,6 @@ def _setUp(self, request: Any, mocker: MockerFixture) -> None: ) self.protocol_handler.initialize() - def mock_cert(a: Any) -> Any: - return test_cert_bytes - self.server = self.mock_server_conn.return_value self.server_ssl_connection = mocker.MagicMock(spec=ssl.SSLSocket) self.server_ssl_connection.getpeercert = mock_cert From 615ceb3ef67f1557a11635330445098b6e78454a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 10 Aug 2024 05:33:42 +0000 Subject: [PATCH 03/11] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/certificates/test_cert_data.py | 40 ++++++++++++++-------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/tests/certificates/test_cert_data.py b/tests/certificates/test_cert_data.py index 7bf24bf6f4..e6edfd0d9d 100644 --- a/tests/certificates/test_cert_data.py +++ b/tests/certificates/test_cert_data.py @@ -20,26 +20,26 @@ def mock_cert(_: Any) -> Any: cert_dict = { - "subject": ( - (("countryName", "as"),), - (("stateOrProvinceName", "as"),), - (("localityName", "as"),), - (("organizationName", "as"),), - (("organizationalUnitName", "as"),), - (("commonName", "as"),), - (("emailAddress", "as"),), + '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"),), + '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", + 'version': 3, + 'serialNumber': '5045018CA6EAD823CF90B044C704DE9B3959F320', + 'notBefore': 'Apr 29 12:50:57 2024 GMT', + 'notAfter': 'Apr 29 12:50:57 2025 GMT', } From 53375eadbc55c913ebdce208cc191a31802b9e4b Mon Sep 17 00:00:00 2001 From: Abhinav Singh Date: Sat, 10 Aug 2024 11:08:58 +0530 Subject: [PATCH 04/11] Fix type issues with `cert_der_to_dict` --- proxy/common/utils.py | 6 +++--- tests/certificates/test_cert_data.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/proxy/common/utils.py b/proxy/common/utils.py index bb0e8c6db5..93c1f12a3b 100644 --- a/proxy/common/utils.py +++ b/proxy/common/utils.py @@ -39,12 +39,12 @@ logger = logging.getLogger(__name__) -def cert_der_to_dict(certificate_der: Optional[bytes]) -> dict[str, Any]: +def cert_der_to_dict(der: Optional[bytes]) -> Dict[str, Any]: """Parse a DER formatted certificate to a python dict""" - if not certificate_der: + if not der: return {} with tempfile.NamedTemporaryFile() as cert_file: - certificate_pem = ssl.DER_cert_to_PEM_cert(certificate_der) + certificate_pem = ssl.DER_cert_to_PEM_cert(der) cert_file.write(certificate_pem.encode()) cert_file.seek(0) certificate = _ssl._test_decode_cert(cert_file.name) diff --git a/tests/certificates/test_cert_data.py b/tests/certificates/test_cert_data.py index e6edfd0d9d..4c37e722ab 100644 --- a/tests/certificates/test_cert_data.py +++ b/tests/certificates/test_cert_data.py @@ -12,7 +12,7 @@ # 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" +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 def mock_cert(_: Any) -> Any: From 7df166db6e93f10d2a3db9fd08d7fb78eafa3fbd Mon Sep 17 00:00:00 2001 From: Abhinav Singh Date: Sat, 10 Aug 2024 11:10:57 +0530 Subject: [PATCH 05/11] Flake8 exception --- tests/certificates/test_cert_data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/certificates/test_cert_data.py b/tests/certificates/test_cert_data.py index 4c37e722ab..612429bfee 100644 --- a/tests/certificates/test_cert_data.py +++ b/tests/certificates/test_cert_data.py @@ -12,7 +12,7 @@ # 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 +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: From 7facb78cf99f8f1534d146798ec9fe5526e80846 Mon Sep 17 00:00:00 2001 From: Abhinav Singh Date: Sat, 10 Aug 2024 11:26:07 +0530 Subject: [PATCH 06/11] Fix `cert_der_to_dict` where file may not be writter before it is gets used by `_test_decode_cert` --- proxy/common/utils.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/proxy/common/utils.py b/proxy/common/utils.py index 93c1f12a3b..84f3484a48 100644 --- a/proxy/common/utils.py +++ b/proxy/common/utils.py @@ -12,6 +12,7 @@ utils """ +import os import ssl import sys import socket @@ -43,11 +44,16 @@ 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() as cert_file: - certificate_pem = ssl.DER_cert_to_PEM_cert(der) - cert_file.write(certificate_pem.encode()) + 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 {} From 618cead7f82cd67f883ae31be338523e11a21a01 Mon Sep 17 00:00:00 2001 From: Abhinav Singh Date: Sat, 10 Aug 2024 11:36:53 +0530 Subject: [PATCH 07/11] Silence lint issue due to pylint bug --- proxy/core/connection/server.py | 2 +- proxy/http/proxy/server.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/proxy/core/connection/server.py b/proxy/core/connection/server.py index 238a9307cd..ff879bcd9e 100644 --- a/proxy/core/connection/server.py +++ b/proxy/core/connection/server.py @@ -56,7 +56,7 @@ def wrap( cafile=ca_file, ) ctx.options |= ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3 | ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1 - if verify_mode == ssl.VerifyMode.CERT_NONE: + if verify_mode == ssl.VerifyMode.CERT_NONE: # pylint: disable=E1101 ctx.check_hostname = False else: ctx.check_hostname = hostname is not None diff --git a/proxy/http/proxy/server.py b/proxy/http/proxy/server.py index 1ba37bc236..2106b3abbd 100644 --- a/proxy/http/proxy/server.py +++ b/proxy/http/proxy/server.py @@ -768,6 +768,7 @@ def wrap_server(self) -> bool: assert isinstance(self.upstream.connection, socket.socket) do_close = False try: + # pylint: disable=E1101 verify_mode = ssl.VerifyMode.CERT_NONE if self.flags.insecure else ssl.VerifyMode.CERT_REQUIRED self.upstream.wrap( text_(self.request.host), From baecc8e0cca0b23c2d6bd8bf70426a734e6d9820 Mon Sep 17 00:00:00 2001 From: Abhinav Singh Date: Sat, 10 Aug 2024 11:44:20 +0530 Subject: [PATCH 08/11] Rename flag to `--insecure-tls-interception` --- README.md | 81 +++++++++++++++++++++----------------- proxy/common/constants.py | 2 +- proxy/http/proxy/server.py | 22 ++++++----- 3 files changed, 58 insertions(+), 47 deletions(-) diff --git a/README.md b/README.md index 9c259554f7..bf3964d933 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,7 +1242,12 @@ cached file instead of plain text. Now use CA flags with other [plugin examples](#plugin-examples) to see them work with `https` traffic. -To intercept TLS traffic from a server using a self-signed certificate add the `--insecure` flag to disable mandatory TLS certificate validation. +## 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 @@ -2512,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] @@ -2535,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] [--insecure] [--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] @@ -2551,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. @@ -2569,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 @@ -2590,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 @@ -2670,7 +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 Default: False. Disables certificate verification + --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- @@ -2679,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 @@ -2697,8 +2703,9 @@ options: from responses. Extracted content type is written to the cache directory e.g. video.mp4. --cache-dir CACHE_DIR - Default: /home/kali/.proxy/cache. Flag only applicable - when cache plugin is used with on-disk storage. + Default: /Users/abhinavsingh/.proxy/cache. Flag only + applicable when cache plugin is used with on-disk + storage. --proxy-pool PROXY_POOL List of upstream proxies to use in the pool --enable-web-server Default: False. Whether to enable diff --git a/proxy/common/constants.py b/proxy/common/constants.py index 44a3e15bbe..ca47abdb18 100644 --- a/proxy/common/constants.py +++ b/proxy/common/constants.py @@ -91,7 +91,7 @@ def _env_threadless_compliant() -> bool: DEFAULT_MAX_SEND_SIZE = 64 * 1024 DEFAULT_BUFFER_SIZE = 128 * 1024 DEFAULT_CA_CERT_DIR = None -DEFAULT_INSECURE = False +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/http/proxy/server.py b/proxy/http/proxy/server.py index 2106b3abbd..b6f9c1a04d 100644 --- a/proxy/http/proxy/server.py +++ b/proxy/http/proxy/server.py @@ -40,10 +40,10 @@ TcpServerConnection, TcpConnectionUninitializedException, ) from ...common.constants import ( - COMMA, DEFAULT_CA_FILE, DEFAULT_INSECURE, 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, + 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_INSECURE_TLS_INTERCEPTION, DEFAULT_HTTP_PROXY_ACCESS_LOG_FORMAT, DEFAULT_HTTPS_PROXY_ACCESS_LOG_FORMAT, ) @@ -76,10 +76,10 @@ ) flags.add_argument( - '--insecure', - action='store_true', - default=DEFAULT_INSECURE, - help='Default: False. Disables certificate verification', + "--insecure-tls-interception", + action="store_true", + default=DEFAULT_INSECURE_TLS_INTERCEPTION, + help="Default: False. Disables certificate verification", ) flags.add_argument( @@ -769,7 +769,11 @@ def wrap_server(self) -> bool: do_close = False try: # pylint: disable=E1101 - verify_mode = ssl.VerifyMode.CERT_NONE if self.flags.insecure else ssl.VerifyMode.CERT_REQUIRED + 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, From 9c33a52267b359dd46f93bd70aec3dc5787b4f7b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 10 Aug 2024 06:15:09 +0000 Subject: [PATCH 09/11] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- proxy/http/proxy/server.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proxy/http/proxy/server.py b/proxy/http/proxy/server.py index b6f9c1a04d..80af1686c0 100644 --- a/proxy/http/proxy/server.py +++ b/proxy/http/proxy/server.py @@ -76,10 +76,10 @@ ) flags.add_argument( - "--insecure-tls-interception", - action="store_true", + '--insecure-tls-interception', + action='store_true', default=DEFAULT_INSECURE_TLS_INTERCEPTION, - help="Default: False. Disables certificate verification", + help='Default: False. Disables certificate verification', ) flags.add_argument( From bed356c31c2b265aaed7a03b451b4e21442d1889 Mon Sep 17 00:00:00 2001 From: Abhinav Singh Date: Sat, 10 Aug 2024 11:51:42 +0530 Subject: [PATCH 10/11] Reuse `DEFAULT_SSL_CONTEXT_OPTIONS` --- proxy/common/utils.py | 3 ++- proxy/core/connection/server.py | 14 ++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/proxy/common/utils.py b/proxy/common/utils.py index 84f3484a48..751e4a634c 100644 --- a/proxy/common/utils.py +++ b/proxy/common/utils.py @@ -25,7 +25,8 @@ from types import TracebackType from typing import Any, Dict, List, Type, Tuple, Callable, Optional -import _ssl # noqa: WPS436 +# noqa: WPS436 +import _ssl # type: ignore[import-not-found] from .types import HostPort from .constants import ( diff --git a/proxy/core/connection/server.py b/proxy/core/connection/server.py index ff879bcd9e..619ada4fc7 100644 --- a/proxy/core/connection/server.py +++ b/proxy/core/connection/server.py @@ -15,6 +15,7 @@ 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,15 +52,12 @@ def wrap( # 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 - if verify_mode == ssl.VerifyMode.CERT_NONE: # pylint: disable=E1101 - ctx.check_hostname = False - else: - ctx.check_hostname = hostname is not None ctx.verify_mode = verify_mode self.connection.setblocking(True) self._conn = ctx.wrap_socket( From 4734c5f4563b38e7050d431c74670c8be1e56b21 Mon Sep 17 00:00:00 2001 From: Abhinav Singh Date: Sat, 10 Aug 2024 11:54:04 +0530 Subject: [PATCH 11/11] # noqa: WPS436 --- proxy/common/utils.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/proxy/common/utils.py b/proxy/common/utils.py index 751e4a634c..84f3484a48 100644 --- a/proxy/common/utils.py +++ b/proxy/common/utils.py @@ -25,8 +25,7 @@ from types import TracebackType from typing import Any, Dict, List, Type, Tuple, Callable, Optional -# noqa: WPS436 -import _ssl # type: ignore[import-not-found] +import _ssl # noqa: WPS436 from .types import HostPort from .constants import (