Browse Source

Add SSL option for redis backends (#3831)

* Add redis_backend_use_ssl option

* Correct documentation for broker_use_ssl with redis

The previous documentation was only correct for pyamqp but not redis,
which doesn't allow `True` and requires a slightly different set of keys
when passing a dictionary.

* Document redis_backend_use_ssl

* Add a test for redis_backend_use_ssl

* Fixed typo.

* Add redis_backend_use_ssl to celery.app.defaults
Chris Kuehl 8 years ago
parent
commit
8c8354f77e

+ 1 - 0
celery/app/defaults.py

@@ -152,6 +152,7 @@ NAMESPACES = Namespace(
     redis=Namespace(
         __old__=old_ns('celery_redis'),
 
+        backend_use_ssl=Option(type='dict'),
         db=Option(type='int'),
         host=Option(type='string'),
         max_connections=Option(type='int'),

+ 9 - 0
celery/backends/redis.py

@@ -132,6 +132,15 @@ class RedisBackend(base.BaseKeyValueStoreBackend, async.AsyncBackendMixin):
             'socket_connect_timeout':
                 socket_connect_timeout and float(socket_connect_timeout),
         }
+
+        # "redis_backend_use_ssl" must be a dict with the keys:
+        # 'ssl_cert_reqs', 'ssl_ca_certs', 'ssl_certfile', 'ssl_keyfile'
+        # (the same as "broker_use_ssl")
+        ssl = _get('redis_backend_use_ssl')
+        if ssl:
+            self.connparams.update(ssl)
+            self.connparams['connection_class'] = redis.SSLConnection
+
         if url:
             self.connparams = self._params_from_url(url, self.connparams)
         self.url = url

+ 30 - 0
docs/userguide/configuration.rst

@@ -878,6 +878,16 @@ The fields of the URL are defined as follows:
     Database number to use. Default is 0.
     The db can include an optional leading slash.
 
+.. setting:: redis_backend_use_ssl
+
+``redis_backend_use_ssl``
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Default: Disabled.
+
+The Redis backend supports SSL. The valid values of this options are the same
+as :setting:`broker_use_ssl`.
+
 .. setting:: redis_max_connections
 
 ``redis_max_connections``
@@ -1793,6 +1803,11 @@ Default: Disabled.
 
 Toggles SSL usage on broker connection and SSL settings.
 
+The valid values for this option vary by transport.
+
+``pyamqp``
+__________
+
 If ``True`` the connection will use SSL with default SSL settings.
 If set to a dict, will configure SSL connection according to the specified
 policy. The format used is Python's :func:`ssl.wrap_socket` options.
@@ -1820,6 +1835,21 @@ certificate authority:
     `ssl module security
     considerations <https://docs.python.org/3/library/ssl.html#ssl-security>`_.
 
+``redis``
+_________
+
+
+The setting must be a dict the keys:
+
+*  ``ssl_cert_reqs`` (required): one of the ``SSLContext.verify_mode`` values:
+    * ``ssl.CERT_NONE``
+    * ``ssl.CERT_OPTIONAL``
+    * ``ssl.CERT_REQUIRED``
+*  ``ssl_ca_certs`` (optional): path to the CA certificate
+*  ``ssl_certfile`` (optional): path to the client certificate
+*  ``ssl_keyfile`` (optional): path to the client key
+
+
 .. setting:: broker_pool_limit
 
 ``broker_pool_limit``

+ 29 - 0
t/unit/backends/test_redis.py

@@ -1,5 +1,6 @@
 from __future__ import absolute_import, unicode_literals
 import pytest
+import ssl
 from datetime import timedelta
 from contextlib import contextmanager
 from pickle import loads, dumps
@@ -179,6 +180,34 @@ class test_RedisBackend:
         assert 'socket_connect_timeout' not in x.connparams
         assert x.connparams['db'] == 3
 
+    @skip.unless_module('redis')
+    def test_backend_ssl(self):
+        self.app.conf.redis_backend_use_ssl = {
+            'ssl_cert_reqs': ssl.CERT_REQUIRED,
+            'ssl_ca_certs': '/path/to/ca.crt',
+            'ssl_certfile': '/path/to/client.crt',
+            'ssl_keyfile': '/path/to/client.key',
+        }
+        self.app.conf.redis_socket_timeout = 30.0
+        self.app.conf.redis_socket_connect_timeout = 100.0
+        x = self.Backend(
+            'redis://:bosco@vandelay.com:123//1', app=self.app,
+        )
+        assert x.connparams
+        assert x.connparams['host'] == 'vandelay.com'
+        assert x.connparams['db'] == 1
+        assert x.connparams['port'] == 123
+        assert x.connparams['password'] == 'bosco'
+        assert x.connparams['socket_timeout'] == 30.0
+        assert x.connparams['socket_connect_timeout'] == 100.0
+        assert x.connparams['ssl_cert_reqs'] == ssl.CERT_REQUIRED
+        assert x.connparams['ssl_ca_certs'] == '/path/to/ca.crt'
+        assert x.connparams['ssl_certfile'] == '/path/to/client.crt'
+        assert x.connparams['ssl_keyfile'] == '/path/to/client.key'
+
+        from redis.connection import SSLConnection
+        assert x.connparams['connection_class'] is SSLConnection
+
     def test_compat_propertie(self):
         x = self.Backend(
             'redis://:bosco@vandelay.com:123//1', app=self.app,