From fe6e9a7f5c832fd02847b99f3e2cb84292eb660b Mon Sep 17 00:00:00 2001 From: Rich Megginson Date: Tue, 26 Jan 2016 12:41:55 -0700 Subject: [PATCH] Urllib3HttpConnection should have separate client_cert and client_key arguments #344 https://github.com/elastic/elasticsearch-py/issues/344 Add a client_key parameter to complement the client_cert parameter. If client_key is used, it is assumed that client_cert will contain only the certificate, not the combined cert and key in a single file. Closes #344 --- docs/index.rst | 12 ++++++++++++ elasticsearch/client/__init__.py | 18 ++++++++++++++++++ elasticsearch/connection/http_requests.py | 12 +++++++++--- elasticsearch/connection/http_urllib3.py | 9 ++++++--- 4 files changed, 45 insertions(+), 6 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index e26428a0..9f0b75eb 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -194,6 +194,18 @@ elasticsearch cluster, including certificate verification and http auth:: ca_certs=certifi.where(), ) + # SSL client authentication using client_cert and client_key + + es = Elasticsearch( + ['localhost', 'otherhost'], + 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', + ) .. warning:: diff --git a/elasticsearch/client/__init__.py b/elasticsearch/client/__init__.py index 8349ee37..b7275c93 100644 --- a/elasticsearch/client/__init__.py +++ b/elasticsearch/client/__init__.py @@ -120,6 +120,24 @@ class Elasticsearch(object): ca_certs='/path/to/CA_certs' ) + SSL client authentication is supported + (see :class:`~elasticsearch.Urllib3HttpConnection` for + detailed description of the options):: + + es = Elasticsearch( + ['localhost:443', 'other_host:443'], + # turn on SSL + use_ssl=True, + # make sure we verify SSL certificates (off by default) + verify_certs=True, + # provide a path to CA certs on disk + ca_certs='/path/to/CA_certs', + # PEM formatted SSL client certificate + client_cert='/path/to/clientcert.pem', + # PEM formatted SSL client key + client_key='/path/to/clientkey.pem' + ) + Alternatively you can use RFC-1738 formatted URLs, as long as they are not in conflict with other options:: diff --git a/elasticsearch/connection/http_requests.py b/elasticsearch/connection/http_requests.py index 4271403f..8a4dda5b 100644 --- a/elasticsearch/connection/http_requests.py +++ b/elasticsearch/connection/http_requests.py @@ -21,11 +21,13 @@ class RequestsHttpConnection(Connection): :arg ca_certs: optional path to CA bundle. By default standard requests' bundle will be used. :arg client_cert: path to the file containing the private key and the - certificate + certificate, or cert only if using client_key + :arg client_key: path to the file containing the private key if using + separate cert and key files (client_cert will contain only the cert) """ def __init__(self, host='localhost', port=9200, http_auth=None, use_ssl=False, verify_certs=False, ca_certs=None, client_cert=None, - **kwargs): + client_key=None, **kwargs): if not REQUESTS_AVAILABLE: raise ImproperlyConfigured("Please install requests to use RequestsHttpConnection.") @@ -42,7 +44,11 @@ class RequestsHttpConnection(Connection): host, port, self.url_prefix ) self.session.verify = verify_certs - self.session.cert = client_cert + if not client_key: + self.session.cert = client_cert + else: + # cert is a tuple of (certfile, keyfile) + self.session.cert = (client_cert, client_key) if ca_certs: if not verify_certs: raise ImproperlyConfigured("You cannot pass CA certificates when verify SSL is off.") diff --git a/elasticsearch/connection/http_urllib3.py b/elasticsearch/connection/http_urllib3.py index 1558b2ac..d0f213f9 100644 --- a/elasticsearch/connection/http_urllib3.py +++ b/elasticsearch/connection/http_urllib3.py @@ -23,7 +23,9 @@ class Urllib3HttpConnection(Connection): http://urllib3.readthedocs.org/en/latest/security.html#using-certifi-with-urllib3 for instructions how to get default set :arg client_cert: path to the file containing the private key and the - certificate + certificate, or cert only if using client_key + :arg client_key: path to the file containing the private key if using + separate cert and key files (client_cert will contain only the cert) :arg ssl_version: version of the SSL protocol to use. Choices are: SSLv23 (default) SSLv2 SSLv3 TLSv1 (see ``PROTOCOL_*`` constants in the ``ssl`` module for exact options for your environment). @@ -34,8 +36,8 @@ class Urllib3HttpConnection(Connection): """ def __init__(self, host='localhost', port=9200, http_auth=None, use_ssl=False, verify_certs=False, ca_certs=None, client_cert=None, - ssl_version=None, ssl_assert_hostname=None, ssl_assert_fingerprint=None, - maxsize=10, **kwargs): + client_key=None, ssl_version=None, ssl_assert_hostname=None, + ssl_assert_fingerprint=None, maxsize=10, **kwargs): super(Urllib3HttpConnection, self).__init__(host=host, port=port, **kwargs) self.headers = urllib3.make_headers(keep_alive=True) @@ -59,6 +61,7 @@ class Urllib3HttpConnection(Connection): '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.")