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 <d_4xfe@proton.me>
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>
This commit is contained in:
Abhinav Singh 2024-08-10 12:12:55 +05:30 committed by GitHub
parent 39854e1d79
commit 50046d3f68
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 142 additions and 47 deletions

View File

@ -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

View File

@ -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

View File

@ -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 \

View File

@ -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(

View File

@ -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

View File

@ -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',
}

View File

@ -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 = \

View File

@ -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