Proper SSL implementation, including optional certificate verification

This commit is contained in:
Honza Král
2014-11-11 03:20:16 +01:00
parent d9296e85b9
commit fedcfafe7e
4 changed files with 53 additions and 10 deletions
+3 -3
View File
@@ -36,8 +36,8 @@ Connection Selector
:members:
Connection
----------
Urllib3HttpConnection (default connection_class)
------------------------------------------------
.. autoclass:: Connection
.. autoclass:: Urllib3HttpConnection
:members:
+14 -3
View File
@@ -6,7 +6,7 @@ except ImportError:
REQUESTS_AVAILABLE = False
from .base import Connection
from ..exceptions import ConnectionError, ImproperlyConfigured, ConnectionTimeout
from ..exceptions import ConnectionError, ImproperlyConfigured, ConnectionTimeout, SSLError
from ..compat import urlencode
class RequestsHttpConnection(Connection):
@@ -16,8 +16,12 @@ class RequestsHttpConnection(Connection):
:arg http_auth: optional http auth information as either ':' separated
string or a tuple
:arg use_ssl: use ssl for the connection if `True`
:arg verify_certs: whether to verify SSL certificates
:arg ca_certs: optional path to CA bundle. By default standard requests'
bundle will be used.
"""
def __init__(self, host='localhost', port=9200, http_auth=None, use_ssl=False, **kwargs):
def __init__(self, host='localhost', port=9200, http_auth=None,
use_ssl=False, verify_certs=False, ca_certs=None, **kwargs):
if not REQUESTS_AVAILABLE:
raise ImproperlyConfigured("Please install requests to use RequestsHttpConnection.")
@@ -32,7 +36,11 @@ class RequestsHttpConnection(Connection):
's' if use_ssl else '',
host, port, self.url_prefix
)
self.session.verify = verify_certs
if ca_certs:
if not verify_certs:
raise ImproperlyConfigured("You cannot pass CA certificates when verify SSL is off.")
self.session.verify = ca_certs
def perform_request(self, method, url, params=None, body=None, timeout=None, ignore=()):
url = self.base_url + url
@@ -44,6 +52,9 @@ class RequestsHttpConnection(Connection):
response = self.session.request(method, url, data=body, timeout=timeout or self.timeout)
duration = time.time() - start
raw_data = response.text
except requests.exceptions.SSLError as e:
self.log_request_fail(method, url, body, time.time() - start, exception=e)
raise SSLError('N/A', str(e), e)
except requests.Timeout as e:
self.log_request_fail(method, url, body, time.time() - start, exception=e)
raise ConnectionTimeout('TIMEOUT', str(e), e)
+21 -4
View File
@@ -1,9 +1,9 @@
import time
import urllib3
from urllib3.exceptions import ReadTimeoutError
from urllib3.exceptions import ReadTimeoutError, SSLError as UrllibSSLError
from .base import Connection
from ..exceptions import ConnectionError, ConnectionTimeout
from ..exceptions import ConnectionError, ImproperlyConfigured, ConnectionTimeout, SSLError
from ..compat import urlencode
class Urllib3HttpConnection(Connection):
@@ -13,10 +13,17 @@ class Urllib3HttpConnection(Connection):
:arg http_auth: optional http auth information as either ':' separated
string or a tuple
:arg use_ssl: use ssl for the connection if `True`
:arg verify_certs: whether to verify SSL certificates
:arg ca_certs: optional path to CA bundle. See
http://urllib3.readthedocs.org/en/latest/security.html#using-certifi-with-urllib3
for instructions how to get default set
:arg maxsize: the maximum number of connections which will be kept open to
this host.
"""
def __init__(self, host='localhost', port=9200, http_auth=None, use_ssl=False, maxsize=10, **kwargs):
def __init__(self, host='localhost', port=9200, http_auth=None,
use_ssl=False, verify_certs=False, ca_certs=None, maxsize=10,
**kwargs):
super(Urllib3HttpConnection, self).__init__(host=host, port=port, **kwargs)
self.headers = {}
if http_auth is not None:
@@ -25,10 +32,17 @@ class Urllib3HttpConnection(Connection):
self.headers = urllib3.make_headers(basic_auth=http_auth)
pool_class = urllib3.HTTPConnectionPool
kw = {}
if use_ssl:
pool_class = urllib3.HTTPSConnectionPool
self.pool = pool_class(host, port=port, timeout=self.timeout, maxsize=maxsize)
if verify_certs:
kw['cert_reqs'] = 'CERT_REQUIRED'
kw['ca_certs'] = ca_certs
elif ca_certs:
raise ImproperlyConfigured("You cannot pass CA certificates when verify SSL is off.")
self.pool = pool_class(host, port=port, timeout=self.timeout, maxsize=maxsize, **kw)
def perform_request(self, method, url, params=None, body=None, timeout=None, ignore=()):
url = self.url_prefix + url
@@ -50,6 +64,9 @@ class Urllib3HttpConnection(Connection):
response = self.pool.urlopen(method, url, body, retries=False, headers=self.headers, **kw)
duration = time.time() - start
raw_data = response.data.decode('utf-8')
except UrllibSSLError as e:
self.log_request_fail(method, full_url, body, time.time() - start, exception=e)
raise SSLError('N/A', str(e), e)
except ReadTimeoutError as e:
self.log_request_fail(method, full_url, body, time.time() - start, exception=e)
raise ConnectionTimeout('TIMEOUT', str(e), e)
+15
View File
@@ -8,6 +8,7 @@ class ImproperlyConfigured(Exception):
Exception raised when the config passed to the client is inconsistent or invalid.
"""
class ElasticsearchException(Exception):
"""
Base class for all exceptions raised by this package's operations (doesn't
@@ -61,6 +62,10 @@ class ConnectionError(TransportError):
self.error, self.info.__class__.__name__, self.info)
class SSLError(ConnectionError):
""" Error raised when encountering SSL errors. """
class ConnectionTimeout(ConnectionError):
""" A network timeout. """
def __str__(self):
@@ -79,9 +84,19 @@ class ConflictError(TransportError):
class RequestError(TransportError):
""" Exception representing a 400 status code. """
class AuthenticationException(TransportError):
""" Exception representing a 401 status code. """
class AuthorizationException(TransportError):
""" Exception representing a 403 status code. """
# more generic mappings from status_code to python exceptions
HTTP_EXCEPTIONS = {
400: RequestError,
401: AuthenticationException,
403: AuthorizationException,
404: NotFoundError,
409: ConflictError,
}