Skip to content

Commit 7339a27

Browse files
committed
Add SSL context for versions of Python that support SSL Context.
Instead of passing parameters manually, you can construct an SSL context. This allows for greater flexibility in using self signed certs.
1 parent e04c905 commit 7339a27

File tree

5 files changed

+88
-25
lines changed

5 files changed

+88
-25
lines changed

.travis.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ before_install:
2424
- java -version
2525

2626
install:
27-
- curl -L -o /tmp/es-snap.zip https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.0.0-beta2.zip
27+
- curl -L -o /tmp/es-snap.zip https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.0.0-rc1.zip
2828
- unzip /tmp/es-snap.zip -d /tmp/
29-
- /tmp/elasticsearch-*/bin/elasticsearch -E script.inline=true -E path.repo=/tmp -E repositories.url.allowed_urls='http://*' -E node.attr.testattr=test -d
29+
- /tmp/elasticsearch-*/bin/elasticsearch -E path.repo=/tmp -E repositories.url.allowed_urls='http://*' -E node.attr.testattr=test -d
3030
- git clone https://github.com/elastic/elasticsearch.git ../elasticsearch
3131
- pip install .
3232

docs/connection.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,5 +45,15 @@ 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`.
56+
57+
4858
.. autoclass:: Urllib3HttpConnection
4959
:members:

elasticsearch/connection/http_urllib3.py

Lines changed: 50 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import time
2+
import ssl
23
import urllib3
34
from urllib3.exceptions import ReadTimeoutError, SSLError as UrllibSSLError
45
import warnings
@@ -15,6 +16,19 @@
1516
from ..exceptions import ConnectionError, ImproperlyConfigured, ConnectionTimeout, SSLError
1617
from ..compat import urlencode
1718

19+
20+
def create_ssl_context(**kwargs):
21+
"""
22+
A helper function around creating an SSL context
23+
24+
https://docs.python.org/3/library/ssl.html#context-creation
25+
26+
Accepts kwargs in the same manner as `create_default_context`.
27+
"""
28+
ctx = ssl.create_default_context(**kwargs)
29+
return ctx
30+
31+
1832
class Urllib3HttpConnection(Connection):
1933
"""
2034
Default connection class using the `urllib3` library and the http protocol.
@@ -47,7 +61,7 @@ class Urllib3HttpConnection(Connection):
4761
def __init__(self, host='localhost', port=9200, http_auth=None,
4862
use_ssl=False, verify_certs=True, ca_certs=None, client_cert=None,
4963
client_key=None, ssl_version=None, ssl_assert_hostname=None,
50-
ssl_assert_fingerprint=None, maxsize=10, headers=None, **kwargs):
64+
ssl_assert_fingerprint=None, maxsize=10, headers=None, ssl_context=None, **kwargs):
5165

5266
super(Urllib3HttpConnection, self).__init__(host=host, port=port, use_ssl=use_ssl, **kwargs)
5367
self.headers = urllib3.make_headers(keep_alive=True)
@@ -62,33 +76,49 @@ def __init__(self, host='localhost', port=9200, http_auth=None,
6276
self.headers[k.lower()] = headers[k]
6377

6478
self.headers.setdefault('content-type', 'application/json')
65-
ca_certs = CA_CERTS if ca_certs is None else ca_certs
6679
pool_class = urllib3.HTTPConnectionPool
6780
kw = {}
68-
if use_ssl:
81+
82+
# if providing an SSL context, raise error if any other SSL related flag is used
83+
if ssl_context and (verify_certs or ca_certs or ssl_version):
84+
raise ImproperlyConfigured("When using `ssl_context`, `use_ssl`, `verify_certs`, `ca_certs` and `ssl_version` are not permitted")
85+
86+
# if ssl_context provided use SSL by default
87+
if use_ssl or ssl_context:
88+
if not ca_certs and not ssl_context and verify_certs:
89+
# If no ca_certs and no sslcontext passed and asking to verify certs
90+
# raise error
91+
raise ImproperlyConfigured("Root certificates are missing for certificate "
92+
"validation. Either pass them in using the ca_certs parameter or "
93+
"install certifi to use it automatically.")
94+
if verify_certs or ca_certs or ssl_version:
95+
warnings.warn('Use of `verify_certs`, `ca_certs`, `ssl_version` have been deprecated in favor of using SSLContext`', DeprecationWarning)
6996
pool_class = urllib3.HTTPSConnectionPool
97+
98+
if not ssl_context:
99+
# if SSLContext hasn't been passed in, create one.
100+
cafile = CA_CERTS if ca_certs is None else ca_certs
101+
# need to skip if sslContext isn't avail
102+
try:
103+
ssl_context = create_ssl_context(cafile=cafile)
104+
except AttributeError:
105+
ssl_context = None
106+
107+
if not verify_certs and ssl_context is not None:
108+
ssl_context.check_hostname = False
109+
ssl_context.verify_mode = ssl.CERT_NONE
110+
warnings.warn(
111+
'Connecting to %s using SSL with verify_certs=False is insecure.' % host)
112+
70113
kw.update({
71114
'ssl_version': ssl_version,
72115
'assert_hostname': ssl_assert_hostname,
73116
'assert_fingerprint': ssl_assert_fingerprint,
117+
'ssl_context': ssl_context,
118+
'cert_file': client_cert,
119+
'ca_certs': ca_certs,
120+
'key_file': client_key,
74121
})
75-
76-
if verify_certs:
77-
if not ca_certs:
78-
raise ImproperlyConfigured("Root certificates are missing for certificate "
79-
"validation. Either pass them in using the ca_certs parameter or "
80-
"install certifi to use it automatically.")
81-
82-
kw.update({
83-
'cert_reqs': 'CERT_REQUIRED',
84-
'ca_certs': ca_certs,
85-
'cert_file': client_cert,
86-
'key_file': client_key,
87-
})
88-
else:
89-
warnings.warn(
90-
'Connecting to %s using SSL with verify_certs=False is insecure.' % host)
91-
92122
self.pool = pool_class(host, port=port, timeout=self.timeout, maxsize=maxsize, **kw)
93123

