Skip to content

Commit 058d38f

Browse files
authored
Use use ssl_context or don't but don't mix (elastic#714)
* Use original SSL process and add SSLContext Not going to deprecate and replace with SSLContext. But instead give option for using SSLContext next to the original way of handling SSL.
1 parent 4438ea7 commit 058d38f

File tree

5 files changed

+71
-63
lines changed

5 files changed

+71
-63
lines changed

README

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,9 @@ Elastic Cloud (and SSL) use-case::
9797
Using SSL Context with a self-signed cert use-case::
9898

9999
>>> from elasticsearch import Elasticsearch
100-
>>> from elasticsearch.connection import create_ssl_context
100+
>>> from ssl import create_default_context
101101

102-
>>> context = create_ssl_context(cafile="path/to/cafile.pem")
102+
>>> context = create_default_context(cafile="path/to/cafile.pem")
103103
>>> es = Elasticsearch("https://elasticsearch.url:port", ssl_context=context, http_auth=('elastic','yourpassword'))
104104
>>> es.info()
105105

docs/connection.rst

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -45,23 +45,20 @@ Connection Selector
4545
Urllib3HttpConnection (default connection_class)
4646
------------------------------------------------
4747

48-
Deprecation Notice: `use_ssl`, `verify_certs`, `ca_certs` and `ssl_version` are being
49-
deprecated in favor of using a `SSLContext` (https://docs.python.org/3/library/ssl.html#ssl.SSLContext) object.
50-
51-
You can continue to use the deprecated parameters and an `SSLContext` will be created for you.
52-
53-
If you want to create your own `SSLContext` object you can create one natively using the
54-
python SSL library with the `create_default_context` (https://docs.python.org/3/library/ssl.html#ssl.create_default_context) method
55-
or you can use the wrapper function :function:`~elasticsearch.connection.http_urllib3.create_ssl_context`.
48+
If you have complex SSL logic for connecting to Elasticsearch using an `SSLContext` object
49+
might be more helpful. You can create one natively using the python SSL library with the
50+
`create_default_context` (https://docs.python.org/3/library/ssl.html#ssl.create_default_context) method.
5651

5752
To create an `SSLContext` object you only need to use one of cafile, capath or cadata::
5853

59-
>>> from elasticsearch.connection import create_ssl_context
60-
>>> context = create_ssl_context(cafile=None, capath=None, cadata=None)
54+
>>> from ssl import create_default_context
55+
>>> context = create_default_context(cafile=None, capath=None, cadata=None)
6156

6257
* `cafile` is the path to your CA File
6358
* `capath` is the directory of a collection of CA's
6459
* `cadata` is either an ASCII string of one or more PEM-encoded certificates or a bytes-like object of DER-encoded certificates.
6560

61+
Please note that the use of SSLContext is only available for Urllib3.
62+
6663
.. autoclass:: Urllib3HttpConnection
6764
:members:

docs/index.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -200,9 +200,9 @@ elasticsearch cluster, including certificate verification and http auth::
200200

201201
# SSL client authentication using client_cert and client_key
202202

203-
from elasticsearch.connection import create_ssl_context
203+
from ssl import create_default_context
204204

205-
context = create_ssl_context(cafile="path/to/cert.pem")
205+
context = create_default_context(cafile="path/to/cert.pem")
206206
es = Elasticsearch(
207207
['localhost', 'otherhost'],
208208
http_auth=('user', 'secret'),

elasticsearch/connection/http_urllib3.py

Lines changed: 42 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@
44
from urllib3.exceptions import ReadTimeoutError, SSLError as UrllibSSLError
55
import warnings
66

7+
# sentinal value for `verify_certs`.
8+
# This is used to detect if a user is passing in a value for `verify_certs`
9+
# so we can raise a warning if using SSL kwargs AND SSLContext.
10+
VERIFY_CERTS_DEFAULT = None
11+
712
CA_CERTS = None
813

914
try:
@@ -41,8 +46,8 @@ class Urllib3HttpConnection(Connection):
4146
string or a tuple
4247
:arg use_ssl: use ssl for the connection if `True`
4348
:arg verify_certs: whether to verify SSL certificates
44-
:arg ca_certs: optional path to CA bundle. See
45-
https://urllib3.readthedocs.io/en/latest/security.html#using-certifi-with-urllib3
49+
:arg ca_certs: optional path to CA bundle.
50+
See https://urllib3.readthedocs.io/en/latest/security.html#using-certifi-with-urllib3
4651
for instructions how to get default set
4752
:arg client_cert: path to the file containing the private key and the
4853
certificate, or cert only if using client_key
@@ -59,7 +64,7 @@ class Urllib3HttpConnection(Connection):
5964
:arg headers: any custom http headers to be add to requests
6065
"""
6166
def __init__(self, host='localhost', port=9200, http_auth=None,
62-
use_ssl=False, verify_certs=True, ca_certs=None, client_cert=None,
67+
use_ssl=False, verify_certs=VERIFY_CERTS_DEFAULT, ca_certs=None, client_cert=None,
6368
client_key=None, ssl_version=None, ssl_assert_hostname=None,
6469
ssl_assert_fingerprint=None, maxsize=10, headers=None, ssl_context=None, **kwargs):
6570

@@ -80,48 +85,51 @@ def __init__(self, host='localhost', port=9200, http_auth=None,
8085
kw = {}
8186

8287
# if providing an SSL context, raise error if any other SSL related flag is used
83-
if ssl_context and (ca_certs or ssl_version):
84-
raise ImproperlyConfigured("When using `ssl_context`, `use_ssl`, `verify_certs`, `ca_certs` and `ssl_version` are not permitted")
88+
if ssl_context and ( (verify_certs is not VERIFY_CERTS_DEFAULT) or ca_certs
89+
or client_cert or client_key or ssl_version):
90+
warnings.warn("When using `ssl_context`, all other SSL related kwargs are ignored")
8591

8692
# if ssl_context provided use SSL by default
87-
if self.use_ssl or ssl_context:
88-
ca_certs = CA_CERTS if ca_certs is None else ca_certs
89-
90-
if not ca_certs and not ssl_context and verify_certs:
91-
# If no ca_certs and no sslcontext passed and asking to verify certs
92-
# raise error
93-
raise ImproperlyConfigured("Root certificates are missing for certificate "
94-
"validation. Either pass them in using the ca_certs parameter or "
95-
"install certifi to use it automatically.")
96-
if verify_certs or ca_certs or ssl_version:
97-
warnings.warn('Use of `verify_certs`, `ca_certs`, `ssl_version` have been deprecated in favor of using SSLContext`', DeprecationWarning)
93+
if ssl_context and self.use_ssl:
9894
pool_class = urllib3.HTTPSConnectionPool
95+
kw.update({
96+
'assert_fingerprint': ssl_assert_fingerprint,
97+
'ssl_context': ssl_context,
98+
})
99+
self.pool = pool_class(host, port=port, timeout=self.timeout, maxsize=maxsize, **kw)
99100

100-
if not ssl_context:
101-
# if SSLContext hasn't been passed in, create one.
102-
# need to skip if sslContext isn't avail
103-
try:
104-
ssl_context = create_ssl_context(cafile=ca_certs)
105-
except AttributeError:
106-
ssl_context = None
107-
108-
if not verify_certs and ssl_context is not None:
109-
ssl_context.check_hostname = False
110-
ssl_context.verify_mode = ssl.CERT_NONE
111-
warnings.warn(
112-
'Connecting to %s using SSL with verify_certs=False is insecure.' % host)
113-
101+
elif self.use_ssl:
102+
pool_class = urllib3.HTTPSConnectionPool
114103
kw.update({
115104
'ssl_version': ssl_version,
116105
'assert_hostname': ssl_assert_hostname,
117106
'assert_fingerprint': ssl_assert_fingerprint,
118-
'ssl_context': ssl_context,
119-
'cert_file': client_cert,
120-
'ca_certs': ca_certs,
121-
'key_file': client_key,
122107
})
108+
109+
# If `verify_certs` is sentinal value, default `verify_certs` to `True`
110+
if verify_certs is VERIFY_CERTS_DEFAULT:
111+
verify_certs = True
112+
113+
ca_certs = CA_CERTS if ca_certs is None else ca_certs
114+
if verify_certs:
115+
if not ca_certs:
116+
raise ImproperlyConfigured("Root certificates are missing for certificate "
117+
"validation. Either pass them in using the ca_certs parameter or "
118+
"install certifi to use it automatically.")
119+
120+
kw.update({
121+
'cert_reqs': 'CERT_REQUIRED',
122+
'ca_certs': ca_certs,
123+
'cert_file': client_cert,
124+
'key_file': client_key,
125+
})
126+
else:
127+
warnings.warn(
128+
'Connecting to %s using SSL with verify_certs=False is insecure.' % host)
129+
123130
self.pool = pool_class(host, port=port, timeout=self.timeout, maxsize=maxsize, **kw)
124131

132+
125133
def perform_request(self, method, url, params=None, body=None, timeout=None, ignore=(), headers=None):
126134
url = self.url_prefix + url
127135
if params:

test_elasticsearch/test_connection.py

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,28 @@
1010
from elasticsearch.connection import RequestsHttpConnection, \
1111
Urllib3HttpConnection
1212
from elasticsearch.exceptions import ImproperlyConfigured
13-
from elasticsearch.connection.http_urllib3 import create_ssl_context
1413
from .test_cases import TestCase, SkipTest
1514

1615

1716
class TestUrllib3Connection(TestCase):
17+
def test_ssl_context(self):
18+
try:
19+
context = ssl.create_default_context()
20+
except AttributeError:
21+
# if create_default_context raises an AttributeError Exception
22+
# it means SSLContext is not available for that version of python
23+
# and we should skip this test.
24+
raise SkipTest(
25+
"Test test_ssl_context is skipped cause SSLContext is not available for this version of ptyhon")
26+
27+
con = Urllib3HttpConnection(use_ssl=True, ssl_context=context)
28+
self.assertEqual(len(con.pool.conn_kw.keys()), 1)
29+
self.assertIsInstance(
30+
con.pool.conn_kw['ssl_context'],
31+
ssl.SSLContext
32+
)
33+
self.assertTrue(con.use_ssl)
34+
1835
def test_timeout_set(self):
1936
con = Urllib3HttpConnection(timeout=42)
2037
self.assertEquals(42, con.timeout)
@@ -45,12 +62,6 @@ def test_http_auth_list(self):
4562
'connection': 'keep-alive'}, con.headers)
4663

4764
def test_uses_https_if_verify_certs_is_off(self):
48-
if (
49-
sys.version_info >= (3,0) and sys.version_info <= (3,4)
50-
) or (
51-
sys.version_info >= (2,6) and sys.version_info <= (2,7)
52-
):
53-
raise SkipTest("SSL Context not supported in this version of python")
5465
with warnings.catch_warnings(record=True) as w:
5566
con = Urllib3HttpConnection(use_ssl=True, verify_certs=False)
5667
self.assertEquals(1, len(w))
@@ -62,14 +73,6 @@ def test_doesnt_use_https_if_not_specified(self):
6273
con = Urllib3HttpConnection()
6374
self.assertIsInstance(con.pool, urllib3.HTTPConnectionPool)
6475

65-
def test_ssl_context_and_depreicated_values(self):
66-
try:
67-
ctx = create_ssl_context()
68-
except AttributeError:
69-
raise SkipTest("SSL Context not supported in this version of python")
70-
self.assertRaises(ImproperlyConfigured, Urllib3HttpConnection, ssl_context=ctx, ca_certs="/some/path/to/cert.crt")
71-
self.assertRaises(ImproperlyConfigured, Urllib3HttpConnection, ssl_context=ctx, ssl_version=ssl.PROTOCOL_SSLv23)
72-
7376
class TestRequestsConnection(TestCase):
7477
def _get_mock_connection(self, connection_params={}, status_code=200, response_body='{}'):
7578
con = RequestsHttpConnection(**connection_params)

0 commit comments

Comments
 (0)