diff --git a/releasenotes/notes/memcached-tls-support_7dc1a652ff087b83.yaml b/releasenotes/notes/memcached-tls-support_7dc1a652ff087b83.yaml new file mode 100644 index 00000000..c8c1d674 --- /dev/null +++ b/releasenotes/notes/memcached-tls-support_7dc1a652ff087b83.yaml @@ -0,0 +1,15 @@ +--- +features: + - | + Added TLS support for Memcached driver. + + The following TLS-related options now can be specifed in the Memcached + connection URL as query parameters + + - ``use_ssl``: enable SSL/TLS connection or not, default is "False" + - ``ca_cert``: CA file to use for authentication + - ``ssl_cert``: path to client public key certificate file + - ``ssl_key``: path to client private key file + - ``ssl_key_password``: password for decrypting the private key + - ``ssl_check_hostname``: verify server hostname against its certificate + - ``ssl_ciphers``: available ciphers, a string in the OpenSSL cipher list format diff --git a/tooz/drivers/memcached.py b/tooz/drivers/memcached.py index 8548c62c..dd3484f1 100644 --- a/tooz/drivers/memcached.py +++ b/tooz/drivers/memcached.py @@ -18,7 +18,9 @@ import errno import functools import logging import socket +import ssl +from oslo_utils import strutils from pymemcache import client as pymemcache_client import tooz @@ -205,6 +207,13 @@ class MemcachedDriver(coordination.CoordinationDriverCachedRunWatchers, lock_timeout 30 leader_timeout 30 max_pool_size None + use_ssl False + ca_cert None + ssl_key None + ssl_key_password None + ssl_cert None + ssl_ciphers None + ssl_check_hostname False ================== ======= General recommendations/usage considerations: @@ -263,6 +272,29 @@ class MemcachedDriver(coordination.CoordinationDriverCachedRunWatchers, else: self.max_pool_size = None self._acquired_locks = [] + self.ssl_context = None + use_ssl = self._options.get('use_ssl', 'False') + use_ssl = strutils.bool_from_string(use_ssl, + strict=False, + default=False) + if use_ssl: + ca_cert = self._options.get('ca_cert') + ssl_key = self._options.get('ssl_key') + ssl_cert = self._options.get('ssl_cert') + ssl_key_password = self._options.get('ssl_key_password') + ciphers = self._options.get('ssl_ciphers') + check_hostname = self._options.get('ssl_check_hostname', 'False') + check_hostname = strutils.bool_from_string(check_hostname, + strict=False, + default=False) + self.ssl_context = ssl.create_default_context( + ssl.Purpose.SERVER_AUTH, cafile=ca_cert) + if ciphers is not None: + self.ssl_context.set_ciphers(ciphers) + self.ssl_context.check_hostname = check_hostname + self.ssl_context.load_cert_chain(certfile=ssl_cert, + keyfile=ssl_key, + password=ssl_key_password) @staticmethod def _msgpack_serializer(key, value): @@ -288,7 +320,8 @@ class MemcachedDriver(coordination.CoordinationDriverCachedRunWatchers, deserializer=self._msgpack_deserializer, timeout=self.timeout, connect_timeout=self.timeout, - max_pool_size=self.max_pool_size) + max_pool_size=self.max_pool_size, + tls_context=self.ssl_context) # Run heartbeat here because pymemcache use a lazy connection # method and only connect once you do an operation. self.heartbeat()