94124
def perform_request(self, method, url, params=None, body=None, timeout=None, ignore=()):

test_elasticsearch/test_connection.py

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import sys
12
import re
3+
import ssl
24
from mock import Mock, patch
35
import urllib3
46
import warnings
@@ -7,8 +9,9 @@
79
from elasticsearch.exceptions import TransportError, ConflictError, RequestError, NotFoundError
810
from elasticsearch.connection import RequestsHttpConnection, \
911
Urllib3HttpConnection
10-
11-
from .test_cases import TestCase
12+
from elasticsearch.exceptions import ImproperlyConfigured
13+
from elasticsearch.connection.http_urllib3 import create_ssl_context
14+
from .test_cases import TestCase, SkipTest
1215

1316

1417
class TestUrllib3Connection(TestCase):
@@ -42,6 +45,12 @@ def test_http_auth_list(self):
4245
'connection': 'keep-alive'}, con.headers)
4346

4447
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")
4554
with warnings.catch_warnings(record=True) as w:
4655
con = Urllib3HttpConnection(use_ssl=True, verify_certs=False)
4756
self.assertEquals(1, len(w))
@@ -53,6 +62,16 @@ def test_doesnt_use_https_if_not_specified(self):
5362
con = Urllib3HttpConnection()
5463
self.assertIsInstance(con.pool, urllib3.HTTPConnectionPool)
5564

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, use_ssl=True)
71+
self.assertRaises(ImproperlyConfigured, Urllib3HttpConnection, ssl_context=ctx, verify_certs=True)
72+
self.assertRaises(ImproperlyConfigured, Urllib3HttpConnection, ssl_context=ctx, ca_certs="/some/path/to/cert.crt")
73+
self.assertRaises(ImproperlyConfigured, Urllib3HttpConnection, ssl_context=ctx, ssl_version=ssl.PROTOCOL_SSLv23)
74+
5675
class TestRequestsConnection(TestCase):
5776
def _get_mock_connection(self, connection_params={}, status_code=200, response_body='{}'):
5877
con = RequestsHttpConnection(**connection_params)

test_elasticsearch/test_server/test_common.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,11 @@
4040
'*': set(('TestBulk10Basic', 'TestCatSnapshots10Basic',
4141
'TestClusterPutSettings10Basic', 'TestIndex10WithId',
4242
'TestClusterPutScript10Basic', 'TestIndicesPutMapping10Basic',
43-
'TestIndicesPutTemplate10Basic', 'TestMsearch10Basic'))
43+
'TestIndicesPutTemplate10Basic', 'TestMsearch10Basic',
44+
# skip these two till https://github.com/elastic/elasticsearch/pull/26905 has been merged
45+
'TestSearchAggregation190PercentilesHdrMetric',
46+
'TestSearchAggregation180PercentilesTdigestMetric',
47+
))
4448

4549
}
4650

0 commit comments

Comments
 (0)