Skip to content

Commit 86e69f2

Browse files
authored
Merge branch 'master' into fix/retry
2 parents 73309b2 + 273bd56 commit 86e69f2

File tree

5 files changed

+75
-6
lines changed

5 files changed

+75
-6
lines changed

.readthedocs.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ version: 2
77

88
# Set the version of Python and other tools you might need
99
build:
10-
os: ubuntu-22.04
10+
os: "ubuntu-lts-latest"
1111
tools:
12-
python: "3.7"
12+
python: "latest"
1313
# You can also specify other tool versions:
1414
# nodejs: "19"
1515
# rust: "1.64"

kazoo/client.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ def __init__(
120120
ca=None,
121121
use_ssl=False,
122122
verify_certs=True,
123+
check_hostname=False,
123124
**kwargs,
124125
):
125126
"""Create a :class:`KazooClient` instance. All time arguments
@@ -182,6 +183,8 @@ def __init__(
182183
:param use_ssl: argument to control whether SSL is used or not
183184
:param verify_certs: when using SSL, argument to bypass
184185
certs verification
186+
:param check_hostname: when using SSL, check the hostname
187+
against the hostname in the cert
185188
186189
Basic Example:
187190
@@ -237,6 +240,7 @@ def __init__(
237240

238241
self.use_ssl = use_ssl
239242
self.verify_certs = verify_certs
243+
self.check_hostname = check_hostname
240244
self.certfile = certfile
241245
self.keyfile = keyfile
242246
self.keyfile_password = keyfile_password
@@ -758,15 +762,18 @@ def command(self, cmd=b"ruok"):
758762
raise ConnectionLoss("No connection to server")
759763

760764
peer = self._connection._socket.getpeername()[:2]
765+
peer_host = self._connection._socket.getpeername()[1]
761766
sock = self.handler.create_connection(
762767
peer,
768+
hostname=peer_host,
763769
timeout=self._session_timeout / 1000.0,
764770
use_ssl=self.use_ssl,
765771
ca=self.ca,
766772
certfile=self.certfile,
767773
keyfile=self.keyfile,
768774
keyfile_password=self.keyfile_password,
769775
verify_certs=self.verify_certs,
776+
check_hostname=self.check_hostname,
770777
)
771778
sock.sendall(cmd)
772779
result = sock.recv(8192)

kazoo/handlers/utils.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -196,13 +196,15 @@ def create_tcp_socket(module):
196196
def create_tcp_connection(
197197
module,
198198
address,
199+
hostname=None,
199200
timeout=None,
200201
use_ssl=False,
201202
ca=None,
202203
certfile=None,
203204
keyfile=None,
204205
keyfile_password=None,
205206
verify_certs=True,
207+
check_hostname=False,
206208
options=None,
207209
ciphers=None,
208210
):
@@ -237,11 +239,14 @@ def create_tcp_connection(
237239

238240
# Load default CA certs
239241
context.load_default_certs(ssl.Purpose.SERVER_AUTH)
242+
if check_hostname and not verify_certs:
243+
raise ValueError(
244+
"verify_certs must be True when"
245+
+ " check_hostname is True"
246+
)
240247
# We must set check_hostname to False prior to setting
241248
# verify_mode to CERT_NONE.
242-
# TODO: Make hostname verification configurable as some users may
243-
# elect to use it.
244-
context.check_hostname = False
249+
context.check_hostname = check_hostname
245250
context.verify_mode = (
246251
ssl.CERT_REQUIRED if verify_certs else ssl.CERT_NONE
247252
)
@@ -258,7 +263,9 @@ def create_tcp_connection(
258263
addrs = socket.getaddrinfo(
259264
address[0], address[1], 0, socket.SOCK_STREAM
260265
)
261-
conn = context.wrap_socket(module.socket(addrs[0][0]))
266+
conn = context.wrap_socket(
267+
module.socket(addrs[0][0]), server_hostname=hostname
268+
)
262269
conn.settimeout(timeout_at)
263270
conn.connect(address)
264271
sock = conn

kazoo/protocol/connection.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -703,13 +703,15 @@ def _connect(self, host, hostip, port):
703703
with self._socket_error_handling():
704704
self._socket = self.handler.create_connection(
705705
address=(hostip, port),
706+
hostname=host,
706707
timeout=client._session_timeout / 1000.0,
707708
use_ssl=self.client.use_ssl,
708709
keyfile=self.client.keyfile,
709710
certfile=self.client.certfile,
710711
ca=self.client.ca,
711712
keyfile_password=self.client.keyfile_password,
712713
verify_certs=self.client.verify_certs,
714+
check_hostname=self.client.check_hostname,
713715
)
714716

715717
self._socket.setblocking(0)

kazoo/tests/test_utils.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,59 @@ def test_timeout_arg(self):
3030
timeout = call_args[0][1]
3131
assert timeout >= 0, "socket timeout must be nonnegative"
3232

33+
def test_ssl_server_hostname(self):
34+
from kazoo.handlers import utils
35+
from kazoo.handlers.utils import create_tcp_connection, socket, ssl
36+
37+
with patch.object(utils, "_set_default_tcpsock_options"):
38+
with patch.object(ssl.SSLContext, "wrap_socket") as wrap_socket:
39+
create_tcp_connection(
40+
socket,
41+
("127.0.0.1", 2181),
42+
timeout=1.5,
43+
hostname="fakehostname",
44+
use_ssl=True,
45+
)
46+
47+
for call_args in wrap_socket.call_args_list:
48+
server_hostname = call_args[1]["server_hostname"]
49+
assert server_hostname == "fakehostname"
50+
51+
def test_ssl_server_check_hostname(self):
52+
from kazoo.handlers import utils
53+
from kazoo.handlers.utils import create_tcp_connection, socket, ssl
54+
55+
with patch.object(utils, "_set_default_tcpsock_options"):
56+
with patch.object(
57+
ssl.SSLContext, "wrap_socket", autospec=True
58+
) as wrap_socket:
59+
create_tcp_connection(
60+
socket,
61+
("127.0.0.1", 2181),
62+
timeout=1.5,
63+
hostname="fakehostname",
64+
use_ssl=True,
65+
check_hostname=True,
66+
)
67+
68+
for call_args in wrap_socket.call_args_list:
69+
ssl_context = call_args[0][0]
70+
assert ssl_context.check_hostname
71+
72+
def test_ssl_server_check_hostname_config_validation(self):
73+
from kazoo.handlers.utils import create_tcp_connection, socket
74+
75+
with pytest.raises(ValueError):
76+
create_tcp_connection(
77+
socket,
78+
("127.0.0.1", 2181),
79+
timeout=1.5,
80+
hostname="fakehostname",
81+
use_ssl=True,
82+
verify_certs=False,
83+
check_hostname=True,
84+
)
85+
3386
def test_timeout_arg_eventlet(self):
3487
if not EVENTLET_HANDLER_AVAILABLE:
3588
pytest.skip("eventlet handler not available.")

0 commit comments

Comments
 (0)