Skip to content

Commit

Permalink
Dependencies: Permit installation with urllib3 2.0
Browse files Browse the repository at this point in the history
  • Loading branch information
amotl committed Sep 5, 2023
1 parent d011673 commit 69e6156
Show file tree
Hide file tree
Showing 6 changed files with 61 additions and 2 deletions.
9 changes: 9 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@ Unreleased

- Properly handle Python-native UUID types in SQL parameters
- SQLAlchemy: Fix handling URL parameters ``timeout`` and ``pool_size``
- Permit installation with urllib3 v2, see also `urllib3 v2.0 roadmap`_
and `urllib3 v2.0 migration guide`_. You can optionally retain support
for TLS 1.0 and TLS 1.1, but a few other outdated use-cases of X.509
certificate details are immanent, like no longer accepting the long
deprecated ``commonName`` attribute. Instead, going forward, only the
``subjectAltName`` attribute will be used.

.. _urllib3 v2.0 migration guide: https://urllib3.readthedocs.io/en/latest/v2-migration-guide.html
.. _urllib3 v2.0 roadmap: https://urllib3.readthedocs.io/en/stable/v2-roadmap.html


2023/07/17 0.33.0
Expand Down
15 changes: 15 additions & 0 deletions docs/by-example/https.rst
Original file line number Diff line number Diff line change
Expand Up @@ -110,3 +110,18 @@ The connection will also fail when providing an invalid CA certificate:
Traceback (most recent call last):
...
crate.client.exceptions.ConnectionError: Server not available, exception: HTTPSConnectionPool...


Relaxing minimum SSL version
============================

urrlib3 v2 dropped support for TLS 1.0 and TLS 1.1 by default, see `Modern security by default -
HTTPS requires TLS 1.2+`_. If you need to re-enable it, use the ``ssl_relax_minimum_version`` flag,
which will configure ``kwargs["ssl_minimum_version"] = ssl.TLSVersion.MINIMUM_SUPPORTED``.

>>> client = HttpClient([crate_host], ssl_relax_minimum_version=True, verify_ssl_cert=False)
>>> client.server_infos(crate_host)
('https://localhost:65534', 'test', '0.0.0')


.. _Modern security by default - HTTPS requires TLS 1.2+: https://urllib3.readthedocs.io/en/latest/v2-migration-guide.html#https-requires-tls-1-2
11 changes: 11 additions & 0 deletions docs/connect.rst
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,16 @@ Here, replace ``<CERT_FILE>`` with the path to the client certificate file, and
verification. In such circumstances, you can combine the two methods above
to do both at once.

Relaxing minimum SSL version
............................

urrlib3 v2 dropped support for TLS 1.0 and TLS 1.1 by default, see `Modern security by default -
HTTPS requires TLS 1.2+`_. If you need to re-enable it, use the ``ssl_relax_minimum_version`` flag,
which will configure ``kwargs["ssl_minimum_version"] = ssl.TLSVersion.MINIMUM_SUPPORTED``.

>>> connection = client.connect(..., ssl_relax_minimum_version=True)


Timeout
-------

Expand Down Expand Up @@ -268,6 +278,7 @@ Once you're connected, you can :ref:`query CrateDB <query>`.


.. _client-side random load balancing: https://en.wikipedia.org/wiki/Load_balancing_(computing)#Client-side_random_load_balancing
.. _Modern security by default - HTTPS requires TLS 1.2+: https://urllib3.readthedocs.io/en/latest/v2-migration-guide.html#https-requires-tls-1-2
.. _Python Database API Specification v2.0: https://www.python.org/dev/peps/pep-0249/
.. _round-robin DNS: https://en.wikipedia.org/wiki/Round-robin_DNS
.. _sample application: https://github.com/crate/crate-sample-apps/tree/main/python-flask
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def read(path):
'crate = crate.client.sqlalchemy:CrateDialect'
]
},
install_requires=['urllib3>=1.9,<2'],
install_requires=['urllib3<2.1'],
extras_require=dict(
sqlalchemy=['sqlalchemy>=1.0,<2.1',
'geojson>=2.5.0,<4',
Expand Down
2 changes: 2 additions & 0 deletions src/crate/client/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ def __init__(self,
error_trace=False,
cert_file=None,
key_file=None,
ssl_relax_minimum_version=False,
username=None,
password=None,
schema=None,
Expand Down Expand Up @@ -138,6 +139,7 @@ def __init__(self,
error_trace=error_trace,
cert_file=cert_file,
key_file=key_file,
ssl_relax_minimum_version=ssl_relax_minimum_version,
username=username,
password=password,
schema=schema,
Expand Down
24 changes: 23 additions & 1 deletion src/crate/client/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
from decimal import Decimal
from uuid import UUID

import urllib3
from urllib3 import connection_from_url
from urllib3.connection import HTTPConnection
from urllib3.exceptions import (
Expand All @@ -48,6 +49,8 @@
SSLError,
)
from urllib3.util.retry import Retry

from crate.client._pep440 import Version
from crate.client.exceptions import (
ConnectionError,
BlobLocationNotFoundException,
Expand Down Expand Up @@ -274,6 +277,19 @@ def _remove_certs_for_non_https(server, kwargs):
return kwargs


def _update_pool_kwargs_for_ssl_minimum_version(server, kwargs):
"""
On urllib3 v2, re-add support for TLS 1.0 and TLS 1.1.
https://urllib3.readthedocs.io/en/latest/v2-migration-guide.html#https-requires-tls-1-2
"""
if Version(urllib3.__version__) >= Version("2"):
from urllib3.util import parse_url
scheme, _, host, port, *_ = parse_url(server)
if scheme == "https":
kwargs["ssl_minimum_version"] = ssl.TLSVersion.MINIMUM_SUPPORTED


def _create_sql_payload(stmt, args, bulk_args):
if not isinstance(stmt, str):
raise ValueError('stmt is not a string')
Expand Down Expand Up @@ -304,7 +320,7 @@ def _get_socket_opts(keepalive=True,
# always use TCP keepalive
opts = [(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)]

# hasattr check because some of the options depend on system capabilities
# hasattr check because some options depend on system capabilities
# see https://docs.python.org/3/library/socket.html#socket.SOMAXCONN
if hasattr(socket, 'TCP_KEEPIDLE') and tcp_keepidle is not None:
opts.append((socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, tcp_keepidle))
Expand Down Expand Up @@ -340,6 +356,7 @@ def __init__(self,
error_trace=False,
cert_file=None,
key_file=None,
ssl_relax_minimum_version=False,
username=None,
password=None,
schema=None,
Expand Down Expand Up @@ -380,6 +397,7 @@ def __init__(self,
'socket_tcp_keepintvl': socket_tcp_keepintvl,
'socket_tcp_keepcnt': socket_tcp_keepcnt,
})
self.ssl_relax_minimum_version = ssl_relax_minimum_version
self.backoff_factor = backoff_factor
self.server_pool = {}
self._update_server_pool(servers, **pool_kw)
Expand All @@ -400,6 +418,10 @@ def close(self):

def _create_server(self, server, **pool_kw):
kwargs = _remove_certs_for_non_https(server, pool_kw)
# After updating to urllib3 v2, optionally retain support for TLS 1.0 and TLS 1.1,
# in order to support connectivity to older versions of CrateDB.
if self.ssl_relax_minimum_version:
_update_pool_kwargs_for_ssl_minimum_version(server, kwargs)
self.server_pool[server] = Server(server, **kwargs)

def _update_server_pool(self, servers, **pool_kw):
Expand Down

0 comments on commit 69e6156

Please sign in to comment.