From 8314f7b25c1c77f07a7855a690d595944095cf04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Honza=20Kr=C3=A1l?= Date: Mon, 17 Oct 2016 14:16:56 +0200 Subject: [PATCH] Set verify_certs default to True If certifi is installed use it as default for ca_certs value Closes #403 --- Changelog.rst | 2 ++ docs/index.rst | 14 ++++++-------- elasticsearch/connection/http_requests.py | 2 +- elasticsearch/connection/http_urllib3.py | 18 +++++++++++++++--- test_elasticsearch/test_connection.py | 8 ++++---- 5 files changed, 28 insertions(+), 16 deletions(-) diff --git a/Changelog.rst b/Changelog.rst index a343ec1d..4b5cc3ca 100644 --- a/Changelog.rst +++ b/Changelog.rst @@ -8,6 +8,8 @@ Changelog Version compatible with elasticsearch 5.0 + * when using SSL certificate validation is now on by default. Install + ``certifi`` or supply root certificate bundle. * added ``headers`` arg to connections to support custom http headers * passing in a keyword parameter with ``None`` as value will cause that param to be ignored diff --git a/docs/index.rst b/docs/index.rst index d6722396..c1ab4287 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -189,9 +189,7 @@ elasticsearch cluster, including certificate verification and http auth:: ['localhost', 'otherhost'], http_auth=('user', 'secret'), port=443, - use_ssl=True, - verify_certs=True, - ca_certs=certifi.where(), + use_ssl=True ) # SSL client authentication using client_cert and client_key @@ -201,7 +199,6 @@ elasticsearch cluster, including certificate verification and http auth:: http_auth=('user', 'secret'), port=443, use_ssl=True, - verify_certs=True, ca_certs='/path/to/cacert.pem', client_cert='/path/to/client_cert.pem', client_key='/path/to/client_key.pem', @@ -209,10 +206,11 @@ elasticsearch cluster, including certificate verification and http auth:: .. warning:: - By default SSL certificates won't be verified, pass in - ``verify_certs=True`` to make sure your certificates will get verified. The - client doesn't ship with any CA certificates; easiest way to obtain the - common set is by using the `certifi`_ package (as shown above). + ``elasticsearch-py`` doesn't ship with default set of root certificates. To + have working SSL certificate validation you need to either specify your own + as ``ca_certs`` or install `certifi`_ which will be picked up + automatically. + See class :class:`~elasticsearch.Urllib3HttpConnection` for detailed description of the options. diff --git a/elasticsearch/connection/http_requests.py b/elasticsearch/connection/http_requests.py index 2ca7002a..895888ab 100644 --- a/elasticsearch/connection/http_requests.py +++ b/elasticsearch/connection/http_requests.py @@ -27,7 +27,7 @@ class RequestsHttpConnection(Connection): :arg headers: any custom http headers to be add to requests """ def __init__(self, host='localhost', port=9200, http_auth=None, - use_ssl=False, verify_certs=False, ca_certs=None, client_cert=None, + use_ssl=False, verify_certs=True, ca_certs=None, client_cert=None, client_key=None, headers=None, **kwargs): if not REQUESTS_AVAILABLE: raise ImproperlyConfigured("Please install requests to use RequestsHttpConnection.") diff --git a/elasticsearch/connection/http_urllib3.py b/elasticsearch/connection/http_urllib3.py index 31857383..868bab02 100644 --- a/elasticsearch/connection/http_urllib3.py +++ b/elasticsearch/connection/http_urllib3.py @@ -3,6 +3,14 @@ import urllib3 from urllib3.exceptions import ReadTimeoutError, SSLError as UrllibSSLError import warnings +CA_CERTS = None + +try: + import certifi + CA_CERTS = certifi.where() +except ImportError: + pass + from .base import Connection from ..exceptions import ConnectionError, ImproperlyConfigured, ConnectionTimeout, SSLError from ..compat import urlencode @@ -36,7 +44,7 @@ class Urllib3HttpConnection(Connection): :arg headers: any custom http headers to be add to requests """ def __init__(self, host='localhost', port=9200, http_auth=None, - use_ssl=False, verify_certs=False, ca_certs=None, client_cert=None, + use_ssl=False, verify_certs=True, ca_certs=None, client_cert=None, client_key=None, ssl_version=None, ssl_assert_hostname=None, ssl_assert_fingerprint=None, maxsize=10, headers=None, **kwargs): @@ -48,6 +56,7 @@ class Urllib3HttpConnection(Connection): http_auth = ':'.join(http_auth) self.headers.update(urllib3.make_headers(basic_auth=http_auth)) + ca_certs = CA_CERTS if ca_certs is None else ca_certs pool_class = urllib3.HTTPConnectionPool kw = {} if use_ssl: @@ -59,14 +68,17 @@ class Urllib3HttpConnection(Connection): }) if verify_certs: + if not ca_certs: + raise ImproperlyConfigured("Root certificates are missing for certificate " + "validation. Either pass them in using the ca_certs parameter or " + "install certifi to use it automatically.") + kw.update({ 'cert_reqs': 'CERT_REQUIRED', 'ca_certs': ca_certs, 'cert_file': client_cert, 'key_file': client_key, }) - elif ca_certs: - raise ImproperlyConfigured("You cannot pass CA certificates when verify SSL is off.") else: warnings.warn( 'Connecting to %s using SSL with verify_certs=False is insecure.' % host) diff --git a/test_elasticsearch/test_connection.py b/test_elasticsearch/test_connection.py index db3cd53e..c5cc5e7f 100644 --- a/test_elasticsearch/test_connection.py +++ b/test_elasticsearch/test_connection.py @@ -35,9 +35,9 @@ class TestUrllib3Connection(TestCase): self.assertEquals({'authorization': 'Basic dXNlcm5hbWU6c2VjcmV0', 'connection': 'keep-alive'}, con.headers) - def test_uses_https_if_specified(self): + def test_uses_https_if_verify_certs_is_off(self): with warnings.catch_warnings(record=True) as w: - con = Urllib3HttpConnection(use_ssl=True) + con = Urllib3HttpConnection(use_ssl=True, verify_certs=False) self.assertEquals(1, len(w)) self.assertEquals('Connecting to localhost using SSL with verify_certs=False is insecure.', str(w[0].message)) @@ -86,9 +86,9 @@ class TestRequestsConnection(TestCase): con = RequestsHttpConnection(timeout=42) self.assertEquals(42, con.timeout) - def test_use_https_if_specified(self): + def test_uses_https_if_verify_certs_is_off(self): with warnings.catch_warnings(record=True) as w: - con = self._get_mock_connection({'use_ssl': True, 'url_prefix': 'url'}) + con = self._get_mock_connection({'use_ssl': True, 'url_prefix': 'url', 'verify_certs': False}) self.assertEquals(1, len(w)) self.assertEquals('Connecting to https://localhost:9200/url using SSL with verify_certs=False is insecure.', str(w[0].message))